[RFC PATCH net-next 08/10] net: hibmcge: Implement workqueue and some ethtool_ops functions
From: Jijie Shao <shaojijie@huawei.com>
Date: 2024-07-31 09:48:26
Also in:
lkml
Subsystem:
hisilicon network hibmcge driver, hisilicon network subsystem driver, networking drivers, the rest · Maintainers:
Jijie Shao, Jian Shen, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Implement a workqueue in this module, The workqueue is invoked once every second to update link status. Implement the .get_drvinfo .get_link .get_link_ksettings to get the basic information and working status of the driver. Implement the .set_link_ksettings to modify the rate, duplex, and auto-negotiation status. Signed-off-by: Jijie Shao <shaojijie@huawei.com> --- .../ethernet/hisilicon/hibmcge/hbg_common.h | 1 + .../ethernet/hisilicon/hibmcge/hbg_ethtool.c | 56 +++++++++++ .../ethernet/hisilicon/hibmcge/hbg_ethtool.h | 11 +++ .../net/ethernet/hisilicon/hibmcge/hbg_main.c | 96 ++++++++++++++++++- 4 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.h
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
index 25563af04897..45ec4b463e70 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h@@ -146,6 +146,7 @@ struct hbg_priv { struct hbg_vector vectors; struct hbg_ring tx_ring; struct hbg_ring rx_ring; + struct delayed_work service_task; }; #endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c
new file mode 100644
index 000000000000..3acd6eae189e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c@@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2024 Hisilicon Limited. + +#include <linux/ethtool.h> +#include <linux/phy.h> +#include "hbg_common.h" +#include "hbg_ethtool.h" +#include "hbg_hw.h" +#include "hbg_main.h" +#include "hbg_mdio.h" + +static void hbg_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + strscpy(drvinfo->version, HBG_MOD_VERSION, sizeof(drvinfo->version)); + drvinfo->version[sizeof(drvinfo->version) - 1] = '\0'; +} + +static u32 hbg_ethtool_get_link(struct net_device *netdev) +{ + struct hbg_priv *priv = netdev_priv(netdev); + + return priv->mac.link_status; +} + +static int hbg_ethtool_get_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *ksettings) +{ + struct hbg_priv *priv = netdev_priv(netdev); + + phy_ethtool_ksettings_get(priv->mac.phydev, ksettings); + return 0; +} + +static int hbg_ethtool_set_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) +{ + struct hbg_priv *priv = netdev_priv(netdev); + + if (cmd->base.speed == SPEED_1000 && cmd->base.duplex == DUPLEX_HALF) + return -EINVAL; + + return phy_ethtool_ksettings_set(priv->mac.phydev, cmd); +} + +static const struct ethtool_ops hbg_ethtool_ops = { + .get_drvinfo = hbg_ethtool_get_drvinfo, + .get_link = hbg_ethtool_get_link, + .get_link_ksettings = hbg_ethtool_get_ksettings, + .set_link_ksettings = hbg_ethtool_set_ksettings, +}; + +void hbg_ethtool_set_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &hbg_ethtool_ops; +}
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.h
new file mode 100644
index 000000000000..628707ec2686
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.h@@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2024 Hisilicon Limited. */ + +#ifndef __HBG_ETHTOOL_H +#define __HBG_ETHTOOL_H + +#include <linux/netdevice.h> + +void hbg_ethtool_set_ops(struct net_device *netdev); + +#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
index bb5f8321da8a..bea596123c37 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c@@ -6,12 +6,15 @@ #include <linux/netdevice.h> #include <linux/pci.h> #include "hbg_common.h" +#include "hbg_ethtool.h" #include "hbg_hw.h" #include "hbg_irq.h" #include "hbg_main.h" #include "hbg_mdio.h" #include "hbg_txrx.h" +static struct workqueue_struct *hbg_workqueue; + static void hbg_enable_intr(struct hbg_priv *priv, bool enabled) { u32 i;
@@ -136,6 +139,52 @@ static const struct net_device_ops hbg_netdev_ops = { .ndo_tx_timeout = hbg_net_tx_timeout, }; +static void hbg_update_link_status(struct hbg_priv *priv) +{ + u32 link; + + link = hbg_get_link_status(priv); + if (link == priv->mac.link_status) + return; + + priv->mac.link_status = link; + if (link == HBG_LINK_DOWN) { + netif_carrier_off(priv->netdev); + netif_tx_stop_all_queues(priv->netdev); + dev_info(&priv->pdev->dev, "link down!"); + } else { + netif_tx_wake_all_queues(priv->netdev); + netif_carrier_on(priv->netdev); + dev_info(&priv->pdev->dev, "link up!"); + } +} + +static void hbg_service_task(struct work_struct *work) +{ + struct hbg_priv *priv = + container_of(work, struct hbg_priv, service_task.work); + + hbg_update_link_status(priv); + + mod_delayed_work(hbg_workqueue, &priv->service_task, + round_jiffies_relative(HZ)); +} + +static void hbg_delaywork_init(struct hbg_priv *priv) +{ + INIT_DELAYED_WORK(&priv->service_task, hbg_service_task); + mod_delayed_work(hbg_workqueue, &priv->service_task, + round_jiffies_relative(HZ)); +} + +static void hbg_delaywork_uninit(void *data) +{ + struct hbg_priv *priv = data; + + if (priv->service_task.work.func) + cancel_delayed_work_sync(&priv->service_task); +} + static const u32 hbg_mode_ability[] = { ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ETHTOOL_LINK_MODE_100baseT_Full_BIT,
@@ -177,12 +226,17 @@ static int hbg_init(struct net_device *netdev) ret = hbg_irq_init(priv); if (ret) return ret; - ret = devm_add_action_or_reset(&priv->pdev->dev, hbg_irq_uninit, priv); if (ret) return ret; - return hbg_mac_init(priv); + ret = hbg_mac_init(priv); + if (ret) + return ret; + + hbg_delaywork_init(priv); + return devm_add_action_or_reset(&priv->pdev->dev, + hbg_delaywork_uninit, priv); } static int hbg_pci_init(struct pci_dev *pdev)
@@ -249,6 +303,7 @@ static int hbg_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return dev_err_probe(&pdev->dev, ret, "failed to register netdev\n"); + hbg_ethtool_set_ops(netdev); set_bit(HBG_NIC_STATE_INITED, &priv->state); return 0; }
@@ -264,7 +319,42 @@ static struct pci_driver hbg_driver = { .id_table = hbg_pci_tbl, .probe = hbg_probe, }; -module_pci_driver(hbg_driver); + +static int __init hbg_module_init(void) +{ + int ret; + + hbg_workqueue = alloc_workqueue("%s", WQ_UNBOUND, 0, HBG_DEV_NAME); + if (!hbg_workqueue) { + pr_err("%s: failed to create workqueue\n", HBG_DEV_NAME); + return -ENOMEM; + } + + ret = pci_register_driver(&hbg_driver); + if (ret) { + pr_err("%s: failed to register PCI driver, ret = %d\n", + HBG_DEV_NAME, ret); + goto err_destroy_workqueue; + } + + return 0; + +err_destroy_workqueue: + destroy_workqueue(hbg_workqueue); + hbg_workqueue = NULL; + + return ret; +} +module_init(hbg_module_init); + +static void __exit hbg_module_exit(void) +{ + pci_unregister_driver(&hbg_driver); + flush_workqueue(hbg_workqueue); + destroy_workqueue(hbg_workqueue); + hbg_workqueue = NULL; +} +module_exit(hbg_module_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
--
2.33.0