mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

No driver should spam the kernel log when merely being loaded.
Fixes: 17fcb3dc12
("hinic3: module initialization and tx/rx logic")
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/5310dac0b3ab4bd16dd8fb761566f12e73b38cab.1748357352.git.geert+renesas@glider.be
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
352 lines
8.1 KiB
C
352 lines
8.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
|
|
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/netdevice.h>
|
|
|
|
#include "hinic3_common.h"
|
|
#include "hinic3_hw_comm.h"
|
|
#include "hinic3_hwdev.h"
|
|
#include "hinic3_hwif.h"
|
|
#include "hinic3_lld.h"
|
|
#include "hinic3_nic_cfg.h"
|
|
#include "hinic3_nic_dev.h"
|
|
#include "hinic3_nic_io.h"
|
|
#include "hinic3_rx.h"
|
|
#include "hinic3_tx.h"
|
|
|
|
#define HINIC3_NIC_DRV_DESC "Intelligent Network Interface Card Driver"
|
|
|
|
#define HINIC3_RX_BUF_LEN 2048
|
|
#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
|
|
|
|
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_free_txqs;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_free_txqs:
|
|
hinic3_free_txqs(netdev);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void hinic3_free_txrxqs(struct net_device *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;
|
|
|
|
nic_dev->netdev = netdev;
|
|
SET_NETDEV_DEV(netdev, &pdev->dev);
|
|
nic_dev->hwdev = hwdev;
|
|
nic_dev->pdev = pdev;
|
|
|
|
nic_dev->rx_buf_len = HINIC3_RX_BUF_LEN;
|
|
nic_dev->lro_replenish_thld = HINIC3_LRO_REPLENISH_THLD;
|
|
nic_dev->nic_svc_cap = hwdev->cfg_mgmt->cap.nic_svc_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;
|
|
|
|
/* 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");
|
|
return err;
|
|
}
|
|
|
|
err = hinic3_alloc_txrxqs(netdev);
|
|
if (err) {
|
|
dev_err(hwdev->dev, "Failed to alloc qps\n");
|
|
goto err_del_mac;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_del_mac:
|
|
hinic3_del_mac(hwdev, netdev->dev_addr, 0,
|
|
hinic3_global_func_id(hwdev));
|
|
|
|
return err;
|
|
}
|
|
|
|
static void hinic3_sw_uninit(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));
|
|
}
|
|
|
|
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, HINIC3_NIC_F_CSUM))
|
|
cso_fts |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM;
|
|
if (hinic3_test_support(nic_dev, HINIC3_NIC_F_SCTP_CRC))
|
|
cso_fts |= NETIF_F_SCTP_CRC;
|
|
if (hinic3_test_support(nic_dev, HINIC3_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 (link_status_up) {
|
|
if (netif_carrier_ok(netdev))
|
|
return;
|
|
|
|
nic_dev->link_status_up = true;
|
|
netif_carrier_on(netdev);
|
|
netdev_dbg(netdev, "Link is up\n");
|
|
} else {
|
|
if (!netif_carrier_ok(netdev))
|
|
return;
|
|
|
|
nic_dev->link_status_up = false;
|
|
netif_carrier_off(netdev);
|
|
netdev_dbg(netdev, "Link is down\n");
|
|
}
|
|
}
|
|
|
|
static void hinic3_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(HINIC3_EVENT_SRV_NIC,
|
|
HINIC3_NIC_EVENT_LINK_UP):
|
|
hinic3_link_status_change(netdev, true);
|
|
break;
|
|
case HINIC3_SRV_EVENT_TYPE(HINIC3_EVENT_SRV_NIC,
|
|
HINIC3_NIC_EVENT_LINK_DOWN):
|
|
hinic3_link_status_change(netdev, false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int hinic3_nic_probe(struct auxiliary_device *adev,
|
|
const struct auxiliary_device_id *id)
|
|
{
|
|
struct hinic3_hwdev *hwdev = hinic3_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;
|
|
}
|
|
|
|
hinic3_adev_event_register(adev, hinic3_nic_event);
|
|
|
|
glb_func_id = hinic3_global_func_id(hwdev);
|
|
err = hinic3_func_reset(hwdev, glb_func_id, COMM_FUNC_RESET_BIT_NIC);
|
|
if (err) {
|
|
dev_err(&adev->dev, "Failed to reset function\n");
|
|
goto err_unregister_adev_event;
|
|
}
|
|
|
|
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;
|
|
goto err_unregister_adev_event;
|
|
}
|
|
|
|
nic_dev = netdev_priv(netdev);
|
|
dev_set_drvdata(&adev->dev, nic_dev);
|
|
err = hinic3_init_nic_dev(netdev, hwdev);
|
|
if (err)
|
|
goto err_free_netdev;
|
|
|
|
err = hinic3_init_nic_io(nic_dev);
|
|
if (err)
|
|
goto err_free_netdev;
|
|
|
|
err = hinic3_sw_init(netdev);
|
|
if (err)
|
|
goto err_free_nic_io;
|
|
|
|
hinic3_assign_netdev_ops(netdev);
|
|
|
|
netdev_feature_init(netdev);
|
|
err = hinic3_set_default_hw_feature(netdev);
|
|
if (err)
|
|
goto err_uninit_sw;
|
|
|
|
netif_carrier_off(netdev);
|
|
|
|
err = register_netdev(netdev);
|
|
if (err)
|
|
goto err_uninit_nic_feature;
|
|
|
|
return 0;
|
|
|
|
err_uninit_nic_feature:
|
|
hinic3_update_nic_feature(nic_dev, 0);
|
|
hinic3_set_nic_feature_to_hw(nic_dev);
|
|
|
|
err_uninit_sw:
|
|
hinic3_sw_uninit(netdev);
|
|
|
|
err_free_nic_io:
|
|
hinic3_free_nic_io(nic_dev);
|
|
|
|
err_free_netdev:
|
|
free_netdev(netdev);
|
|
|
|
err_unregister_adev_event:
|
|
hinic3_adev_event_unregister(adev);
|
|
dev_err(&pdev->dev, "NIC service probe failed\n");
|
|
|
|
return err;
|
|
}
|
|
|
|
static void hinic3_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_uninit(netdev);
|
|
|
|
hinic3_free_nic_io(nic_dev);
|
|
|
|
free_netdev(netdev);
|
|
}
|
|
|
|
static const struct auxiliary_device_id hinic3_nic_id_table[] = {
|
|
{
|
|
.name = HINIC3_NIC_DRV_NAME ".nic",
|
|
},
|
|
{}
|
|
};
|
|
|
|
static struct auxiliary_driver hinic3_nic_driver = {
|
|
.probe = hinic3_nic_probe,
|
|
.remove = hinic3_nic_remove,
|
|
.suspend = NULL,
|
|
.resume = NULL,
|
|
.name = "nic",
|
|
.id_table = hinic3_nic_id_table,
|
|
};
|
|
|
|
static __init int hinic3_nic_lld_init(void)
|
|
{
|
|
int err;
|
|
|
|
err = hinic3_lld_init();
|
|
if (err)
|
|
return err;
|
|
|
|
err = auxiliary_driver_register(&hinic3_nic_driver);
|
|
if (err) {
|
|
hinic3_lld_exit();
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static __exit void hinic3_nic_lld_exit(void)
|
|
{
|
|
auxiliary_driver_unregister(&hinic3_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");
|