* [PATCH V5 net-next 01/11] net: hibmcge: Add pci table supported in this module
2024-08-27 13:14 [PATCH V5 net-next 00/11] Add support of HIBMCGE Ethernet Driver Jijie Shao
@ 2024-08-27 13:14 ` Jijie Shao
2024-08-27 13:14 ` [PATCH V5 net-next 02/11] net: hibmcge: Add read/write registers supported through the bar space Jijie Shao
` (9 subsequent siblings)
10 siblings, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-27 13:14 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: shenjian15, wangpeiyang1, liuyonglong, chenhao418, shaojijie,
sudongming1, xujunsheng, shiyongbang, libaihan, andrew, jdamato,
horms, jonathan.cameron, shameerali.kolothum.thodi, salil.mehta,
netdev, linux-kernel
Add pci table supported in this module, and implement pci_driver function
to initialize this driver.
hibmcge is a passthrough network device. Its software runs
on the host side, and the MAC hardware runs on the BMC side
to reduce the host CPU area. The software interacts with the
MAC hardware through the PCIe.
┌─────────────────────────┐
│ HOST CPU network device │
│ ┌──────────────┐ │
│ │hibmcge driver│ │
│ └─────┬─┬──────┘ │
│ │ │ │
│HOST ┌───┴─┴───┐ │
│ │ PCIE RC │ │
└──────┴───┬─┬───┴────────┘
│ │
PCIE
│ │
┌──────┬───┴─┴───┬────────┐
│ │ PCIE EP │ │
│BMC └───┬─┬───┘ │
│ │ │ │
│ ┌────────┴─┴──────────┐ │
│ │ GE │ │
│ │ ┌─────┐ ┌─────┐ │ │
│ │ │ MAC │ │ MAC │ │ │
└─┴─┼─────┼────┼─────┼──┴─┘
│ PHY │ │ PHY │
└─────┘ └─────┘
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
.../ethernet/hisilicon/hibmcge/hbg_common.h | 16 ++++
.../net/ethernet/hisilicon/hibmcge/hbg_main.c | 82 +++++++++++++++++++
2 files changed, 98 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
new file mode 100644
index 000000000000..614650e9a71f
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2024 Hisilicon Limited. */
+
+#ifndef __HBG_COMMON_H
+#define __HBG_COMMON_H
+
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+
+struct hbg_priv {
+ struct net_device *netdev;
+ struct pci_dev *pdev;
+ u8 __iomem *io_base;
+};
+
+#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
new file mode 100644
index 000000000000..9195c7fb13e3
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (c) 2024 Hisilicon Limited.
+
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include "hbg_common.h"
+
+static int hbg_pci_init(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct hbg_priv *priv = netdev_priv(netdev);
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable PCI device\n");
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to set PCI DMA mask\n");
+
+ ret = pcim_iomap_regions(pdev, BIT(0), dev_driver_string(dev));
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to map PCI bar space\n");
+
+ priv->io_base = pcim_iomap_table(pdev)[0];
+ if (!priv->io_base)
+ return dev_err_probe(dev, -ENOMEM, "failed to get io base\n");
+
+ pci_set_master(pdev);
+ return 0;
+}
+
+static int hbg_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct device *dev = &pdev->dev;
+ struct net_device *netdev;
+ struct hbg_priv *priv;
+ int ret;
+
+ netdev = devm_alloc_etherdev_mqs(dev, sizeof(struct hbg_priv), 1, 1);
+ if (!netdev)
+ return -ENOMEM;
+
+ pci_set_drvdata(pdev, netdev);
+
+ SET_NETDEV_DEV(netdev, dev);
+
+ priv = netdev_priv(netdev);
+ priv->netdev = netdev;
+ priv->pdev = pdev;
+
+ ret = hbg_pci_init(pdev);
+ if (ret)
+ return ret;
+
+ ret = devm_register_netdev(dev, netdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register netdev\n");
+
+ return 0;
+}
+
+static const struct pci_device_id hbg_pci_tbl[] = {
+ {PCI_VDEVICE(HUAWEI, 0x3730), 0},
+ { }
+};
+MODULE_DEVICE_TABLE(pci, hbg_pci_tbl);
+
+static struct pci_driver hbg_driver = {
+ .name = "hibmcge",
+ .id_table = hbg_pci_tbl,
+ .probe = hbg_probe,
+};
+module_pci_driver(hbg_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
+MODULE_DESCRIPTION("hibmcge driver");
+MODULE_VERSION("1.0");
--
2.33.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH V5 net-next 02/11] net: hibmcge: Add read/write registers supported through the bar space
2024-08-27 13:14 [PATCH V5 net-next 00/11] Add support of HIBMCGE Ethernet Driver Jijie Shao
2024-08-27 13:14 ` [PATCH V5 net-next 01/11] net: hibmcge: Add pci table supported in this module Jijie Shao
@ 2024-08-27 13:14 ` Jijie Shao
2024-08-27 13:14 ` [PATCH V5 net-next 03/11] net: hibmcge: Add mdio and hardware configuration supported in this module Jijie Shao
` (8 subsequent siblings)
10 siblings, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-27 13:14 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: shenjian15, wangpeiyang1, liuyonglong, chenhao418, shaojijie,
sudongming1, xujunsheng, shiyongbang, libaihan, andrew, jdamato,
horms, jonathan.cameron, shameerali.kolothum.thodi, salil.mehta,
netdev, linux-kernel
Add support for to read and write registers through the pic bar space.
Some driver parameters, such as mac_id, are determined by the
board form. Therefore, these parameters are initialized
from the register as device specifications.
the device specifications register are initialized and writed by bmc.
driver will read these registers when loading.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
.../ethernet/hisilicon/hibmcge/hbg_common.h | 26 ++++++
.../net/ethernet/hisilicon/hibmcge/hbg_hw.c | 82 +++++++++++++++++++
.../net/ethernet/hisilicon/hibmcge/hbg_hw.h | 32 ++++++++
.../net/ethernet/hisilicon/hibmcge/hbg_main.c | 10 +++
.../net/ethernet/hisilicon/hibmcge/hbg_reg.h | 20 +++++
5 files changed, 170 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
index 614650e9a71f..6fbc24803942 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
@@ -7,10 +7,36 @@
#include <linux/netdevice.h>
#include <linux/pci.h>
+enum hbg_nic_state {
+ HBG_NIC_STATE_EVENT_HANDLING = 0,
+};
+
+enum hbg_hw_event_type {
+ HBG_HW_EVENT_NONE = 0,
+ HBG_HW_EVENT_INIT, /* driver is loading */
+};
+
+struct hbg_dev_specs {
+ u32 mac_id;
+ struct sockaddr mac_addr;
+ u32 phy_addr;
+ u32 mdio_frequency;
+ u32 rx_fifo_num;
+ u32 tx_fifo_num;
+ u32 vlan_layers;
+ u32 max_mtu;
+ u32 min_mtu;
+
+ u32 max_frame_len;
+ u32 rx_buf_size;
+};
+
struct hbg_priv {
struct net_device *netdev;
struct pci_dev *pdev;
u8 __iomem *io_base;
+ struct hbg_dev_specs dev_specs;
+ unsigned long state;
};
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
new file mode 100644
index 000000000000..677f81022411
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (c) 2024 Hisilicon Limited.
+
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/iopoll.h>
+#include <linux/minmax.h>
+#include "hbg_common.h"
+#include "hbg_hw.h"
+#include "hbg_reg.h"
+
+static bool hbg_hw_spec_is_valid(struct hbg_priv *priv)
+{
+ return hbg_reg_read(priv, HBG_REG_SPEC_VALID_ADDR) &&
+ !hbg_reg_read(priv, HBG_REG_EVENT_REQ_ADDR);
+}
+
+static int hbg_hw_event_notify(struct hbg_priv *priv,
+ enum hbg_hw_event_type event_type)
+{
+#define HBG_HW_EVENT_WAIT_TIMEOUT_US (2 * 1000 * 1000)
+#define HBG_HW_EVENT_WAIT_INTERVAL_US (10 * 1000)
+
+ bool is_valid;
+ int ret;
+
+ if (test_and_set_bit(HBG_NIC_STATE_EVENT_HANDLING, &priv->state))
+ return -EBUSY;
+
+ /* notify */
+ hbg_reg_write(priv, HBG_REG_EVENT_REQ_ADDR, event_type);
+
+ ret = read_poll_timeout(hbg_hw_spec_is_valid, is_valid, is_valid,
+ HBG_HW_EVENT_WAIT_INTERVAL_US,
+ HBG_HW_EVENT_WAIT_TIMEOUT_US,
+ HBG_HW_EVENT_WAIT_INTERVAL_US, priv);
+
+ clear_bit(HBG_NIC_STATE_EVENT_HANDLING, &priv->state);
+
+ if (ret)
+ dev_err(&priv->pdev->dev, "event %d wait timeout\n", event_type);
+
+ return ret;
+}
+
+static int hbg_hw_dev_specs_init(struct hbg_priv *priv)
+{
+ struct hbg_dev_specs *dev_specs = &priv->dev_specs;
+ u64 mac_addr;
+
+ if (!hbg_hw_spec_is_valid(priv)) {
+ dev_err(&priv->pdev->dev, "dev_specs not init\n");
+ return -EINVAL;
+ }
+
+ dev_specs->mac_id = hbg_reg_read(priv, HBG_REG_MAC_ID_ADDR);
+ dev_specs->phy_addr = hbg_reg_read(priv, HBG_REG_PHY_ID_ADDR);
+ dev_specs->mdio_frequency = hbg_reg_read(priv, HBG_REG_MDIO_FREQ_ADDR);
+ dev_specs->max_mtu = hbg_reg_read(priv, HBG_REG_MAX_MTU_ADDR);
+ dev_specs->min_mtu = hbg_reg_read(priv, HBG_REG_MIN_MTU_ADDR);
+ dev_specs->vlan_layers = hbg_reg_read(priv, HBG_REG_VLAN_LAYERS_ADDR);
+ dev_specs->rx_fifo_num = hbg_reg_read(priv, HBG_REG_RX_FIFO_NUM_ADDR);
+ dev_specs->tx_fifo_num = hbg_reg_read(priv, HBG_REG_TX_FIFO_NUM_ADDR);
+ mac_addr = hbg_reg_read64(priv, HBG_REG_MAC_ADDR_ADDR);
+ u64_to_ether_addr(mac_addr, (u8 *)dev_specs->mac_addr.sa_data);
+
+ if (!is_valid_ether_addr((u8 *)dev_specs->mac_addr.sa_data))
+ return -EADDRNOTAVAIL;
+
+ return 0;
+}
+
+int hbg_hw_init(struct hbg_priv *priv)
+{
+ int ret;
+
+ ret = hbg_hw_event_notify(priv, HBG_HW_EVENT_INIT);
+ if (ret)
+ return ret;
+
+ return hbg_hw_dev_specs_init(priv);
+}
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
new file mode 100644
index 000000000000..7460701412a1
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2024 Hisilicon Limited. */
+
+#ifndef __HBG_HW_H
+#define __HBG_HW_H
+
+#include <linux/bitfield.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+
+static inline u32 hbg_reg_read(struct hbg_priv *priv, u32 addr)
+{
+ return readl(priv->io_base + addr);
+}
+
+static inline void hbg_reg_write(struct hbg_priv *priv, u32 addr, u32 value)
+{
+ writel(value, priv->io_base + addr);
+}
+
+static inline u64 hbg_reg_read64(struct hbg_priv *priv, u32 addr)
+{
+ return lo_hi_readq(priv->io_base + addr);
+}
+
+static inline void hbg_reg_write64(struct hbg_priv *priv, u32 addr, u64 value)
+{
+ lo_hi_writeq(value, priv->io_base + addr);
+}
+
+int hbg_hw_init(struct hbg_priv *priv);
+
+#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
index 9195c7fb13e3..a5c12c4b7d89 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
@@ -5,6 +5,12 @@
#include <linux/netdevice.h>
#include <linux/pci.h>
#include "hbg_common.h"
+#include "hbg_hw.h"
+
+static int hbg_init(struct hbg_priv *priv)
+{
+ return hbg_hw_init(priv);
+}
static int hbg_pci_init(struct pci_dev *pdev)
{
@@ -56,6 +62,10 @@ static int hbg_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
return ret;
+ ret = hbg_init(priv);
+ if (ret)
+ return ret;
+
ret = devm_register_netdev(dev, netdev);
if (ret)
return dev_err_probe(dev, ret, "failed to register netdev\n");
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
new file mode 100644
index 000000000000..77153f1132fd
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2024 Hisilicon Limited. */
+
+#ifndef __HBG_REG_H
+#define __HBG_REG_H
+
+/* DEV SPEC */
+#define HBG_REG_SPEC_VALID_ADDR 0x0000
+#define HBG_REG_EVENT_REQ_ADDR 0x0004
+#define HBG_REG_MAC_ID_ADDR 0x0008
+#define HBG_REG_PHY_ID_ADDR 0x000C
+#define HBG_REG_MAC_ADDR_ADDR 0x0010
+#define HBG_REG_MDIO_FREQ_ADDR 0x0024
+#define HBG_REG_MAX_MTU_ADDR 0x0028
+#define HBG_REG_MIN_MTU_ADDR 0x002C
+#define HBG_REG_TX_FIFO_NUM_ADDR 0x0030
+#define HBG_REG_RX_FIFO_NUM_ADDR 0x0034
+#define HBG_REG_VLAN_LAYERS_ADDR 0x0038
+
+#endif
--
2.33.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH V5 net-next 03/11] net: hibmcge: Add mdio and hardware configuration supported in this module
2024-08-27 13:14 [PATCH V5 net-next 00/11] Add support of HIBMCGE Ethernet Driver Jijie Shao
2024-08-27 13:14 ` [PATCH V5 net-next 01/11] net: hibmcge: Add pci table supported in this module Jijie Shao
2024-08-27 13:14 ` [PATCH V5 net-next 02/11] net: hibmcge: Add read/write registers supported through the bar space Jijie Shao
@ 2024-08-27 13:14 ` Jijie Shao
2024-08-27 13:14 ` [PATCH V5 net-next 04/11] net: hibmcge: Add interrupt " Jijie Shao
` (7 subsequent siblings)
10 siblings, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-27 13:14 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: shenjian15, wangpeiyang1, liuyonglong, chenhao418, shaojijie,
sudongming1, xujunsheng, shiyongbang, libaihan, andrew, jdamato,
horms, jonathan.cameron, shameerali.kolothum.thodi, salil.mehta,
netdev, linux-kernel
this driver using phy through genphy device. Implements the C22
read and write PHY registers interfaces.
Some hardware interfaces related to the PHY are also implemented
in this patch.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
.../ethernet/hisilicon/hibmcge/hbg_common.h | 19 ++
.../net/ethernet/hisilicon/hibmcge/hbg_hw.c | 82 +++++-
.../net/ethernet/hisilicon/hibmcge/hbg_hw.h | 17 +-
.../net/ethernet/hisilicon/hibmcge/hbg_main.c | 9 +-
.../net/ethernet/hisilicon/hibmcge/hbg_mdio.c | 245 ++++++++++++++++++
.../net/ethernet/hisilicon/hibmcge/hbg_mdio.h | 12 +
.../net/ethernet/hisilicon/hibmcge/hbg_reg.h | 55 ++++
7 files changed, 436 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.h
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
index 6fbc24803942..e047539a407a 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
@@ -6,6 +6,13 @@
#include <linux/netdevice.h>
#include <linux/pci.h>
+#include "hbg_reg.h"
+
+#define HBG_STATUS_DISABLE 0x0
+#define HBG_STATUS_ENABLE 0x1
+#define HBG_DEFAULT_MTU_SIZE 1500
+#define HBG_RX_SKIP1 0x00
+#define HBG_RX_SKIP2 0x01
enum hbg_nic_state {
HBG_NIC_STATE_EVENT_HANDLING = 0,
@@ -31,12 +38,24 @@ struct hbg_dev_specs {
u32 rx_buf_size;
};
+struct hbg_mac {
+ struct mii_bus *mdio_bus;
+ struct phy_device *phydev;
+ u8 phy_addr;
+
+ u32 speed;
+ u32 duplex;
+ u32 autoneg;
+ u32 link_status;
+};
+
struct hbg_priv {
struct net_device *netdev;
struct pci_dev *pdev;
u8 __iomem *io_base;
struct hbg_dev_specs dev_specs;
unsigned long state;
+ struct hbg_mac mac;
};
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
index 677f81022411..61769bad284c 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
@@ -70,13 +70,93 @@ static int hbg_hw_dev_specs_init(struct hbg_priv *priv)
return 0;
}
+void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex)
+{
+ hbg_reg_write_field(priv, HBG_REG_PORT_MODE_ADDR,
+ HBG_REG_PORT_MODE_M, speed);
+ hbg_reg_write_field(priv, HBG_REG_DUPLEX_TYPE_ADDR,
+ HBG_REG_DUPLEX_B, duplex);
+}
+
+static void hbg_hw_init_transmit_control(struct hbg_priv *priv)
+{
+ u32 control = 0;
+
+ control |= FIELD_PREP(HBG_REG_TRANSMIT_CONTROL_AN_EN_B, HBG_STATUS_ENABLE);
+ control |= FIELD_PREP(HBG_REG_TRANSMIT_CONTROL_CRC_ADD_B, HBG_STATUS_ENABLE);
+ control |= FIELD_PREP(HBG_REG_TRANSMIT_CONTROL_PAD_EN_B, HBG_STATUS_ENABLE);
+
+ hbg_reg_write(priv, HBG_REG_TRANSMIT_CONTROL_ADDR, control);
+}
+
+static void hbg_hw_init_rx_ctrl(struct hbg_priv *priv)
+{
+ u32 ctrl = 0;
+
+ ctrl |= FIELD_PREP(HBG_REG_RX_CTRL_RX_GET_ADDR_MODE_B, HBG_STATUS_ENABLE);
+ ctrl |= FIELD_PREP(HBG_REG_RX_CTRL_TIME_INF_EN_B, HBG_STATUS_DISABLE);
+ ctrl |= FIELD_PREP(HBG_REG_RX_CTRL_RXBUF_1ST_SKIP_SIZE_M, HBG_RX_SKIP1);
+ ctrl |= FIELD_PREP(HBG_REG_RX_CTRL_RXBUF_1ST_SKIP_SIZE2_M, HBG_RX_SKIP2);
+ ctrl |= FIELD_PREP(HBG_REG_RX_CTRL_RX_ALIGN_NUM_M, NET_IP_ALIGN);
+ ctrl |= FIELD_PREP(HBG_REG_RX_CTRL_PORT_NUM, priv->dev_specs.mac_id);
+
+ hbg_reg_write(priv, HBG_REG_RX_CTRL_ADDR, ctrl);
+}
+
+static void hbg_hw_init_rx_pkt_mode(struct hbg_priv *priv)
+{
+ u32 mode = 0;
+
+ /* parse from L2 layer */
+ mode |= FIELD_PREP(HBG_REG_RX_PKT_MODE_PARSE_MODE_M, 0x1);
+
+ hbg_reg_write(priv, HBG_REG_RX_PKT_MODE_ADDR, mode);
+}
+
+static void hbg_hw_init_recv_ctrl(struct hbg_priv *priv)
+{
+ u32 ctrl = 0;
+
+ ctrl |= FIELD_PREP(HBG_REG_RECV_CONTROL_STRIP_PAD_EN_B, HBG_STATUS_ENABLE);
+
+ hbg_reg_write(priv, HBG_REG_RECV_CONTROL_ADDR, ctrl);
+}
+
+static void hbg_hw_init_rx_control(struct hbg_priv *priv)
+{
+ hbg_hw_init_rx_ctrl(priv);
+ hbg_hw_init_rx_pkt_mode(priv);
+ hbg_hw_init_recv_ctrl(priv);
+ hbg_reg_write_field(priv, HBG_REG_RX_BUF_SIZE_ADDR,
+ HBG_REG_RX_BUF_SIZE_M, priv->dev_specs.rx_buf_size);
+ hbg_reg_write_field(priv, HBG_REG_CF_CRC_STRIP_ADDR,
+ HBG_REG_CF_CRC_STRIP_B, HBG_STATUS_DISABLE);
+}
+
int hbg_hw_init(struct hbg_priv *priv)
{
+/* little endian or big endian.
+ * ctrl means packet description, data means skb packet data
+ */
+#define HBG_ENDIAN_CTRL_LE_DATA_BE 0x0
+
int ret;
ret = hbg_hw_event_notify(priv, HBG_HW_EVENT_INIT);
if (ret)
return ret;
- return hbg_hw_dev_specs_init(priv);
+ ret = hbg_hw_dev_specs_init(priv);
+ if (ret)
+ return ret;
+
+ hbg_reg_write_field(priv, HBG_REG_BUS_CTRL_ADDR,
+ HBG_REG_BUS_CTRL_ENDIAN_M,
+ HBG_ENDIAN_CTRL_LE_DATA_BE);
+ hbg_reg_write_field(priv, HBG_REG_MODE_CHANGE_EN_ADDR,
+ HBG_REG_MODE_CHANGE_EN_B, HBG_STATUS_ENABLE);
+
+ hbg_hw_init_rx_control(priv);
+ hbg_hw_init_transmit_control(priv);
+ return 0;
}
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
index 7460701412a1..88fa378db757 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
@@ -27,6 +27,21 @@ static inline void hbg_reg_write64(struct hbg_priv *priv, u32 addr, u64 value)
lo_hi_writeq(value, priv->io_base + addr);
}
-int hbg_hw_init(struct hbg_priv *priv);
+#define hbg_reg_read_field(priv, addr, mask) \
+ FIELD_GET(mask, hbg_reg_read(priv, addr))
+
+#define hbg_field_modify(reg_value, mask, value) ({ \
+ (reg_value) &= ~(mask); \
+ (reg_value) |= FIELD_PREP(mask, value); })
+
+#define hbg_reg_write_field(priv, addr, mask, val) ({ \
+ typeof(priv) _priv = (priv); \
+ typeof(addr) _addr = (addr); \
+ u32 _value = hbg_reg_read(_priv, _addr); \
+ hbg_field_modify(_value, mask, val); \
+ hbg_reg_write(_priv, _addr, _value); })
+
+int hbg_hw_init(struct hbg_priv *pri);
+void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
index a5c12c4b7d89..132711554c1e 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
@@ -6,10 +6,17 @@
#include <linux/pci.h>
#include "hbg_common.h"
#include "hbg_hw.h"
+#include "hbg_mdio.h"
static int hbg_init(struct hbg_priv *priv)
{
- return hbg_hw_init(priv);
+ int ret;
+
+ ret = hbg_hw_init(priv);
+ if (ret)
+ return ret;
+
+ return hbg_mdio_init(priv);
}
static int hbg_pci_init(struct pci_dev *pdev)
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c
new file mode 100644
index 000000000000..ec0d536b5b2a
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (c) 2024 Hisilicon Limited.
+
+#include <linux/phy.h>
+#include "hbg_common.h"
+#include "hbg_hw.h"
+#include "hbg_mdio.h"
+#include "hbg_reg.h"
+
+#define HBG_MAC_GET_PRIV(mac) ((struct hbg_priv *)(mac)->mdio_bus->priv)
+#define HBG_MII_BUS_GET_MAC(bus) (&((struct hbg_priv *)(bus)->priv)->mac)
+
+#define HBG_MDIO_C22_MODE 0x1
+#define HBG_MDIO_C22_REG_WRITE 0x1
+#define HBG_MDIO_C22_REG_READ 0x2
+
+static void hbg_mdio_set_command(struct hbg_mac *mac, u32 cmd)
+{
+ hbg_reg_write(HBG_MAC_GET_PRIV(mac), HBG_REG_MDIO_COMMAND_ADDR, cmd);
+}
+
+static void hbg_mdio_get_command(struct hbg_mac *mac, u32 *cmd)
+{
+ *cmd = hbg_reg_read(HBG_MAC_GET_PRIV(mac), HBG_REG_MDIO_COMMAND_ADDR);
+}
+
+static void hbg_mdio_set_wdata_reg(struct hbg_mac *mac, u16 wdata_value)
+{
+ hbg_reg_write_field(HBG_MAC_GET_PRIV(mac), HBG_REG_MDIO_WDATA_ADDR,
+ HBG_REG_MDIO_WDATA_M, wdata_value);
+}
+
+static u32 hbg_mdio_get_rdata_reg(struct hbg_mac *mac)
+{
+ return hbg_reg_read_field(HBG_MAC_GET_PRIV(mac),
+ HBG_REG_MDIO_RDATA_ADDR,
+ HBG_REG_MDIO_WDATA_M);
+}
+
+static int hbg_mdio_check_op_status(struct hbg_mac *mac)
+{
+ struct hbg_priv *priv = HBG_MAC_GET_PRIV(mac);
+
+ return hbg_reg_read(priv, HBG_REG_MDIO_STA_ADDR) ? -EBUSY : 0;
+}
+
+static int hbg_mdio_wait_ready(struct hbg_mac *mac)
+{
+#define HBG_MDIO_OP_TIMEOUT_US (1 * 1000 * 1000)
+#define HBG_MDIO_OP_INTERVAL_US (5 * 1000)
+
+ struct hbg_priv *priv = HBG_MAC_GET_PRIV(mac);
+ u32 cmd;
+
+ return readl_poll_timeout(priv->io_base + HBG_REG_MDIO_COMMAND_ADDR, cmd,
+ !FIELD_GET(HBG_REG_MDIO_COMMAND_START_B, cmd),
+ HBG_MDIO_OP_INTERVAL_US,
+ HBG_MDIO_OP_TIMEOUT_US);
+}
+
+static int hbg_mdio_check_send_result(struct hbg_mac *mac)
+{
+ int ret;
+
+ ret = hbg_mdio_wait_ready(mac);
+ if (ret)
+ return ret;
+
+ return hbg_mdio_check_op_status(mac);
+}
+
+static int hbg_mdio_cmd_send(struct hbg_mac *mac, u32 prt_addr, u32 dev_addr,
+ u32 type, u32 op_code)
+{
+ u32 cmd = 0;
+
+ hbg_mdio_get_command(mac, &cmd);
+ hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_ST_M, type);
+ hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_OP_M, op_code);
+ hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_PRTAD_M, prt_addr);
+ hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_DEVAD_M, dev_addr);
+
+ /* if auto scan enabled, this value need fix to 0 */
+ hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_START_B, 0x1);
+
+ hbg_mdio_set_command(mac, cmd);
+
+ /* wait operation complete and check the result */
+ return hbg_mdio_check_send_result(mac);
+}
+
+static int hbg_mdio_read22(struct mii_bus *bus, int phy_addr, int regnum)
+{
+ struct hbg_mac *mac = HBG_MII_BUS_GET_MAC(bus);
+ int ret;
+
+ ret = hbg_mdio_check_op_status(mac);
+ if (ret)
+ return ret;
+
+ ret = hbg_mdio_cmd_send(mac, phy_addr, regnum, HBG_MDIO_C22_MODE,
+ HBG_MDIO_C22_REG_READ);
+ if (ret)
+ return ret;
+
+ return hbg_mdio_get_rdata_reg(mac);
+}
+
+static int hbg_mdio_write22(struct mii_bus *bus, int phy_addr, int regnum,
+ u16 val)
+{
+ struct hbg_mac *mac = HBG_MII_BUS_GET_MAC(bus);
+ int ret;
+
+ ret = hbg_mdio_check_op_status(mac);
+ if (ret)
+ return ret;
+
+ hbg_mdio_set_wdata_reg(mac, val);
+ return hbg_mdio_cmd_send(mac, phy_addr, regnum, HBG_MDIO_C22_MODE,
+ HBG_MDIO_C22_REG_WRITE);
+}
+
+static int hbg_mdio_init_hw(struct hbg_priv *priv)
+{
+ u32 freq = priv->dev_specs.mdio_frequency;
+ struct hbg_mac *mac = &priv->mac;
+ u32 cmd = 0;
+
+ cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_ST_M, HBG_MDIO_C22_MODE);
+ cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_AUTO_SCAN_B, HBG_STATUS_DISABLE);
+
+ /* freq use two bits, which are stored in clk_sel and clk_sel_exp */
+ cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_CLK_SEL_B, freq & 0x1);
+ cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_CLK_SEL_EXP_B, (freq >> 1) & 0x1);
+
+ hbg_mdio_set_command(mac, cmd);
+ return 0;
+}
+
+static void hbg_phy_adjust_link(struct net_device *netdev)
+{
+ struct hbg_priv *priv = netdev_priv(netdev);
+ struct phy_device *phydev = priv->mac.phydev;
+ u32 speed;
+
+ if (phydev->link != priv->mac.link_status) {
+ if (phydev->link) {
+ switch (phydev->speed) {
+ case SPEED_10:
+ speed = HBG_PORT_MODE_SGMII_10M;
+ break;
+ case SPEED_100:
+ speed = HBG_PORT_MODE_SGMII_100M;
+ break;
+ case SPEED_1000:
+ speed = HBG_PORT_MODE_SGMII_1000M;
+ break;
+ default:
+ return;
+ }
+
+ priv->mac.speed = speed;
+ priv->mac.duplex = phydev->duplex;
+ priv->mac.autoneg = phydev->autoneg;
+ hbg_hw_adjust_link(priv, speed, phydev->duplex);
+ }
+
+ priv->mac.link_status = phydev->link;
+ phy_print_status(phydev);
+ }
+}
+
+static void hbg_phy_disconnect(void *data)
+{
+ phy_disconnect((struct phy_device *)data);
+}
+
+static int hbg_phy_connect(struct hbg_priv *priv)
+{
+ struct phy_device *phydev = priv->mac.phydev;
+ struct device *dev = &priv->pdev->dev;
+ struct hbg_mac *mac = &priv->mac;
+ int ret;
+
+ ret = phy_connect_direct(priv->netdev, mac->phydev, hbg_phy_adjust_link,
+ PHY_INTERFACE_MODE_SGMII);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to connect phy\n");
+
+ ret = devm_add_action_or_reset(dev, hbg_phy_disconnect, mac->phydev);
+ if (ret)
+ return ret;
+
+ phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
+ phy_attached_info(phydev);
+
+ return 0;
+}
+
+void hbg_phy_start(struct hbg_priv *priv)
+{
+ phy_start(priv->mac.phydev);
+}
+
+void hbg_phy_stop(struct hbg_priv *priv)
+{
+ phy_stop(priv->mac.phydev);
+}
+
+int hbg_mdio_init(struct hbg_priv *priv)
+{
+ struct device *dev = &priv->pdev->dev;
+ struct hbg_mac *mac = &priv->mac;
+ struct phy_device *phydev;
+ struct mii_bus *mdio_bus;
+ int ret;
+
+ mac->phy_addr = priv->dev_specs.phy_addr;
+ mdio_bus = devm_mdiobus_alloc(dev);
+ if (!mdio_bus)
+ return dev_err_probe(dev, -ENOMEM, "failed to alloc MDIO bus\n");
+
+ mdio_bus->parent = dev;
+ mdio_bus->priv = priv;
+ mdio_bus->phy_mask = ~(1 << mac->phy_addr);
+ mdio_bus->name = "hibmcge mii bus";
+ mac->mdio_bus = mdio_bus;
+
+ mdio_bus->read = hbg_mdio_read22;
+ mdio_bus->write = hbg_mdio_write22;
+ snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%s", "mii", dev_name(dev));
+
+ ret = devm_mdiobus_register(dev, mdio_bus);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register MDIO bus\n");
+
+ phydev = mdiobus_get_phy(mdio_bus, mac->phy_addr);
+ if (!phydev)
+ return dev_err_probe(dev, -ENODEV, "failed to get phy device\n");
+
+ mac->phydev = phydev;
+ hbg_mdio_init_hw(priv);
+ return hbg_phy_connect(priv);
+}
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.h
new file mode 100644
index 000000000000..febd02a309c7
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2024 Hisilicon Limited. */
+
+#ifndef __HBG_MDIO_H
+#define __HBG_MDIO_H
+
+#include "hbg_common.h"
+
+int hbg_mdio_init(struct hbg_priv *priv);
+void hbg_phy_start(struct hbg_priv *priv);
+void hbg_phy_stop(struct hbg_priv *priv);
+#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
index 77153f1132fd..81e6d6e9a429 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
@@ -17,4 +17,59 @@
#define HBG_REG_RX_FIFO_NUM_ADDR 0x0034
#define HBG_REG_VLAN_LAYERS_ADDR 0x0038
+/* MDIO */
+#define HBG_REG_MDIO_BASE 0x8000
+#define HBG_REG_MDIO_COMMAND_ADDR (HBG_REG_MDIO_BASE + 0x0000)
+#define HBG_REG_MDIO_COMMAND_CLK_SEL_EXP_B BIT(17)
+#define HBG_REG_MDIO_COMMAND_AUTO_SCAN_B BIT(16)
+#define HBG_REG_MDIO_COMMAND_CLK_SEL_B BIT(15)
+#define HBG_REG_MDIO_COMMAND_START_B BIT(14)
+#define HBG_REG_MDIO_COMMAND_ST_M GENMASK(13, 12)
+#define HBG_REG_MDIO_COMMAND_OP_M GENMASK(11, 10)
+#define HBG_REG_MDIO_COMMAND_PRTAD_M GENMASK(9, 5)
+#define HBG_REG_MDIO_COMMAND_DEVAD_M GENMASK(4, 0)
+#define HBG_REG_MDIO_WDATA_ADDR (HBG_REG_MDIO_BASE + 0x0008)
+#define HBG_REG_MDIO_WDATA_M GENMASK(15, 0)
+#define HBG_REG_MDIO_RDATA_ADDR (HBG_REG_MDIO_BASE + 0x000C)
+#define HBG_REG_MDIO_STA_ADDR (HBG_REG_MDIO_BASE + 0x0010)
+
+/* GMAC */
+#define HBG_REG_SGMII_BASE 0x10000
+#define HBG_REG_DUPLEX_TYPE_ADDR (HBG_REG_SGMII_BASE + 0x0008)
+#define HBG_REG_DUPLEX_B BIT(0)
+#define HBG_REG_PORT_MODE_ADDR (HBG_REG_SGMII_BASE + 0x0040)
+#define HBG_REG_PORT_MODE_M GENMASK(3, 0)
+#define HBG_REG_TRANSMIT_CONTROL_ADDR (HBG_REG_SGMII_BASE + 0x0060)
+#define HBG_REG_TRANSMIT_CONTROL_PAD_EN_B BIT(7)
+#define HBG_REG_TRANSMIT_CONTROL_CRC_ADD_B BIT(6)
+#define HBG_REG_TRANSMIT_CONTROL_AN_EN_B BIT(5)
+#define HBG_REG_CF_CRC_STRIP_ADDR (HBG_REG_SGMII_BASE + 0x01B0)
+#define HBG_REG_CF_CRC_STRIP_B BIT(0)
+#define HBG_REG_MODE_CHANGE_EN_ADDR (HBG_REG_SGMII_BASE + 0x01B4)
+#define HBG_REG_MODE_CHANGE_EN_B BIT(0)
+#define HBG_REG_RECV_CONTROL_ADDR (HBG_REG_SGMII_BASE + 0x01E0)
+#define HBG_REG_RECV_CONTROL_STRIP_PAD_EN_B BIT(3)
+
+/* PCU */
+#define HBG_REG_RX_BUF_SIZE_ADDR (HBG_REG_SGMII_BASE + 0x04E4)
+#define HBG_REG_RX_BUF_SIZE_M GENMASK(15, 0)
+#define HBG_REG_BUS_CTRL_ADDR (HBG_REG_SGMII_BASE + 0x04E8)
+#define HBG_REG_BUS_CTRL_ENDIAN_M GENMASK(2, 1)
+#define HBG_REG_RX_CTRL_ADDR (HBG_REG_SGMII_BASE + 0x04F0)
+#define HBG_REG_RX_CTRL_RXBUF_1ST_SKIP_SIZE_M GENMASK(31, 28)
+#define HBG_REG_RX_CTRL_TIME_INF_EN_B BIT(23)
+#define HBG_REG_RX_CTRL_RX_ALIGN_NUM_M GENMASK(18, 17)
+#define HBG_REG_RX_CTRL_PORT_NUM GENMASK(16, 13)
+#define HBG_REG_RX_CTRL_RX_GET_ADDR_MODE_B BIT(12)
+#define HBG_REG_RX_CTRL_RXBUF_1ST_SKIP_SIZE2_M GENMASK(3, 0)
+#define HBG_REG_RX_PKT_MODE_ADDR (HBG_REG_SGMII_BASE + 0x04F4)
+#define HBG_REG_RX_PKT_MODE_PARSE_MODE_M GENMASK(22, 21)
+
+enum hbg_port_mode {
+ /* 0x0 ~ 0x5 are reserved */
+ HBG_PORT_MODE_SGMII_10M = 0x6,
+ HBG_PORT_MODE_SGMII_100M = 0x7,
+ HBG_PORT_MODE_SGMII_1000M = 0x8,
+};
+
#endif
--
2.33.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH V5 net-next 04/11] net: hibmcge: Add interrupt supported in this module
2024-08-27 13:14 [PATCH V5 net-next 00/11] Add support of HIBMCGE Ethernet Driver Jijie Shao
` (2 preceding siblings ...)
2024-08-27 13:14 ` [PATCH V5 net-next 03/11] net: hibmcge: Add mdio and hardware configuration supported in this module Jijie Shao
@ 2024-08-27 13:14 ` Jijie Shao
2024-08-29 1:35 ` Jakub Kicinski
2024-08-27 13:14 ` [PATCH V5 net-next 05/11] net: hibmcge: Implement some .ndo functions Jijie Shao
` (6 subsequent siblings)
10 siblings, 1 reply; 28+ messages in thread
From: Jijie Shao @ 2024-08-27 13:14 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: shenjian15, wangpeiyang1, liuyonglong, chenhao418, shaojijie,
sudongming1, xujunsheng, shiyongbang, libaihan, andrew, jdamato,
horms, jonathan.cameron, shameerali.kolothum.thodi, salil.mehta,
netdev, linux-kernel
The driver supports four interrupts: TX interrupt, RX interrupt,
mdio interrupt, and error interrupt.
Actually, the driver does not use the mdio interrupt.
Therefore, the driver does not request the mdio interrupt.
The error interrupt distinguishes different error information
by using different masks. To distinguish different errors,
the statistics count is added for each error.
To ensure the consistency of the code process, masks are added for the
TX interrupt and RX interrupt.
This patch implements interrupt request and free, and provides a
unified entry for the interrupt handler function. However,
the specific interrupt handler function of each interrupt
is not implemented currently.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
.../ethernet/hisilicon/hibmcge/hbg_common.h | 18 +++
.../net/ethernet/hisilicon/hibmcge/hbg_hw.c | 55 +++++++++
.../net/ethernet/hisilicon/hibmcge/hbg_hw.h | 4 +
.../net/ethernet/hisilicon/hibmcge/hbg_irq.c | 112 ++++++++++++++++++
.../net/ethernet/hisilicon/hibmcge/hbg_irq.h | 11 ++
.../net/ethernet/hisilicon/hibmcge/hbg_main.c | 5 +
.../net/ethernet/hisilicon/hibmcge/hbg_reg.h | 27 +++++
7 files changed, 232 insertions(+)
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.h
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
index e047539a407a..e94ae2be5c4c 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
@@ -13,6 +13,7 @@
#define HBG_DEFAULT_MTU_SIZE 1500
#define HBG_RX_SKIP1 0x00
#define HBG_RX_SKIP2 0x01
+#define HBG_VECTOR_NUM 4
enum hbg_nic_state {
HBG_NIC_STATE_EVENT_HANDLING = 0,
@@ -38,6 +39,22 @@ struct hbg_dev_specs {
u32 rx_buf_size;
};
+struct hbg_irq_info {
+ const char *name;
+ u32 mask;
+ bool reenable;
+ bool need_print;
+ u64 count;
+
+ void (*irq_handle)(struct hbg_priv *priv, struct hbg_irq_info *irq_info);
+};
+
+struct hbg_vector {
+ char name[HBG_VECTOR_NUM][32];
+ struct hbg_irq_info *info_array;
+ u32 info_array_len;
+};
+
struct hbg_mac {
struct mii_bus *mdio_bus;
struct phy_device *phydev;
@@ -56,6 +73,7 @@ struct hbg_priv {
struct hbg_dev_specs dev_specs;
unsigned long state;
struct hbg_mac mac;
+ struct hbg_vector vectors;
};
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
index 61769bad284c..f7a7c8524546 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
@@ -70,6 +70,61 @@ static int hbg_hw_dev_specs_init(struct hbg_priv *priv)
return 0;
}
+u32 hbg_hw_get_irq_status(struct hbg_priv *priv)
+{
+ u32 status;
+
+ status = hbg_reg_read(priv, HBG_REG_CF_INTRPT_STAT_ADDR);
+
+ status |= FIELD_PREP(HBG_INT_MSK_TX_B,
+ hbg_reg_read(priv, HBG_REG_CF_IND_TXINT_STAT_ADDR));
+ status |= FIELD_PREP(HBG_INT_MSK_RX_B,
+ hbg_reg_read(priv, HBG_REG_CF_IND_RXINT_STAT_ADDR));
+
+ return status;
+}
+
+void hbg_hw_irq_clear(struct hbg_priv *priv, u32 mask)
+{
+ if (FIELD_GET(HBG_INT_MSK_TX_B, mask))
+ return hbg_reg_write(priv, HBG_REG_CF_IND_TXINT_CLR_ADDR, 0x1);
+
+ if (FIELD_GET(HBG_INT_MSK_RX_B, mask))
+ return hbg_reg_write(priv, HBG_REG_CF_IND_RXINT_CLR_ADDR, 0x1);
+
+ return hbg_reg_write(priv, HBG_REG_CF_INTRPT_CLR_ADDR, mask);
+}
+
+bool hbg_hw_irq_is_enabled(struct hbg_priv *priv, u32 mask)
+{
+ if (FIELD_GET(HBG_INT_MSK_TX_B, mask))
+ return hbg_reg_read(priv, HBG_REG_CF_IND_TXINT_MSK_ADDR);
+
+ if (FIELD_GET(HBG_INT_MSK_RX_B, mask))
+ return hbg_reg_read(priv, HBG_REG_CF_IND_RXINT_MSK_ADDR);
+
+ return hbg_reg_read(priv, HBG_REG_CF_INTRPT_MSK_ADDR) & mask;
+}
+
+void hbg_hw_irq_enable(struct hbg_priv *priv, u32 mask, bool enable)
+{
+ u32 value;
+
+ if (FIELD_GET(HBG_INT_MSK_TX_B, mask))
+ return hbg_reg_write(priv, HBG_REG_CF_IND_TXINT_MSK_ADDR, enable);
+
+ if (FIELD_GET(HBG_INT_MSK_RX_B, mask))
+ return hbg_reg_write(priv, HBG_REG_CF_IND_RXINT_MSK_ADDR, enable);
+
+ value = hbg_reg_read(priv, HBG_REG_CF_INTRPT_MSK_ADDR);
+ if (enable)
+ value |= mask;
+ else
+ value &= ~mask;
+
+ hbg_reg_write(priv, HBG_REG_CF_INTRPT_MSK_ADDR, value);
+}
+
void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex)
{
hbg_reg_write_field(priv, HBG_REG_PORT_MODE_ADDR,
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
index 88fa378db757..09946c3966ff 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
@@ -43,5 +43,9 @@ static inline void hbg_reg_write64(struct hbg_priv *priv, u32 addr, u64 value)
int hbg_hw_init(struct hbg_priv *pri);
void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex);
+u32 hbg_hw_get_irq_status(struct hbg_priv *priv);
+void hbg_hw_irq_clear(struct hbg_priv *priv, u32 mask);
+bool hbg_hw_irq_is_enabled(struct hbg_priv *priv, u32 mask);
+void hbg_hw_irq_enable(struct hbg_priv *priv, u32 mask, bool enable);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
new file mode 100644
index 000000000000..82f40f2eba3f
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (c) 2024 Hisilicon Limited.
+
+#include <linux/interrupt.h>
+#include "hbg_irq.h"
+#include "hbg_hw.h"
+
+static void hbg_irq_handle_err(struct hbg_priv *priv,
+ struct hbg_irq_info *irq_info)
+{
+ if (irq_info->need_print)
+ dev_err(&priv->pdev->dev,
+ "receive error interrupt: %s\n", irq_info->name);
+}
+
+#define HBG_TXRX_IRQ_I(name, handle) \
+ {#name, HBG_INT_MSK_##name##_B, false, false, 0, handle}
+#define HBG_ERR_IRQ_I(name, need_print) \
+ {#name, HBG_INT_MSK_##name##_B, true, need_print, 0, hbg_irq_handle_err}
+
+static struct hbg_irq_info hbg_irqs[] = {
+ HBG_TXRX_IRQ_I(RX, NULL),
+ HBG_TXRX_IRQ_I(TX, NULL),
+ HBG_ERR_IRQ_I(MAC_MII_FIFO_ERR, true),
+ HBG_ERR_IRQ_I(MAC_PCS_RX_FIFO_ERR, true),
+ HBG_ERR_IRQ_I(MAC_PCS_TX_FIFO_ERR, true),
+ HBG_ERR_IRQ_I(MAC_APP_RX_FIFO_ERR, true),
+ HBG_ERR_IRQ_I(MAC_APP_TX_FIFO_ERR, true),
+ HBG_ERR_IRQ_I(SRAM_PARITY_ERR, true),
+ HBG_ERR_IRQ_I(TX_AHB_ERR, true),
+ HBG_ERR_IRQ_I(RX_BUF_AVL, false),
+ HBG_ERR_IRQ_I(REL_BUF_ERR, true),
+ HBG_ERR_IRQ_I(TXCFG_AVL, false),
+ HBG_ERR_IRQ_I(TX_DROP, false),
+ HBG_ERR_IRQ_I(RX_DROP, false),
+ HBG_ERR_IRQ_I(RX_AHB_ERR, true),
+ HBG_ERR_IRQ_I(MAC_FIFO_ERR, false),
+ HBG_ERR_IRQ_I(RBREQ_ERR, false),
+ HBG_ERR_IRQ_I(WE_ERR, false),
+};
+
+static irqreturn_t hbg_irq_handle(int irq_num, void *p)
+{
+ struct hbg_irq_info *info;
+ struct hbg_priv *priv = p;
+ u32 status;
+ u32 i;
+
+ status = hbg_hw_get_irq_status(priv);
+ for (i = 0; i < priv->vectors.info_array_len; i++) {
+ info = &priv->vectors.info_array[i];
+ if (status & info->mask) {
+ if (!hbg_hw_irq_is_enabled(priv, info->mask))
+ continue;
+
+ hbg_hw_irq_enable(priv, info->mask, false);
+ hbg_hw_irq_clear(priv, info->mask);
+
+ info->count++;
+ if (info->irq_handle)
+ info->irq_handle(priv, info);
+
+ if (info->reenable)
+ hbg_hw_irq_enable(priv, info->mask, true);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const char *irq_names_map[HBG_VECTOR_NUM] = { "tx", "rx", "err", "mdio" };
+
+int hbg_irq_init(struct hbg_priv *priv)
+{
+ struct hbg_vector *vectors = &priv->vectors;
+ struct device *dev = &priv->pdev->dev;
+ int ret, id;
+ u32 i;
+
+ ret = pci_alloc_irq_vectors(priv->pdev, HBG_VECTOR_NUM, HBG_VECTOR_NUM,
+ PCI_IRQ_MSI | PCI_IRQ_MSIX);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to allocate MSI vectors\n");
+
+ if (ret != HBG_VECTOR_NUM)
+ return dev_err_probe(dev, -EINVAL,
+ "requested %u MSI, but allocated %d MSI\n",
+ HBG_VECTOR_NUM, ret);
+
+ /* mdio irq not request, so the number of requested interrupts
+ * is HBG_VECTOR_NUM - 1.
+ */
+ for (i = 0; i < HBG_VECTOR_NUM - 1; i++) {
+ id = pci_irq_vector(priv->pdev, i);
+ if (id < 0)
+ return dev_err_probe(dev, id, "failed to get irq number\n");
+
+ snprintf(vectors->name[i], sizeof(vectors->name[i]), "%s-%s-%s",
+ dev_driver_string(dev), pci_name(priv->pdev),
+ irq_names_map[i]);
+
+ ret = devm_request_irq(dev, id, hbg_irq_handle, 0,
+ vectors->name[i], priv);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to requset irq(%d)\n", id);
+ }
+
+ vectors->info_array = hbg_irqs;
+ vectors->info_array_len = ARRAY_SIZE(hbg_irqs);
+ return 0;
+}
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.h
new file mode 100644
index 000000000000..5c5323cfc751
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2024 Hisilicon Limited. */
+
+#ifndef __HBG_IRQ_H
+#define __HBG_IRQ_H
+
+#include "hbg_common.h"
+
+int hbg_irq_init(struct hbg_priv *priv);
+
+#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
index 132711554c1e..fb0e87c8fef7 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
@@ -6,6 +6,7 @@
#include <linux/pci.h>
#include "hbg_common.h"
#include "hbg_hw.h"
+#include "hbg_irq.h"
#include "hbg_mdio.h"
static int hbg_init(struct hbg_priv *priv)
@@ -16,6 +17,10 @@ static int hbg_init(struct hbg_priv *priv)
if (ret)
return ret;
+ ret = hbg_irq_init(priv);
+ if (ret)
+ return ret;
+
return hbg_mdio_init(priv);
}
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
index 81e6d6e9a429..b0991063ccba 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
@@ -51,6 +51,27 @@
#define HBG_REG_RECV_CONTROL_STRIP_PAD_EN_B BIT(3)
/* PCU */
+#define HBG_REG_CF_INTRPT_MSK_ADDR (HBG_REG_SGMII_BASE + 0x042C)
+#define HBG_INT_MSK_WE_ERR_B BIT(31)
+#define HBG_INT_MSK_RBREQ_ERR_B BIT(30)
+#define HBG_INT_MSK_MAC_FIFO_ERR_B BIT(29)
+#define HBG_INT_MSK_RX_AHB_ERR_B BIT(28)
+#define HBG_INT_MSK_RX_DROP_B BIT(26)
+#define HBG_INT_MSK_TX_DROP_B BIT(25)
+#define HBG_INT_MSK_TXCFG_AVL_B BIT(24)
+#define HBG_INT_MSK_REL_BUF_ERR_B BIT(23)
+#define HBG_INT_MSK_RX_BUF_AVL_B BIT(22)
+#define HBG_INT_MSK_TX_AHB_ERR_B BIT(21)
+#define HBG_INT_MSK_SRAM_PARITY_ERR_B BIT(20)
+#define HBG_INT_MSK_MAC_APP_TX_FIFO_ERR_B BIT(19)
+#define HBG_INT_MSK_MAC_APP_RX_FIFO_ERR_B BIT(18)
+#define HBG_INT_MSK_MAC_PCS_TX_FIFO_ERR_B BIT(17)
+#define HBG_INT_MSK_MAC_PCS_RX_FIFO_ERR_B BIT(16)
+#define HBG_INT_MSK_MAC_MII_FIFO_ERR_B BIT(15)
+#define HBG_INT_MSK_TX_B BIT(1) /* just used in driver */
+#define HBG_INT_MSK_RX_B BIT(0) /* just used in driver */
+#define HBG_REG_CF_INTRPT_STAT_ADDR (HBG_REG_SGMII_BASE + 0x0434)
+#define HBG_REG_CF_INTRPT_CLR_ADDR (HBG_REG_SGMII_BASE + 0x0438)
#define HBG_REG_RX_BUF_SIZE_ADDR (HBG_REG_SGMII_BASE + 0x04E4)
#define HBG_REG_RX_BUF_SIZE_M GENMASK(15, 0)
#define HBG_REG_BUS_CTRL_ADDR (HBG_REG_SGMII_BASE + 0x04E8)
@@ -64,6 +85,12 @@
#define HBG_REG_RX_CTRL_RXBUF_1ST_SKIP_SIZE2_M GENMASK(3, 0)
#define HBG_REG_RX_PKT_MODE_ADDR (HBG_REG_SGMII_BASE + 0x04F4)
#define HBG_REG_RX_PKT_MODE_PARSE_MODE_M GENMASK(22, 21)
+#define HBG_REG_CF_IND_TXINT_MSK_ADDR (HBG_REG_SGMII_BASE + 0x0694)
+#define HBG_REG_CF_IND_TXINT_STAT_ADDR (HBG_REG_SGMII_BASE + 0x0698)
+#define HBG_REG_CF_IND_TXINT_CLR_ADDR (HBG_REG_SGMII_BASE + 0x069C)
+#define HBG_REG_CF_IND_RXINT_MSK_ADDR (HBG_REG_SGMII_BASE + 0x06a0)
+#define HBG_REG_CF_IND_RXINT_STAT_ADDR (HBG_REG_SGMII_BASE + 0x06a4)
+#define HBG_REG_CF_IND_RXINT_CLR_ADDR (HBG_REG_SGMII_BASE + 0x06a8)
enum hbg_port_mode {
/* 0x0 ~ 0x5 are reserved */
--
2.33.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH V5 net-next 04/11] net: hibmcge: Add interrupt supported in this module
2024-08-27 13:14 ` [PATCH V5 net-next 04/11] net: hibmcge: Add interrupt " Jijie Shao
@ 2024-08-29 1:35 ` Jakub Kicinski
2024-08-29 1:54 ` Jijie Shao
0 siblings, 1 reply; 28+ messages in thread
From: Jakub Kicinski @ 2024-08-29 1:35 UTC (permalink / raw)
To: Jijie Shao
Cc: davem, edumazet, pabeni, shenjian15, wangpeiyang1, liuyonglong,
chenhao418, sudongming1, xujunsheng, shiyongbang, libaihan,
andrew, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
On Tue, 27 Aug 2024 21:14:48 +0800 Jijie Shao wrote:
> + ret = pci_alloc_irq_vectors(priv->pdev, HBG_VECTOR_NUM, HBG_VECTOR_NUM,
> + PCI_IRQ_MSI | PCI_IRQ_MSIX);
These are not devm_ -managed, don't you have to free them?
On remove and errors during probe?
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH V5 net-next 04/11] net: hibmcge: Add interrupt supported in this module
2024-08-29 1:35 ` Jakub Kicinski
@ 2024-08-29 1:54 ` Jijie Shao
2024-08-29 2:12 ` Jakub Kicinski
0 siblings, 1 reply; 28+ messages in thread
From: Jijie Shao @ 2024-08-29 1:54 UTC (permalink / raw)
To: Jakub Kicinski
Cc: shaojijie, davem, edumazet, pabeni, shenjian15, wangpeiyang1,
liuyonglong, chenhao418, sudongming1, xujunsheng, shiyongbang,
libaihan, andrew, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
on 2024/8/29 9:35, Jakub Kicinski wrote:
> On Tue, 27 Aug 2024 21:14:48 +0800 Jijie Shao wrote:
>> + ret = pci_alloc_irq_vectors(priv->pdev, HBG_VECTOR_NUM, HBG_VECTOR_NUM,
>> + PCI_IRQ_MSI | PCI_IRQ_MSIX);
> These are not devm_ -managed, don't you have to free them?
> On remove and errors during probe?
>
Jonathan Cameron told me:
I have used pcim_enable(), so, the irq vectors become device managed.
Look for where those paths call pci_setup_msi_context() / pcim_setup_msi_release()
So there should be no need to free the vectors on remove()
Thanks,
Jijie Shao
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH V5 net-next 04/11] net: hibmcge: Add interrupt supported in this module
2024-08-29 1:54 ` Jijie Shao
@ 2024-08-29 2:12 ` Jakub Kicinski
2024-08-29 13:02 ` Jijie Shao
0 siblings, 1 reply; 28+ messages in thread
From: Jakub Kicinski @ 2024-08-29 2:12 UTC (permalink / raw)
To: Jijie Shao
Cc: davem, edumazet, pabeni, shenjian15, wangpeiyang1, liuyonglong,
chenhao418, sudongming1, xujunsheng, shiyongbang, libaihan,
andrew, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
On Thu, 29 Aug 2024 09:54:00 +0800 Jijie Shao wrote:
> on 2024/8/29 9:35, Jakub Kicinski wrote:
> > On Tue, 27 Aug 2024 21:14:48 +0800 Jijie Shao wrote:
> >> + ret = pci_alloc_irq_vectors(priv->pdev, HBG_VECTOR_NUM, HBG_VECTOR_NUM,
> >> + PCI_IRQ_MSI | PCI_IRQ_MSIX);
> > These are not devm_ -managed, don't you have to free them?
> > On remove and errors during probe?
> >
> Jonathan Cameron told me:
> I have used pcim_enable(), so, the irq vectors become device managed.
> Look for where those paths call pci_setup_msi_context() / pcim_setup_msi_release()
>
> So there should be no need to free the vectors on remove()
Please include change logs in individual patches, going forward.
Please add this information to the commit message, and remove
the existing mention of freeing there which is now out of date.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH V5 net-next 04/11] net: hibmcge: Add interrupt supported in this module
2024-08-29 2:12 ` Jakub Kicinski
@ 2024-08-29 13:02 ` Jijie Shao
0 siblings, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-29 13:02 UTC (permalink / raw)
To: Jakub Kicinski
Cc: shaojijie, davem, edumazet, pabeni, shenjian15, wangpeiyang1,
liuyonglong, chenhao418, sudongming1, xujunsheng, shiyongbang,
libaihan, andrew, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
on 2024/8/29 10:12, Jakub Kicinski wrote:
> On Thu, 29 Aug 2024 09:54:00 +0800 Jijie Shao wrote:
>> on 2024/8/29 9:35, Jakub Kicinski wrote:
>>> On Tue, 27 Aug 2024 21:14:48 +0800 Jijie Shao wrote:
>>>> + ret = pci_alloc_irq_vectors(priv->pdev, HBG_VECTOR_NUM, HBG_VECTOR_NUM,
>>>> + PCI_IRQ_MSI | PCI_IRQ_MSIX);
>>> These are not devm_ -managed, don't you have to free them?
>>> On remove and errors during probe?
>>>
>> Jonathan Cameron told me:
>> I have used pcim_enable(), so, the irq vectors become device managed.
>> Look for where those paths call pci_setup_msi_context() / pcim_setup_msi_release()
>>
>> So there should be no need to free the vectors on remove()
> Please include change logs in individual patches, going forward.
> Please add this information to the commit message, and remove
> the existing mention of freeing there which is now out of date.
Ok, I'll add a note to that as well.
Thanks,
Jijie Shao
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH V5 net-next 05/11] net: hibmcge: Implement some .ndo functions
2024-08-27 13:14 [PATCH V5 net-next 00/11] Add support of HIBMCGE Ethernet Driver Jijie Shao
` (3 preceding siblings ...)
2024-08-27 13:14 ` [PATCH V5 net-next 04/11] net: hibmcge: Add interrupt " Jijie Shao
@ 2024-08-27 13:14 ` Jijie Shao
2024-08-29 1:39 ` Jakub Kicinski
2024-08-27 13:14 ` [PATCH V5 net-next 06/11] net: hibmcge: Implement .ndo_start_xmit function Jijie Shao
` (5 subsequent siblings)
10 siblings, 1 reply; 28+ messages in thread
From: Jijie Shao @ 2024-08-27 13:14 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: shenjian15, wangpeiyang1, liuyonglong, chenhao418, shaojijie,
sudongming1, xujunsheng, shiyongbang, libaihan, andrew, jdamato,
horms, jonathan.cameron, shameerali.kolothum.thodi, salil.mehta,
netdev, linux-kernel
Implement the .ndo_open .ndo_stop .ndo_set_mac_address
and .ndo_change_mtu functions.
And .ndo_validate_addr calls the eth_validate_addr function directly
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
.../ethernet/hisilicon/hibmcge/hbg_common.h | 3 +
.../net/ethernet/hisilicon/hibmcge/hbg_hw.c | 40 ++++++++
.../net/ethernet/hisilicon/hibmcge/hbg_hw.h | 3 +
.../net/ethernet/hisilicon/hibmcge/hbg_main.c | 99 +++++++++++++++++++
.../net/ethernet/hisilicon/hibmcge/hbg_reg.h | 11 ++-
5 files changed, 155 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
index e94ae2be5c4c..d11ef081f4da 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
@@ -17,8 +17,11 @@
enum hbg_nic_state {
HBG_NIC_STATE_EVENT_HANDLING = 0,
+ HBG_NIC_STATE_OPEN,
};
+#define hbg_nic_is_open(priv) test_bit(HBG_NIC_STATE_OPEN, &(priv)->state)
+
enum hbg_hw_event_type {
HBG_HW_EVENT_NONE = 0,
HBG_HW_EVENT_INIT, /* driver is loading */
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
index f7a7c8524546..04085a68c317 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
@@ -125,6 +125,46 @@ void hbg_hw_irq_enable(struct hbg_priv *priv, u32 mask, bool enable)
hbg_reg_write(priv, HBG_REG_CF_INTRPT_MSK_ADDR, value);
}
+void hbg_hw_set_uc_addr(struct hbg_priv *priv, u64 mac_addr)
+{
+ hbg_reg_write64(priv, HBG_REG_STATION_ADDR_LOW_2_ADDR, mac_addr);
+}
+
+static void hbg_hw_set_pcu_max_frame_len(struct hbg_priv *priv,
+ u16 max_frame_len)
+{
+#define HBG_PCU_FRAME_LEN_PLUS 4
+
+ max_frame_len = max_t(u32, max_frame_len, HBG_DEFAULT_MTU_SIZE);
+
+ /* lower two bits of value must be set to 0. Otherwise, the value is ignored */
+ max_frame_len = round_up(max_frame_len, HBG_PCU_FRAME_LEN_PLUS);
+
+ hbg_reg_write_field(priv, HBG_REG_MAX_FRAME_LEN_ADDR,
+ HBG_REG_MAX_FRAME_LEN_M, max_frame_len);
+}
+
+static void hbg_hw_set_mac_max_frame_len(struct hbg_priv *priv,
+ u16 max_frame_size)
+{
+ hbg_reg_write_field(priv, HBG_REG_MAX_FRAME_SIZE_ADDR,
+ HBG_REG_MAX_FRAME_LEN_M, max_frame_size);
+}
+
+void hbg_hw_set_mtu(struct hbg_priv *priv, u16 mtu)
+{
+ hbg_hw_set_pcu_max_frame_len(priv, mtu);
+ hbg_hw_set_mac_max_frame_len(priv, mtu);
+}
+
+void hbg_hw_mac_enable(struct hbg_priv *priv, u32 enable)
+{
+ hbg_reg_write_field(priv, HBG_REG_PORT_ENABLE_ADDR,
+ HBG_REG_PORT_ENABLE_TX_B, enable);
+ hbg_reg_write_field(priv, HBG_REG_PORT_ENABLE_ADDR,
+ HBG_REG_PORT_ENABLE_RX_B, enable);
+}
+
void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex)
{
hbg_reg_write_field(priv, HBG_REG_PORT_MODE_ADDR,
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
index 09946c3966ff..ed72e1192b79 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
@@ -47,5 +47,8 @@ u32 hbg_hw_get_irq_status(struct hbg_priv *priv);
void hbg_hw_irq_clear(struct hbg_priv *priv, u32 mask);
bool hbg_hw_irq_is_enabled(struct hbg_priv *priv, u32 mask);
void hbg_hw_irq_enable(struct hbg_priv *priv, u32 mask, bool enable);
+void hbg_hw_set_mtu(struct hbg_priv *priv, u16 mtu);
+void hbg_hw_mac_enable(struct hbg_priv *priv, u32 enable);
+void hbg_hw_set_uc_addr(struct hbg_priv *priv, u64 mac_addr);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
index fb0e87c8fef7..b7248c830434 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
@@ -2,6 +2,7 @@
// Copyright (c) 2024 Hisilicon Limited.
#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include "hbg_common.h"
@@ -9,6 +10,99 @@
#include "hbg_irq.h"
#include "hbg_mdio.h"
+static void hbg_all_irq_enable(struct hbg_priv *priv, bool enabled)
+{
+ struct hbg_irq_info *info;
+ u32 i;
+
+ for (i = 0; i < priv->vectors.info_array_len; i++) {
+ info = &priv->vectors.info_array[i];
+ hbg_hw_irq_enable(priv, info->mask, enabled);
+ }
+}
+
+static int hbg_net_open(struct net_device *dev)
+{
+ struct hbg_priv *priv = netdev_priv(dev);
+
+ if (test_and_set_bit(HBG_NIC_STATE_OPEN, &priv->state))
+ return 0;
+
+ netif_carrier_off(dev);
+ hbg_all_irq_enable(priv, true);
+ hbg_hw_mac_enable(priv, HBG_STATUS_ENABLE);
+ netif_start_queue(dev);
+ hbg_phy_start(priv);
+
+ return 0;
+}
+
+static int hbg_net_stop(struct net_device *dev)
+{
+ struct hbg_priv *priv = netdev_priv(dev);
+
+ if (!hbg_nic_is_open(priv))
+ return 0;
+
+ clear_bit(HBG_NIC_STATE_OPEN, &priv->state);
+ netif_carrier_off(dev);
+ hbg_phy_stop(priv);
+ netif_stop_queue(dev);
+ hbg_hw_mac_enable(priv, HBG_STATUS_DISABLE);
+ hbg_all_irq_enable(priv, false);
+
+ return 0;
+}
+
+static int hbg_net_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct hbg_priv *priv = netdev_priv(dev);
+ u8 *mac_addr;
+
+ mac_addr = ((struct sockaddr *)addr)->sa_data;
+ if (!is_valid_ether_addr(mac_addr))
+ return -EADDRNOTAVAIL;
+
+ hbg_hw_set_uc_addr(priv, ether_addr_to_u64(mac_addr));
+ dev_addr_set(dev, mac_addr);
+
+ return 0;
+}
+
+static void hbg_change_mtu(struct hbg_priv *priv, int new_mtu)
+{
+ u32 frame_len;
+
+ frame_len = new_mtu + VLAN_HLEN * priv->dev_specs.vlan_layers +
+ ETH_HLEN + ETH_FCS_LEN;
+ hbg_hw_set_mtu(priv, frame_len);
+}
+
+static int hbg_net_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct hbg_priv *priv = netdev_priv(dev);
+ bool is_opened = hbg_nic_is_open(priv);
+
+ hbg_net_stop(dev);
+
+ hbg_change_mtu(priv, new_mtu);
+ WRITE_ONCE(dev->mtu, new_mtu);
+
+ dev_dbg(&priv->pdev->dev,
+ "change mtu from %u to %u\n", dev->mtu, new_mtu);
+ if (is_opened)
+ hbg_net_open(dev);
+ return 0;
+}
+
+static const struct net_device_ops hbg_netdev_ops = {
+ .ndo_open = hbg_net_open,
+ .ndo_stop = hbg_net_stop,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = hbg_net_set_mac_address,
+ .ndo_change_mtu = hbg_net_change_mtu,
+};
+
static int hbg_init(struct hbg_priv *priv)
{
int ret;
@@ -69,6 +163,7 @@ static int hbg_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
priv = netdev_priv(netdev);
priv->netdev = netdev;
priv->pdev = pdev;
+ netdev->netdev_ops = &hbg_netdev_ops;
ret = hbg_pci_init(pdev);
if (ret)
@@ -78,6 +173,10 @@ static int hbg_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
return ret;
+ netdev->max_mtu = priv->dev_specs.max_mtu;
+ netdev->min_mtu = priv->dev_specs.min_mtu;
+ hbg_change_mtu(priv, HBG_DEFAULT_MTU_SIZE);
+ hbg_net_set_mac_address(priv->netdev, &priv->dev_specs.mac_addr);
ret = devm_register_netdev(dev, netdev);
if (ret)
return dev_err_probe(dev, ret, "failed to register netdev\n");
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
index b0991063ccba..63bb1bead8c0 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
@@ -37,18 +37,24 @@
#define HBG_REG_SGMII_BASE 0x10000
#define HBG_REG_DUPLEX_TYPE_ADDR (HBG_REG_SGMII_BASE + 0x0008)
#define HBG_REG_DUPLEX_B BIT(0)
+#define HBG_REG_MAX_FRAME_SIZE_ADDR (HBG_REG_SGMII_BASE + 0x003C)
#define HBG_REG_PORT_MODE_ADDR (HBG_REG_SGMII_BASE + 0x0040)
#define HBG_REG_PORT_MODE_M GENMASK(3, 0)
+#define HBG_REG_PORT_ENABLE_ADDR (HBG_REG_SGMII_BASE + 0x0044)
+#define HBG_REG_PORT_ENABLE_RX_B BIT(1)
+#define HBG_REG_PORT_ENABLE_TX_B BIT(2)
#define HBG_REG_TRANSMIT_CONTROL_ADDR (HBG_REG_SGMII_BASE + 0x0060)
#define HBG_REG_TRANSMIT_CONTROL_PAD_EN_B BIT(7)
#define HBG_REG_TRANSMIT_CONTROL_CRC_ADD_B BIT(6)
#define HBG_REG_TRANSMIT_CONTROL_AN_EN_B BIT(5)
#define HBG_REG_CF_CRC_STRIP_ADDR (HBG_REG_SGMII_BASE + 0x01B0)
-#define HBG_REG_CF_CRC_STRIP_B BIT(0)
+#define HBG_REG_CF_CRC_STRIP_B BIT(1)
#define HBG_REG_MODE_CHANGE_EN_ADDR (HBG_REG_SGMII_BASE + 0x01B4)
#define HBG_REG_MODE_CHANGE_EN_B BIT(0)
#define HBG_REG_RECV_CONTROL_ADDR (HBG_REG_SGMII_BASE + 0x01E0)
#define HBG_REG_RECV_CONTROL_STRIP_PAD_EN_B BIT(3)
+#define HBG_REG_STATION_ADDR_LOW_2_ADDR (HBG_REG_SGMII_BASE + 0x0210)
+#define HBG_REG_STATION_ADDR_HIGH_2_ADDR (HBG_REG_SGMII_BASE + 0x0214)
/* PCU */
#define HBG_REG_CF_INTRPT_MSK_ADDR (HBG_REG_SGMII_BASE + 0x042C)
@@ -72,6 +78,8 @@
#define HBG_INT_MSK_RX_B BIT(0) /* just used in driver */
#define HBG_REG_CF_INTRPT_STAT_ADDR (HBG_REG_SGMII_BASE + 0x0434)
#define HBG_REG_CF_INTRPT_CLR_ADDR (HBG_REG_SGMII_BASE + 0x0438)
+#define HBG_REG_MAX_FRAME_LEN_ADDR (HBG_REG_SGMII_BASE + 0x0444)
+#define HBG_REG_MAX_FRAME_LEN_M GENMASK(15, 0)
#define HBG_REG_RX_BUF_SIZE_ADDR (HBG_REG_SGMII_BASE + 0x04E4)
#define HBG_REG_RX_BUF_SIZE_M GENMASK(15, 0)
#define HBG_REG_BUS_CTRL_ADDR (HBG_REG_SGMII_BASE + 0x04E8)
@@ -86,6 +94,7 @@
#define HBG_REG_RX_PKT_MODE_ADDR (HBG_REG_SGMII_BASE + 0x04F4)
#define HBG_REG_RX_PKT_MODE_PARSE_MODE_M GENMASK(22, 21)
#define HBG_REG_CF_IND_TXINT_MSK_ADDR (HBG_REG_SGMII_BASE + 0x0694)
+#define HBG_REG_IND_INTR_MASK_B BIT(0)
#define HBG_REG_CF_IND_TXINT_STAT_ADDR (HBG_REG_SGMII_BASE + 0x0698)
#define HBG_REG_CF_IND_TXINT_CLR_ADDR (HBG_REG_SGMII_BASE + 0x069C)
#define HBG_REG_CF_IND_RXINT_MSK_ADDR (HBG_REG_SGMII_BASE + 0x06a0)
--
2.33.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH V5 net-next 05/11] net: hibmcge: Implement some .ndo functions
2024-08-27 13:14 ` [PATCH V5 net-next 05/11] net: hibmcge: Implement some .ndo functions Jijie Shao
@ 2024-08-29 1:39 ` Jakub Kicinski
2024-08-29 2:40 ` Jijie Shao
0 siblings, 1 reply; 28+ messages in thread
From: Jakub Kicinski @ 2024-08-29 1:39 UTC (permalink / raw)
To: Jijie Shao
Cc: davem, edumazet, pabeni, shenjian15, wangpeiyang1, liuyonglong,
chenhao418, sudongming1, xujunsheng, shiyongbang, libaihan,
andrew, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
On Tue, 27 Aug 2024 21:14:49 +0800 Jijie Shao wrote:
> +static int hbg_net_open(struct net_device *dev)
> +{
> + struct hbg_priv *priv = netdev_priv(dev);
> +
> + if (test_and_set_bit(HBG_NIC_STATE_OPEN, &priv->state))
> + return 0;
> +
> + netif_carrier_off(dev);
Why clear the carrier during open? You should probably clear it once on
the probe path and then on stop.
> + hbg_all_irq_enable(priv, true);
> + hbg_hw_mac_enable(priv, HBG_STATUS_ENABLE);
> + netif_start_queue(dev);
> + hbg_phy_start(priv);
> +
> + return 0;
> +}
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH V5 net-next 05/11] net: hibmcge: Implement some .ndo functions
2024-08-29 1:39 ` Jakub Kicinski
@ 2024-08-29 2:40 ` Jijie Shao
2024-08-29 14:43 ` Jakub Kicinski
0 siblings, 1 reply; 28+ messages in thread
From: Jijie Shao @ 2024-08-29 2:40 UTC (permalink / raw)
To: Jakub Kicinski
Cc: shaojijie, davem, edumazet, pabeni, shenjian15, wangpeiyang1,
liuyonglong, chenhao418, sudongming1, xujunsheng, shiyongbang,
libaihan, andrew, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
on 2024/8/29 9:39, Jakub Kicinski wrote:
> On Tue, 27 Aug 2024 21:14:49 +0800 Jijie Shao wrote:
>> +static int hbg_net_open(struct net_device *dev)
>> +{
>> + struct hbg_priv *priv = netdev_priv(dev);
>> +
>> + if (test_and_set_bit(HBG_NIC_STATE_OPEN, &priv->state))
>> + return 0;
>> +
>> + netif_carrier_off(dev);
> Why clear the carrier during open? You should probably clear it once on
> the probe path and then on stop.
In net_open(), the GMAC is not ready to receive or transmit packets.
Therefore, netif_carrier_off() is called.
Packets can be received or transmitted only after the PHY is linked.
Therefore, netif_carrier_on() should be called in adjust_link.
In net_stop() we also call netif_carrier_off()
Thanks,
Jijie Shao
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH V5 net-next 05/11] net: hibmcge: Implement some .ndo functions
2024-08-29 2:40 ` Jijie Shao
@ 2024-08-29 14:43 ` Jakub Kicinski
2024-08-29 14:59 ` Andrew Lunn
0 siblings, 1 reply; 28+ messages in thread
From: Jakub Kicinski @ 2024-08-29 14:43 UTC (permalink / raw)
To: Jijie Shao
Cc: davem, edumazet, pabeni, shenjian15, wangpeiyang1, liuyonglong,
chenhao418, sudongming1, xujunsheng, shiyongbang, libaihan,
andrew, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
On Thu, 29 Aug 2024 10:40:07 +0800 Jijie Shao wrote:
> on 2024/8/29 9:39, Jakub Kicinski wrote:
> > On Tue, 27 Aug 2024 21:14:49 +0800 Jijie Shao wrote:
> >> +static int hbg_net_open(struct net_device *dev)
> >> +{
> >> + struct hbg_priv *priv = netdev_priv(dev);
> >> +
> >> + if (test_and_set_bit(HBG_NIC_STATE_OPEN, &priv->state))
> >> + return 0;
> >> +
> >> + netif_carrier_off(dev);
> > Why clear the carrier during open? You should probably clear it once on
> > the probe path and then on stop.
>
> In net_open(), the GMAC is not ready to receive or transmit packets.
> Therefore, netif_carrier_off() is called.
>
> Packets can be received or transmitted only after the PHY is linked.
> Therefore, netif_carrier_on() should be called in adjust_link.
But why are you calling _off() during .ndo_open() ?
Surely the link is also off before ndo_open is called?
> In net_stop() we also call netif_carrier_off()
Exactly, so it should already be off.
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH V5 net-next 05/11] net: hibmcge: Implement some .ndo functions
2024-08-29 14:43 ` Jakub Kicinski
@ 2024-08-29 14:59 ` Andrew Lunn
2024-08-30 2:27 ` Jijie Shao
0 siblings, 1 reply; 28+ messages in thread
From: Andrew Lunn @ 2024-08-29 14:59 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Jijie Shao, davem, edumazet, pabeni, shenjian15, wangpeiyang1,
liuyonglong, chenhao418, sudongming1, xujunsheng, shiyongbang,
libaihan, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
On Thu, Aug 29, 2024 at 07:43:39AM -0700, Jakub Kicinski wrote:
> On Thu, 29 Aug 2024 10:40:07 +0800 Jijie Shao wrote:
> > on 2024/8/29 9:39, Jakub Kicinski wrote:
> > > On Tue, 27 Aug 2024 21:14:49 +0800 Jijie Shao wrote:
> > >> +static int hbg_net_open(struct net_device *dev)
> > >> +{
> > >> + struct hbg_priv *priv = netdev_priv(dev);
> > >> +
> > >> + if (test_and_set_bit(HBG_NIC_STATE_OPEN, &priv->state))
> > >> + return 0;
> > >> +
> > >> + netif_carrier_off(dev);
> > > Why clear the carrier during open? You should probably clear it once on
> > > the probe path and then on stop.
> >
> > In net_open(), the GMAC is not ready to receive or transmit packets.
> > Therefore, netif_carrier_off() is called.
> >
> > Packets can be received or transmitted only after the PHY is linked.
> > Therefore, netif_carrier_on() should be called in adjust_link.
>
> But why are you calling _off() during .ndo_open() ?
> Surely the link is also off before ndo_open is called?
I wounder what driver they copied?
The general trend is .probe() calls netif_carrier_off(). After than,
phylib/phylink is in control of the carrier and the MAC driver does
not touch it. in fact, when using phylink, if you try to change the
carrier, you will get SHOUTED at from Russell.
Andrew
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH V5 net-next 05/11] net: hibmcge: Implement some .ndo functions
2024-08-29 14:59 ` Andrew Lunn
@ 2024-08-30 2:27 ` Jijie Shao
0 siblings, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-30 2:27 UTC (permalink / raw)
To: Andrew Lunn, Jakub Kicinski
Cc: shaojijie, davem, edumazet, pabeni, shenjian15, wangpeiyang1,
liuyonglong, chenhao418, sudongming1, xujunsheng, shiyongbang,
libaihan, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
on 2024/8/29 22:59, Andrew Lunn wrote:
> On Thu, Aug 29, 2024 at 07:43:39AM -0700, Jakub Kicinski wrote:
>> On Thu, 29 Aug 2024 10:40:07 +0800 Jijie Shao wrote:
>>> on 2024/8/29 9:39, Jakub Kicinski wrote:
>>>> On Tue, 27 Aug 2024 21:14:49 +0800 Jijie Shao wrote:
>>>>> +static int hbg_net_open(struct net_device *dev)
>>>>> +{
>>>>> + struct hbg_priv *priv = netdev_priv(dev);
>>>>> +
>>>>> + if (test_and_set_bit(HBG_NIC_STATE_OPEN, &priv->state))
>>>>> + return 0;
>>>>> +
>>>>> + netif_carrier_off(dev);
>>>> Why clear the carrier during open? You should probably clear it once on
>>>> the probe path and then on stop.
>>> In net_open(), the GMAC is not ready to receive or transmit packets.
>>> Therefore, netif_carrier_off() is called.
>>>
>>> Packets can be received or transmitted only after the PHY is linked.
>>> Therefore, netif_carrier_on() should be called in adjust_link.
>> But why are you calling _off() during .ndo_open() ?
>> Surely the link is also off before ndo_open is called?
> I wounder what driver they copied?
>
> The general trend is .probe() calls netif_carrier_off(). After than,
> phylib/phylink is in control of the carrier and the MAC driver does
> not touch it. in fact, when using phylink, if you try to change the
> carrier, you will get SHOUTED at from Russell.
>
> Andrew
Read the PHY driver code:
netif_carrier_on() or netif_carrier_off()
has been called in phy_link_change() based on the link status.
Therefore, the driver does not need to process it.
static void phy_link_change(struct phy_device *phydev, bool up)
{
struct net_device *netdev = phydev->attached_dev;
if (up)
netif_carrier_on(netdev);
else
netif_carrier_off(netdev);
phydev->adjust_link(netdev);
if (phydev->mii_ts && phydev->mii_ts->link_state)
phydev->mii_ts->link_state(phydev->mii_ts, phydev);
}
Thank you. I'll delete it in v6.
Jijie Shao
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH V5 net-next 06/11] net: hibmcge: Implement .ndo_start_xmit function
2024-08-27 13:14 [PATCH V5 net-next 00/11] Add support of HIBMCGE Ethernet Driver Jijie Shao
` (4 preceding siblings ...)
2024-08-27 13:14 ` [PATCH V5 net-next 05/11] net: hibmcge: Implement some .ndo functions Jijie Shao
@ 2024-08-27 13:14 ` Jijie Shao
2024-08-29 1:41 ` Jakub Kicinski
2024-08-27 13:14 ` [PATCH V5 net-next 07/11] net: hibmcge: Implement rx_poll function to receive packets Jijie Shao
` (4 subsequent siblings)
10 siblings, 1 reply; 28+ messages in thread
From: Jijie Shao @ 2024-08-27 13:14 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: shenjian15, wangpeiyang1, liuyonglong, chenhao418, shaojijie,
sudongming1, xujunsheng, shiyongbang, libaihan, andrew, jdamato,
horms, jonathan.cameron, shameerali.kolothum.thodi, salil.mehta,
netdev, linux-kernel
Implement .ndo_start_xmit function to fill the information of the packet
to be transmitted into the tx descriptor, and then the hardware will
transmit the packet using the information in the tx descriptor.
In addition, we also implemented the tx_handler function to enable the
tx descriptor to be reused, and .ndo_tx_timeout function to print some
information when the hardware is busy.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
.../ethernet/hisilicon/hibmcge/hbg_common.h | 49 ++++
.../net/ethernet/hisilicon/hibmcge/hbg_hw.c | 18 ++
.../net/ethernet/hisilicon/hibmcge/hbg_hw.h | 2 +
.../net/ethernet/hisilicon/hibmcge/hbg_irq.c | 8 +-
.../net/ethernet/hisilicon/hibmcge/hbg_main.c | 33 +++
.../net/ethernet/hisilicon/hibmcge/hbg_reg.h | 19 ++
.../net/ethernet/hisilicon/hibmcge/hbg_txrx.c | 262 ++++++++++++++++++
.../net/ethernet/hisilicon/hibmcge/hbg_txrx.h | 37 +++
8 files changed, 427 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.h
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
index d11ef081f4da..41de51303d73 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
@@ -14,6 +14,19 @@
#define HBG_RX_SKIP1 0x00
#define HBG_RX_SKIP2 0x01
#define HBG_VECTOR_NUM 4
+#define HBG_PCU_CACHE_LINE_SIZE 32
+#define HBG_TX_TIMEOUT_BUF_LEN 1024
+
+enum hbg_dir {
+ HBG_DIR_TX = 1 << 0,
+ HBG_DIR_RX = 1 << 1,
+ HBG_DIR_TX_RX = HBG_DIR_TX | HBG_DIR_RX,
+};
+
+enum hbg_tx_state {
+ HBG_TX_STATE_COMPLETE = 0, /* clear state, must fix to 0 */
+ HBG_TX_STATE_START,
+};
enum hbg_nic_state {
HBG_NIC_STATE_EVENT_HANDLING = 0,
@@ -22,6 +35,41 @@ enum hbg_nic_state {
#define hbg_nic_is_open(priv) test_bit(HBG_NIC_STATE_OPEN, &(priv)->state)
+struct hbg_priv;
+struct hbg_ring;
+struct hbg_buffer {
+ u32 state;
+ dma_addr_t state_dma;
+
+ struct sk_buff *skb;
+ dma_addr_t skb_dma;
+ u32 skb_len;
+
+ enum hbg_dir dir;
+ struct hbg_ring *ring;
+ struct hbg_priv *priv;
+};
+
+struct hbg_ring {
+ struct hbg_buffer *queue;
+ dma_addr_t queue_dma;
+
+ union {
+ u32 head;
+ u32 ntc;
+ };
+ union {
+ u32 tail;
+ u32 ntu;
+ };
+ u32 len;
+
+ enum hbg_dir dir;
+ struct hbg_priv *priv;
+ struct napi_struct napi;
+ char *tout_log_buf; /* tx timeout log buffer */
+};
+
enum hbg_hw_event_type {
HBG_HW_EVENT_NONE = 0,
HBG_HW_EVENT_INIT, /* driver is loading */
@@ -77,6 +125,7 @@ struct hbg_priv {
unsigned long state;
struct hbg_mac mac;
struct hbg_vector vectors;
+ struct hbg_ring tx_ring;
};
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
index 04085a68c317..4f60bfdc3249 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
@@ -67,6 +67,7 @@ static int hbg_hw_dev_specs_init(struct hbg_priv *priv)
if (!is_valid_ether_addr((u8 *)dev_specs->mac_addr.sa_data))
return -EADDRNOTAVAIL;
+ dev_specs->max_frame_len = HBG_PCU_CACHE_LINE_SIZE + dev_specs->max_mtu;
return 0;
}
@@ -165,6 +166,23 @@ void hbg_hw_mac_enable(struct hbg_priv *priv, u32 enable)
HBG_REG_PORT_ENABLE_RX_B, enable);
}
+u32 hbg_hw_get_fifo_used_num(struct hbg_priv *priv, enum hbg_dir dir)
+{
+ if (dir & HBG_DIR_TX)
+ return hbg_reg_read_field(priv, HBG_REG_CF_CFF_DATA_NUM_ADDR,
+ HBG_REG_CF_CFF_DATA_NUM_ADDR_TX_M);
+
+ return 0;
+}
+
+void hbg_hw_set_tx_desc(struct hbg_priv *priv, struct hbg_tx_desc *tx_desc)
+{
+ hbg_reg_write(priv, HBG_REG_TX_CFF_ADDR_0_ADDR, tx_desc->word0);
+ hbg_reg_write(priv, HBG_REG_TX_CFF_ADDR_1_ADDR, tx_desc->word1);
+ hbg_reg_write(priv, HBG_REG_TX_CFF_ADDR_2_ADDR, tx_desc->word2);
+ hbg_reg_write(priv, HBG_REG_TX_CFF_ADDR_3_ADDR, tx_desc->word3);
+}
+
void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex)
{
hbg_reg_write_field(priv, HBG_REG_PORT_MODE_ADDR,
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
index ed72e1192b79..15bf16a50f14 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
@@ -50,5 +50,7 @@ void hbg_hw_irq_enable(struct hbg_priv *priv, u32 mask, bool enable);
void hbg_hw_set_mtu(struct hbg_priv *priv, u16 mtu);
void hbg_hw_mac_enable(struct hbg_priv *priv, u32 enable);
void hbg_hw_set_uc_addr(struct hbg_priv *priv, u64 mac_addr);
+u32 hbg_hw_get_fifo_used_num(struct hbg_priv *priv, enum hbg_dir dir);
+void hbg_hw_set_tx_desc(struct hbg_priv *priv, struct hbg_tx_desc *tx_desc);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
index 82f40f2eba3f..b2a55967ffad 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
@@ -13,6 +13,12 @@ static void hbg_irq_handle_err(struct hbg_priv *priv,
"receive error interrupt: %s\n", irq_info->name);
}
+static void hbg_irq_handle_tx(struct hbg_priv *priv,
+ struct hbg_irq_info *irq_info)
+{
+ napi_schedule(&priv->tx_ring.napi);
+}
+
#define HBG_TXRX_IRQ_I(name, handle) \
{#name, HBG_INT_MSK_##name##_B, false, false, 0, handle}
#define HBG_ERR_IRQ_I(name, need_print) \
@@ -20,7 +26,7 @@ static void hbg_irq_handle_err(struct hbg_priv *priv,
static struct hbg_irq_info hbg_irqs[] = {
HBG_TXRX_IRQ_I(RX, NULL),
- HBG_TXRX_IRQ_I(TX, NULL),
+ HBG_TXRX_IRQ_I(TX, hbg_irq_handle_tx),
HBG_ERR_IRQ_I(MAC_MII_FIFO_ERR, true),
HBG_ERR_IRQ_I(MAC_PCS_RX_FIFO_ERR, true),
HBG_ERR_IRQ_I(MAC_PCS_TX_FIFO_ERR, true),
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
index b7248c830434..73aa21e9cec9 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
@@ -9,6 +9,7 @@
#include "hbg_hw.h"
#include "hbg_irq.h"
#include "hbg_mdio.h"
+#include "hbg_txrx.h"
static void hbg_all_irq_enable(struct hbg_priv *priv, bool enabled)
{
@@ -29,6 +30,7 @@ static int hbg_net_open(struct net_device *dev)
return 0;
netif_carrier_off(dev);
+ napi_enable(&priv->tx_ring.napi);
hbg_all_irq_enable(priv, true);
hbg_hw_mac_enable(priv, HBG_STATUS_ENABLE);
netif_start_queue(dev);
@@ -50,6 +52,7 @@ static int hbg_net_stop(struct net_device *dev)
netif_stop_queue(dev);
hbg_hw_mac_enable(priv, HBG_STATUS_DISABLE);
hbg_all_irq_enable(priv, false);
+ napi_disable(&priv->tx_ring.napi);
return 0;
}
@@ -95,12 +98,33 @@ static int hbg_net_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+static void hbg_net_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+ struct hbg_priv *priv = netdev_priv(dev);
+ struct hbg_ring *ring = &priv->tx_ring;
+ char *buf = ring->tout_log_buf;
+ u32 pos = 0;
+
+ pos += scnprintf(buf + pos, HBG_TX_TIMEOUT_BUF_LEN - pos,
+ "ring used num: %u, fifo used num: %u\n",
+ hbg_get_queue_used_num(ring),
+ hbg_hw_get_fifo_used_num(priv, HBG_DIR_TX));
+ pos += scnprintf(buf + pos, HBG_TX_TIMEOUT_BUF_LEN - pos,
+ "ntc: %u, ntu: %u, irq enabled: %u\n",
+ ring->ntc, ring->ntu,
+ hbg_hw_irq_is_enabled(priv, HBG_INT_MSK_TX_B));
+
+ netdev_info(dev, "%s", buf);
+}
+
static const struct net_device_ops hbg_netdev_ops = {
.ndo_open = hbg_net_open,
.ndo_stop = hbg_net_stop,
+ .ndo_start_xmit = hbg_net_start_xmit,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = hbg_net_set_mac_address,
.ndo_change_mtu = hbg_net_change_mtu,
+ .ndo_tx_timeout = hbg_net_tx_timeout,
};
static int hbg_init(struct hbg_priv *priv)
@@ -111,6 +135,14 @@ static int hbg_init(struct hbg_priv *priv)
if (ret)
return ret;
+ ret = hbg_txrx_init(priv);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&priv->pdev->dev, hbg_txrx_uninit, priv);
+ if (ret)
+ return ret;
+
ret = hbg_irq_init(priv);
if (ret)
return ret;
@@ -173,6 +205,7 @@ static int hbg_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
return ret;
+ netdev->watchdog_timeo = 5 * HZ;
netdev->max_mtu = priv->dev_specs.max_mtu;
netdev->min_mtu = priv->dev_specs.min_mtu;
hbg_change_mtu(priv, HBG_DEFAULT_MTU_SIZE);
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
index 63bb1bead8c0..0abfcd84e56b 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
@@ -80,6 +80,12 @@
#define HBG_REG_CF_INTRPT_CLR_ADDR (HBG_REG_SGMII_BASE + 0x0438)
#define HBG_REG_MAX_FRAME_LEN_ADDR (HBG_REG_SGMII_BASE + 0x0444)
#define HBG_REG_MAX_FRAME_LEN_M GENMASK(15, 0)
+#define HBG_REG_CF_CFF_DATA_NUM_ADDR (HBG_REG_SGMII_BASE + 0x045C)
+#define HBG_REG_CF_CFF_DATA_NUM_ADDR_TX_M GENMASK(8, 0)
+#define HBG_REG_TX_CFF_ADDR_0_ADDR (HBG_REG_SGMII_BASE + 0x0488)
+#define HBG_REG_TX_CFF_ADDR_1_ADDR (HBG_REG_SGMII_BASE + 0x048C)
+#define HBG_REG_TX_CFF_ADDR_2_ADDR (HBG_REG_SGMII_BASE + 0x0490)
+#define HBG_REG_TX_CFF_ADDR_3_ADDR (HBG_REG_SGMII_BASE + 0x0494)
#define HBG_REG_RX_BUF_SIZE_ADDR (HBG_REG_SGMII_BASE + 0x04E4)
#define HBG_REG_RX_BUF_SIZE_M GENMASK(15, 0)
#define HBG_REG_BUS_CTRL_ADDR (HBG_REG_SGMII_BASE + 0x04E8)
@@ -108,4 +114,17 @@ enum hbg_port_mode {
HBG_PORT_MODE_SGMII_1000M = 0x8,
};
+struct hbg_tx_desc {
+ u32 word0;
+ u32 word1;
+ u32 word2; /* pkt_addr */
+ u32 word3; /* clear_addr */
+};
+
+#define HBG_TX_DESC_W0_IP_OFF_M GENMASK(30, 26)
+#define HBG_TX_DESC_W0_l3_CS_B BIT(2)
+#define HBG_TX_DESC_W0_WB_B BIT(1)
+#define HBG_TX_DESC_W0_l4_CS_B BIT(0)
+#define HBG_TX_DESC_W1_SEND_LEN_M GENMASK(19, 4)
+
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
new file mode 100644
index 000000000000..9c48be9cd469
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (c) 2024 Hisilicon Limited.
+
+#include "hbg_common.h"
+#include "hbg_irq.h"
+#include "hbg_reg.h"
+#include "hbg_txrx.h"
+
+#define netdev_get_tx_ring(netdev) (&(((struct hbg_priv *)netdev_priv(netdev))->tx_ring))
+
+#define buffer_to_dma_dir(buffer) (((buffer)->dir == HBG_DIR_RX) ? \
+ DMA_FROM_DEVICE : DMA_TO_DEVICE)
+
+#define hbg_queue_is_full(head, tail, ring) ((head) == ((tail) + 1) % (ring)->len)
+#define hbg_queue_is_empty(head, tail) ((head) == (tail))
+#define hbg_queue_next_prt(p, ring) (((p) + 1) % (ring)->len)
+
+static int hbg_dma_map(struct hbg_buffer *buffer)
+{
+ struct hbg_priv *priv = buffer->priv;
+
+ buffer->skb_dma = dma_map_single(&priv->pdev->dev,
+ buffer->skb->data, buffer->skb_len,
+ buffer_to_dma_dir(buffer));
+ if (unlikely(dma_mapping_error(&priv->pdev->dev, buffer->skb_dma)))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void hbg_dma_unmap(struct hbg_buffer *buffer)
+{
+ struct hbg_priv *priv = buffer->priv;
+
+ if (unlikely(!buffer->skb_dma))
+ return;
+
+ dma_unmap_single(&priv->pdev->dev, buffer->skb_dma, buffer->skb_len,
+ buffer_to_dma_dir(buffer));
+ buffer->skb_dma = 0;
+}
+
+static void hbg_init_tx_desc(struct hbg_buffer *buffer,
+ struct hbg_tx_desc *tx_desc)
+{
+ u32 ip_offset = buffer->skb->network_header - buffer->skb->mac_header;
+ u32 word0 = 0;
+
+ word0 |= FIELD_PREP(HBG_TX_DESC_W0_WB_B, HBG_STATUS_ENABLE);
+ word0 |= FIELD_PREP(HBG_TX_DESC_W0_IP_OFF_M, ip_offset);
+ if (likely(buffer->skb->ip_summed == CHECKSUM_PARTIAL)) {
+ word0 |= FIELD_PREP(HBG_TX_DESC_W0_l3_CS_B, HBG_STATUS_ENABLE);
+ word0 |= FIELD_PREP(HBG_TX_DESC_W0_l4_CS_B, HBG_STATUS_ENABLE);
+ }
+
+ tx_desc->word0 = word0;
+ tx_desc->word1 = FIELD_PREP(HBG_TX_DESC_W1_SEND_LEN_M, buffer->skb->len);
+ tx_desc->word2 = buffer->skb_dma;
+ tx_desc->word3 = buffer->state_dma;
+}
+
+netdev_tx_t hbg_net_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
+{
+ struct hbg_ring *ring = netdev_get_tx_ring(net_dev);
+ struct hbg_priv *priv = netdev_priv(net_dev);
+ /* This smp_load_acquire() pairs with smp_store_release() in
+ * hbg_tx_buffer_recycle() called in tx interrupt handle process.
+ */
+ u32 ntc = smp_load_acquire(&ring->ntc);
+ struct hbg_buffer *buffer;
+ struct hbg_tx_desc tx_desc;
+ u32 ntu = ring->ntu;
+
+ if (unlikely(!hbg_nic_is_open(priv))) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (unlikely(!skb->len ||
+ skb->len > hbg_spec_max_frame_len(priv, HBG_DIR_TX))) {
+ dev_kfree_skb_any(skb);
+ net_dev->stats.tx_errors++;
+ return NETDEV_TX_OK;
+ }
+
+ if (unlikely(hbg_queue_is_full(ntc, ntu, ring) ||
+ hbg_fifo_is_full(ring->priv, ring->dir))) {
+ netif_stop_queue(net_dev);
+ return NETDEV_TX_BUSY;
+ }
+
+ buffer = &ring->queue[ntu];
+ buffer->skb = skb;
+ buffer->skb_len = skb->len;
+ if (unlikely(hbg_dma_map(buffer))) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ buffer->state = HBG_TX_STATE_START;
+ hbg_init_tx_desc(buffer, &tx_desc);
+ hbg_hw_set_tx_desc(priv, &tx_desc);
+
+ /* This smp_store_release() pairs with smp_load_acquire() in
+ * hbg_tx_buffer_recycle() called in tx interrupt handle process.
+ */
+ smp_store_release(&ring->ntu, hbg_queue_next_prt(ntu, ring));
+ net_dev->stats.tx_bytes += skb->len;
+ net_dev->stats.tx_packets++;
+ return NETDEV_TX_OK;
+}
+
+static void hbg_buffer_free_skb(struct hbg_buffer *buffer)
+{
+ if (unlikely(!buffer->skb))
+ return;
+
+ dev_kfree_skb_any(buffer->skb);
+ buffer->skb = NULL;
+}
+
+static void hbg_buffer_free(struct hbg_buffer *buffer)
+{
+ hbg_dma_unmap(buffer);
+ hbg_buffer_free_skb(buffer);
+}
+
+static int hbg_napi_tx_recycle(struct napi_struct *napi, int budget)
+{
+ struct hbg_ring *ring = container_of(napi, struct hbg_ring, napi);
+ /* This smp_load_acquire() pairs with smp_store_release() in
+ * hbg_start_xmit() called in xmit process.
+ */
+ u32 ntu = smp_load_acquire(&ring->ntu);
+ struct hbg_priv *priv = ring->priv;
+ struct hbg_buffer *buffer;
+ u32 ntc = ring->ntc;
+ int packet_done = 0;
+
+ while (packet_done < budget) {
+ if (unlikely(hbg_queue_is_empty(ntc, ntu)))
+ break;
+
+ /* make sure HW write desc complete */
+ dma_rmb();
+
+ buffer = &ring->queue[ntc];
+ if (buffer->state != HBG_TX_STATE_COMPLETE)
+ break;
+
+ hbg_buffer_free(buffer);
+ ntc = hbg_queue_next_prt(ntc, ring);
+ packet_done++;
+ }
+
+ /* This smp_store_release() pairs with smp_load_acquire() in
+ * hbg_start_xmit() called in xmit process.
+ */
+ smp_store_release(&ring->ntc, ntc);
+ netif_wake_queue(priv->netdev);
+
+ if (likely(napi_complete_done(napi, packet_done)))
+ hbg_hw_irq_enable(priv, HBG_INT_MSK_TX_B, true);
+
+ return packet_done;
+}
+
+static void hbg_ring_uninit(struct hbg_ring *ring)
+{
+ struct hbg_buffer *buffer;
+ u32 i;
+
+ if (!ring->queue)
+ return;
+
+ netif_napi_del(&ring->napi);
+
+ for (i = 0; i < ring->len; i++) {
+ buffer = &ring->queue[i];
+ hbg_buffer_free(buffer);
+ buffer->ring = NULL;
+ buffer->priv = NULL;
+ }
+
+ dma_free_coherent(&ring->priv->pdev->dev,
+ ring->len * sizeof(*ring->queue),
+ ring->queue, ring->queue_dma);
+ ring->queue = NULL;
+ ring->queue_dma = 0;
+ ring->len = 0;
+ ring->priv = NULL;
+}
+
+static int hbg_ring_init(struct hbg_priv *priv, struct hbg_ring *ring,
+ enum hbg_dir dir)
+{
+ struct hbg_buffer *buffer;
+ u32 i, len;
+
+ len = hbg_get_spec_fifo_max_num(priv, dir) + 1;
+ ring->queue = dma_alloc_coherent(&priv->pdev->dev,
+ len * sizeof(*ring->queue),
+ &ring->queue_dma, GFP_KERNEL);
+ if (!ring->queue)
+ return -ENOMEM;
+
+ for (i = 0; i < len; i++) {
+ buffer = &ring->queue[i];
+ buffer->skb_len = 0;
+ buffer->dir = dir;
+ buffer->ring = ring;
+ buffer->priv = priv;
+ buffer->state_dma = ring->queue_dma + (i * sizeof(*buffer));
+ }
+
+ ring->dir = dir;
+ ring->priv = priv;
+ ring->ntc = 0;
+ ring->ntu = 0;
+ ring->len = len;
+ return 0;
+}
+
+static int hbg_tx_ring_init(struct hbg_priv *priv)
+{
+ struct hbg_ring *tx_ring = &priv->tx_ring;
+ int ret;
+
+ if (!tx_ring->tout_log_buf)
+ tx_ring->tout_log_buf = devm_kzalloc(&priv->pdev->dev,
+ HBG_TX_TIMEOUT_BUF_LEN,
+ GFP_KERNEL);
+
+ if (!tx_ring->tout_log_buf)
+ return -ENOMEM;
+
+ ret = hbg_ring_init(priv, tx_ring, HBG_DIR_TX);
+ if (ret)
+ return ret;
+
+ netif_napi_add_tx(priv->netdev, &tx_ring->napi, hbg_napi_tx_recycle);
+ return 0;
+}
+
+int hbg_txrx_init(struct hbg_priv *priv)
+{
+ int ret;
+
+ ret = hbg_tx_ring_init(priv);
+ if (ret)
+ dev_err(&priv->pdev->dev,
+ "failed to init tx ring, ret = %d\n", ret);
+
+ return ret;
+}
+
+void hbg_txrx_uninit(void *data)
+{
+ struct hbg_priv *priv = data;
+
+ hbg_ring_uninit(&priv->tx_ring);
+}
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.h
new file mode 100644
index 000000000000..fa00a74a451a
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2024 Hisilicon Limited. */
+
+#ifndef __HBG_TXRX_H
+#define __HBG_TXRX_H
+
+#include <linux/etherdevice.h>
+#include "hbg_hw.h"
+
+static inline u32 hbg_spec_max_frame_len(struct hbg_priv *priv, enum hbg_dir dir)
+{
+ return (dir == HBG_DIR_TX) ? priv->dev_specs.max_frame_len :
+ priv->dev_specs.rx_buf_size;
+}
+
+static inline u32 hbg_get_spec_fifo_max_num(struct hbg_priv *priv, enum hbg_dir dir)
+{
+ return (dir == HBG_DIR_TX) ? priv->dev_specs.tx_fifo_num :
+ priv->dev_specs.rx_fifo_num;
+}
+
+static inline bool hbg_fifo_is_full(struct hbg_priv *priv, enum hbg_dir dir)
+{
+ return hbg_hw_get_fifo_used_num(priv, dir) >=
+ hbg_get_spec_fifo_max_num(priv, dir);
+}
+
+static inline u32 hbg_get_queue_used_num(struct hbg_ring *ring)
+{
+ return (ring->ntu + ring->len - ring->ntc) % ring->len;
+}
+
+netdev_tx_t hbg_net_start_xmit(struct sk_buff *skb, struct net_device *net_dev);
+int hbg_txrx_init(struct hbg_priv *priv);
+void hbg_txrx_uninit(void *data);
+
+#endif
--
2.33.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH V5 net-next 06/11] net: hibmcge: Implement .ndo_start_xmit function
2024-08-27 13:14 ` [PATCH V5 net-next 06/11] net: hibmcge: Implement .ndo_start_xmit function Jijie Shao
@ 2024-08-29 1:41 ` Jakub Kicinski
2024-08-29 2:32 ` Jijie Shao
0 siblings, 1 reply; 28+ messages in thread
From: Jakub Kicinski @ 2024-08-29 1:41 UTC (permalink / raw)
To: Jijie Shao
Cc: davem, edumazet, pabeni, shenjian15, wangpeiyang1, liuyonglong,
chenhao418, sudongming1, xujunsheng, shiyongbang, libaihan,
andrew, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
On Tue, 27 Aug 2024 21:14:50 +0800 Jijie Shao wrote:
> @@ -111,6 +135,14 @@ static int hbg_init(struct hbg_priv *priv)
> if (ret)
> return ret;
>
> + ret = hbg_txrx_init(priv);
> + if (ret)
> + return ret;
You allocate buffers on the probe path?
The queues and all the memory will be sitting allocated but unused if
admin does ip link set dev $eth0 down?
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH V5 net-next 06/11] net: hibmcge: Implement .ndo_start_xmit function
2024-08-29 1:41 ` Jakub Kicinski
@ 2024-08-29 2:32 ` Jijie Shao
2024-08-29 3:02 ` Andrew Lunn
2024-08-29 14:51 ` Jakub Kicinski
0 siblings, 2 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-29 2:32 UTC (permalink / raw)
To: Jakub Kicinski
Cc: shaojijie, davem, edumazet, pabeni, shenjian15, wangpeiyang1,
liuyonglong, chenhao418, sudongming1, xujunsheng, shiyongbang,
libaihan, andrew, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
on 2024/8/29 9:41, Jakub Kicinski wrote:
> On Tue, 27 Aug 2024 21:14:50 +0800 Jijie Shao wrote:
>> @@ -111,6 +135,14 @@ static int hbg_init(struct hbg_priv *priv)
>> if (ret)
>> return ret;
>>
>> + ret = hbg_txrx_init(priv);
>> + if (ret)
>> + return ret;
> You allocate buffers on the probe path?
> The queues and all the memory will be sitting allocated but unused if
> admin does ip link set dev $eth0 down?
The network port has only one queue and
the TX queue depth is 64, RX queue depth is 127.
so it doesn't take up much memory.
Also, I plan to add the self_test feature in a future patch.
If I don't allocate buffers on the probe path,
I won't be able to do a loopback test if the network port goes down unexpectedly.
So, if there are no other constraints, I prefer to alloc buffer memory in probe path.
Thanks,
Jijie Shao
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH V5 net-next 06/11] net: hibmcge: Implement .ndo_start_xmit function
2024-08-29 2:32 ` Jijie Shao
@ 2024-08-29 3:02 ` Andrew Lunn
2024-08-29 12:57 ` Jijie Shao
[not found] ` <f64c04a6-8c3d-46f5-a2dd-a9864de12169@huawei.com>
2024-08-29 14:51 ` Jakub Kicinski
1 sibling, 2 replies; 28+ messages in thread
From: Andrew Lunn @ 2024-08-29 3:02 UTC (permalink / raw)
To: Jijie Shao
Cc: Jakub Kicinski, davem, edumazet, pabeni, shenjian15, wangpeiyang1,
liuyonglong, chenhao418, sudongming1, xujunsheng, shiyongbang,
libaihan, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
On Thu, Aug 29, 2024 at 10:32:33AM +0800, Jijie Shao wrote:
>
> on 2024/8/29 9:41, Jakub Kicinski wrote:
> > On Tue, 27 Aug 2024 21:14:50 +0800 Jijie Shao wrote:
> > > @@ -111,6 +135,14 @@ static int hbg_init(struct hbg_priv *priv)
> > > if (ret)
> > > return ret;
> > > + ret = hbg_txrx_init(priv);
> > > + if (ret)
> > > + return ret;
> > You allocate buffers on the probe path?
> > The queues and all the memory will be sitting allocated but unused if
> > admin does ip link set dev $eth0 down?
>
> The network port has only one queue and
> the TX queue depth is 64, RX queue depth is 127.
> so it doesn't take up much memory.
>
> Also, I plan to add the self_test feature in a future patch.
> If I don't allocate buffers on the probe path,
> I won't be able to do a loopback test if the network port goes down unexpectedly.
When you come to implement ethtool --set-ring, you will need to
allocate and free the ring buffers outside of probe.
Andrew
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH V5 net-next 06/11] net: hibmcge: Implement .ndo_start_xmit function
2024-08-29 3:02 ` Andrew Lunn
@ 2024-08-29 12:57 ` Jijie Shao
[not found] ` <f64c04a6-8c3d-46f5-a2dd-a9864de12169@huawei.com>
1 sibling, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-29 12:57 UTC (permalink / raw)
To: Andrew Lunn
Cc: shaojijie, Jakub Kicinski, davem, edumazet, pabeni, shenjian15,
wangpeiyang1, liuyonglong, chenhao418, sudongming1, xujunsheng,
shiyongbang, libaihan, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
on 2024/8/29 11:02, Andrew Lunn wrote:
> On Thu, Aug 29, 2024 at 10:32:33AM +0800, Jijie Shao wrote:
>> on 2024/8/29 9:41, Jakub Kicinski wrote:
>>> On Tue, 27 Aug 2024 21:14:50 +0800 Jijie Shao wrote:
>>>> @@ -111,6 +135,14 @@ static int hbg_init(struct hbg_priv *priv)
>>>> if (ret)
>>>> return ret;
>>>> + ret = hbg_txrx_init(priv);
>>>> + if (ret)
>>>> + return ret;
>>> You allocate buffers on the probe path?
>>> The queues and all the memory will be sitting allocated but unused if
>>> admin does ip link set dev $eth0 down?
>> The network port has only one queue and
>> the TX queue depth is 64, RX queue depth is 127.
>> so it doesn't take up much memory.
>>
>> Also, I plan to add the self_test feature in a future patch.
>> If I don't allocate buffers on the probe path,
>> I won't be able to do a loopback test if the network port goes down unexpectedly.
> When you come to implement ethtool --set-ring, you will need to
> allocate and free the ring buffers outside of probe.
>
> Andrew
We discussed this internally, and now we have a problem that we can't solve:
After allocate ring buffers, the driver will writes the address to the hardware FIFO
for receiving packets.
However, FIFO does not have a separate interface to clear ring buffers address.
If ring buffers can be allocated and released on other paths,
the driver buffer address is inconsistent with the hardware buffer address.
As a result, packet receiving is abnormal. The fault is rectified only
after the buffers of a queue depth are used up and new buffers are allocated for.
Actually, the buffer will be released during FLR reset and are allocated again after reset.
In this case, the FIFO are cleared. Therefore, driver writes the ring buffer address
to the hardware again to ensure that the driver address is the same as the hardware address.
If we do an FLR every time we allocate ring buffers on other path, It is expensive.
We will not implement re-allocate ring buffer interfaces like ethtool --set-ring.
Thanks
Jijie Shao
^ permalink raw reply [flat|nested] 28+ messages in thread[parent not found: <f64c04a6-8c3d-46f5-a2dd-a9864de12169@huawei.com>]
* Re: [PATCH V5 net-next 06/11] net: hibmcge: Implement .ndo_start_xmit function
[not found] ` <f64c04a6-8c3d-46f5-a2dd-a9864de12169@huawei.com>
@ 2024-08-29 13:16 ` Andrew Lunn
2024-08-30 12:27 ` Jijie Shao
0 siblings, 1 reply; 28+ messages in thread
From: Andrew Lunn @ 2024-08-29 13:16 UTC (permalink / raw)
To: Jijie Shao; +Cc: netdev
On Thu, Aug 29, 2024 at 08:55:35PM +0800, Jijie Shao wrote:
>
> on 2024/8/29 11:02, Andrew Lunn wrote:
> > On Thu, Aug 29, 2024 at 10:32:33AM +0800, Jijie Shao wrote:
> > > on 2024/8/29 9:41, Jakub Kicinski wrote:
> > > > On Tue, 27 Aug 2024 21:14:50 +0800 Jijie Shao wrote:
> > > > > @@ -111,6 +135,14 @@ static int hbg_init(struct hbg_priv *priv)
> > > > > if (ret)
> > > > > return ret;
> > > > > + ret = hbg_txrx_init(priv);
> > > > > + if (ret)
> > > > > + return ret;
> > > > You allocate buffers on the probe path?
> > > > The queues and all the memory will be sitting allocated but unused if
> > > > admin does ip link set dev $eth0 down?
> > > The network port has only one queue and
> > > the TX queue depth is 64, RX queue depth is 127.
> > > so it doesn't take up much memory.
> > >
> > > Also, I plan to add the self_test feature in a future patch.
> > > If I don't allocate buffers on the probe path,
> > > I won't be able to do a loopback test if the network port goes down unexpectedly.
> > When you come to implement ethtool --set-ring, you will need to
> > allocate and free the ring buffers outside of probe.
> >
> > Andrew
>
> We discussed this internally, and now we have a problem that we can't solve:
>
> After allocate ring buffers, the driver will writes the address to the hardware FIFO
> for receiving packets.
>
> However, FIFO does not have a separate interface to clear ring buffers address.
>
> If ring buffers can be allocated and released on other paths,
> the driver buffer address is inconsistent with the hardware buffer address.
> As a result, packet receiving is abnormal. The fault is rectified only
> after the buffers of a queue depth are used up and new buffers are allocated for.
If the hardware is designed correctly, there should be away to tell
the hardware to stop receiving packets. Often there is then a way to
know it has actually stopped, and all in flight DMA transfers are
complete. Otherwise, you can make a guess at how long the worse case
DMA transfer takes, maybe a jumbo packet at 10Mbps, and simply sleep
that long. It is then safe to allocate new ring buffers, swap the
pointer, and then free the old.
You probably even have this code already in u-boot. You cannot jump
into the kernel without first ensuring the device has stopped DMAing
packets.
> Actually, the buffer will be released during FLR reset and are allocated again after reset.
> In this case, the FIFO are cleared. Therefore, driver writes the ring buffer address
> to the hardware again to ensure that the driver address is the same as the hardware address.
>
> If we do an FLR every time we allocate ring buffers on other path, It is expensive.
It does not matter if it is expensive. This is not hot path. ethtool
--set-ring is only going to be called once, maybe twice before the
machine is shut down. So we generally don't care about the cost.
Andrew
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH V5 net-next 06/11] net: hibmcge: Implement .ndo_start_xmit function
2024-08-29 13:16 ` Andrew Lunn
@ 2024-08-30 12:27 ` Jijie Shao
0 siblings, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-30 12:27 UTC (permalink / raw)
To: Andrew Lunn; +Cc: shaojijie, netdev
on 2024/8/29 21:16, Andrew Lunn wrote:
> On Thu, Aug 29, 2024 at 08:55:35PM +0800, Jijie Shao wrote:
>> on 2024/8/29 11:02, Andrew Lunn wrote:
>>> On Thu, Aug 29, 2024 at 10:32:33AM +0800, Jijie Shao wrote:
>>>> on 2024/8/29 9:41, Jakub Kicinski wrote:
>>>>> On Tue, 27 Aug 2024 21:14:50 +0800 Jijie Shao wrote:
>>>>>> @@ -111,6 +135,14 @@ static int hbg_init(struct hbg_priv *priv)
>>>>>> if (ret)
>>>>>> return ret;
>>>>>> + ret = hbg_txrx_init(priv);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>> You allocate buffers on the probe path?
>>>>> The queues and all the memory will be sitting allocated but unused if
>>>>> admin does ip link set dev $eth0 down?
>>>> The network port has only one queue and
>>>> the TX queue depth is 64, RX queue depth is 127.
>>>> so it doesn't take up much memory.
>>>>
>>>> Also, I plan to add the self_test feature in a future patch.
>>>> If I don't allocate buffers on the probe path,
>>>> I won't be able to do a loopback test if the network port goes down unexpectedly.
>>> When you come to implement ethtool --set-ring, you will need to
>>> allocate and free the ring buffers outside of probe.
>>>
>>> Andrew
>> We discussed this internally, and now we have a problem that we can't solve:
>>
>> After allocate ring buffers, the driver will writes the address to the hardware FIFO
>> for receiving packets.
>>
>> However, FIFO does not have a separate interface to clear ring buffers address.
>>
>> If ring buffers can be allocated and released on other paths,
>> the driver buffer address is inconsistent with the hardware buffer address.
>> As a result, packet receiving is abnormal. The fault is rectified only
>> after the buffers of a queue depth are used up and new buffers are allocated for.
> If the hardware is designed correctly, there should be away to tell
> the hardware to stop receiving packets. Often there is then a way to
> know it has actually stopped, and all in flight DMA transfers are
> complete. Otherwise, you can make a guess at how long the worse case
> DMA transfer takes, maybe a jumbo packet at 10Mbps, and simply sleep
> that long. It is then safe to allocate new ring buffers, swap the
> pointer, and then free the old.
>
> You probably even have this code already in u-boot. You cannot jump
> into the kernel without first ensuring the device has stopped DMAing
> packets.
>
>> Actually, the buffer will be released during FLR reset and are allocated again after reset.
>> In this case, the FIFO are cleared. Therefore, driver writes the ring buffer address
>> to the hardware again to ensure that the driver address is the same as the hardware address.
>>
>> If we do an FLR every time we allocate ring buffers on other path, It is expensive.
> It does not matter if it is expensive. This is not hot path. ethtool
> --set-ring is only going to be called once, maybe twice before the
> machine is shut down. So we generally don't care about the cost.
>
> Andrew
I have removed the alloc ring buffer from the probe path in v6.
After the test, the network port can work normally after being repeatedly down and up.
Thanks,
Jijie Shao
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH V5 net-next 06/11] net: hibmcge: Implement .ndo_start_xmit function
2024-08-29 2:32 ` Jijie Shao
2024-08-29 3:02 ` Andrew Lunn
@ 2024-08-29 14:51 ` Jakub Kicinski
1 sibling, 0 replies; 28+ messages in thread
From: Jakub Kicinski @ 2024-08-29 14:51 UTC (permalink / raw)
To: Jijie Shao
Cc: davem, edumazet, pabeni, shenjian15, wangpeiyang1, liuyonglong,
chenhao418, sudongming1, xujunsheng, shiyongbang, libaihan,
andrew, jdamato, horms, jonathan.cameron,
shameerali.kolothum.thodi, salil.mehta, netdev, linux-kernel
On Thu, 29 Aug 2024 10:32:33 +0800 Jijie Shao wrote:
> I won't be able to do a loopback test if the network port goes down unexpectedly.
One clarification in addition to what Andrew explained - we're talking
about allocating in ndo_open, not carrier itself being up / down.
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH V5 net-next 07/11] net: hibmcge: Implement rx_poll function to receive packets
2024-08-27 13:14 [PATCH V5 net-next 00/11] Add support of HIBMCGE Ethernet Driver Jijie Shao
` (5 preceding siblings ...)
2024-08-27 13:14 ` [PATCH V5 net-next 06/11] net: hibmcge: Implement .ndo_start_xmit function Jijie Shao
@ 2024-08-27 13:14 ` Jijie Shao
2024-08-27 13:14 ` [PATCH V5 net-next 08/11] net: hibmcge: Implement some ethtool_ops functions Jijie Shao
` (3 subsequent siblings)
10 siblings, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-27 13:14 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: shenjian15, wangpeiyang1, liuyonglong, chenhao418, shaojijie,
sudongming1, xujunsheng, shiyongbang, libaihan, andrew, jdamato,
horms, jonathan.cameron, shameerali.kolothum.thodi, salil.mehta,
netdev, linux-kernel
Implement rx_poll function to read the rx descriptor after
receiving the rx interrupt. Adjust the skb based on the
descriptor to complete the reception of the packet.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
.../ethernet/hisilicon/hibmcge/hbg_common.h | 5 +
.../net/ethernet/hisilicon/hibmcge/hbg_hw.c | 10 ++
.../net/ethernet/hisilicon/hibmcge/hbg_hw.h | 1 +
.../net/ethernet/hisilicon/hibmcge/hbg_irq.c | 8 +-
.../net/ethernet/hisilicon/hibmcge/hbg_main.c | 2 +
.../net/ethernet/hisilicon/hibmcge/hbg_reg.h | 13 ++
.../net/ethernet/hisilicon/hibmcge/hbg_txrx.c | 169 +++++++++++++++++-
7 files changed, 206 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
index 41de51303d73..c3ce3512807b 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
@@ -16,6 +16,10 @@
#define HBG_VECTOR_NUM 4
#define HBG_PCU_CACHE_LINE_SIZE 32
#define HBG_TX_TIMEOUT_BUF_LEN 1024
+#define HBG_RX_DESCR 0x01
+
+#define HBG_PACKET_HEAD_SIZE ((HBG_RX_SKIP1 + HBG_RX_SKIP2 + HBG_RX_DESCR) * \
+ HBG_PCU_CACHE_LINE_SIZE)
enum hbg_dir {
HBG_DIR_TX = 1 << 0,
@@ -126,6 +130,7 @@ struct hbg_priv {
struct hbg_mac mac;
struct hbg_vector vectors;
struct hbg_ring tx_ring;
+ struct hbg_ring rx_ring;
};
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
index 4f60bfdc3249..02aa6428e327 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
@@ -68,6 +68,7 @@ static int hbg_hw_dev_specs_init(struct hbg_priv *priv)
return -EADDRNOTAVAIL;
dev_specs->max_frame_len = HBG_PCU_CACHE_LINE_SIZE + dev_specs->max_mtu;
+ dev_specs->rx_buf_size = HBG_PACKET_HEAD_SIZE + dev_specs->max_frame_len;
return 0;
}
@@ -172,6 +173,10 @@ u32 hbg_hw_get_fifo_used_num(struct hbg_priv *priv, enum hbg_dir dir)
return hbg_reg_read_field(priv, HBG_REG_CF_CFF_DATA_NUM_ADDR,
HBG_REG_CF_CFF_DATA_NUM_ADDR_TX_M);
+ if (dir & HBG_DIR_RX)
+ return hbg_reg_read_field(priv, HBG_REG_CF_CFF_DATA_NUM_ADDR,
+ HBG_REG_CF_CFF_DATA_NUM_ADDR_RX_M);
+
return 0;
}
@@ -183,6 +188,11 @@ void hbg_hw_set_tx_desc(struct hbg_priv *priv, struct hbg_tx_desc *tx_desc)
hbg_reg_write(priv, HBG_REG_TX_CFF_ADDR_3_ADDR, tx_desc->word3);
}
+void hbg_hw_fill_buffer(struct hbg_priv *priv, u32 buffer_dma_addr)
+{
+ hbg_reg_write(priv, HBG_REG_RX_CFF_ADDR_ADDR, buffer_dma_addr);
+}
+
void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex)
{
hbg_reg_write_field(priv, HBG_REG_PORT_MODE_ADDR,
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
index 15bf16a50f14..18c6ccbe1b28 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
@@ -52,5 +52,6 @@ void hbg_hw_mac_enable(struct hbg_priv *priv, u32 enable);
void hbg_hw_set_uc_addr(struct hbg_priv *priv, u64 mac_addr);
u32 hbg_hw_get_fifo_used_num(struct hbg_priv *priv, enum hbg_dir dir);
void hbg_hw_set_tx_desc(struct hbg_priv *priv, struct hbg_tx_desc *tx_desc);
+void hbg_hw_fill_buffer(struct hbg_priv *priv, u32 buffer_dma_addr);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
index b2a55967ffad..d7551c5527b4 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
@@ -19,13 +19,19 @@ static void hbg_irq_handle_tx(struct hbg_priv *priv,
napi_schedule(&priv->tx_ring.napi);
}
+static void hbg_irq_handle_rx(struct hbg_priv *priv,
+ struct hbg_irq_info *irq_info)
+{
+ napi_schedule(&priv->rx_ring.napi);
+}
+
#define HBG_TXRX_IRQ_I(name, handle) \
{#name, HBG_INT_MSK_##name##_B, false, false, 0, handle}
#define HBG_ERR_IRQ_I(name, need_print) \
{#name, HBG_INT_MSK_##name##_B, true, need_print, 0, hbg_irq_handle_err}
static struct hbg_irq_info hbg_irqs[] = {
- HBG_TXRX_IRQ_I(RX, NULL),
+ HBG_TXRX_IRQ_I(RX, hbg_irq_handle_rx),
HBG_TXRX_IRQ_I(TX, hbg_irq_handle_tx),
HBG_ERR_IRQ_I(MAC_MII_FIFO_ERR, true),
HBG_ERR_IRQ_I(MAC_PCS_RX_FIFO_ERR, true),
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
index 73aa21e9cec9..9af508987937 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
@@ -30,6 +30,7 @@ static int hbg_net_open(struct net_device *dev)
return 0;
netif_carrier_off(dev);
+ napi_enable(&priv->rx_ring.napi);
napi_enable(&priv->tx_ring.napi);
hbg_all_irq_enable(priv, true);
hbg_hw_mac_enable(priv, HBG_STATUS_ENABLE);
@@ -53,6 +54,7 @@ static int hbg_net_stop(struct net_device *dev)
hbg_hw_mac_enable(priv, HBG_STATUS_DISABLE);
hbg_all_irq_enable(priv, false);
napi_disable(&priv->tx_ring.napi);
+ napi_disable(&priv->rx_ring.napi);
return 0;
}
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
index 0abfcd84e56b..410081d01acf 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
@@ -82,10 +82,12 @@
#define HBG_REG_MAX_FRAME_LEN_M GENMASK(15, 0)
#define HBG_REG_CF_CFF_DATA_NUM_ADDR (HBG_REG_SGMII_BASE + 0x045C)
#define HBG_REG_CF_CFF_DATA_NUM_ADDR_TX_M GENMASK(8, 0)
+#define HBG_REG_CF_CFF_DATA_NUM_ADDR_RX_M GENMASK(24, 16)
#define HBG_REG_TX_CFF_ADDR_0_ADDR (HBG_REG_SGMII_BASE + 0x0488)
#define HBG_REG_TX_CFF_ADDR_1_ADDR (HBG_REG_SGMII_BASE + 0x048C)
#define HBG_REG_TX_CFF_ADDR_2_ADDR (HBG_REG_SGMII_BASE + 0x0490)
#define HBG_REG_TX_CFF_ADDR_3_ADDR (HBG_REG_SGMII_BASE + 0x0494)
+#define HBG_REG_RX_CFF_ADDR_ADDR (HBG_REG_SGMII_BASE + 0x04A0)
#define HBG_REG_RX_BUF_SIZE_ADDR (HBG_REG_SGMII_BASE + 0x04E4)
#define HBG_REG_RX_BUF_SIZE_M GENMASK(15, 0)
#define HBG_REG_BUS_CTRL_ADDR (HBG_REG_SGMII_BASE + 0x04E8)
@@ -127,4 +129,15 @@ struct hbg_tx_desc {
#define HBG_TX_DESC_W0_l4_CS_B BIT(0)
#define HBG_TX_DESC_W1_SEND_LEN_M GENMASK(19, 4)
+struct hbg_rx_desc {
+ u32 word0;
+ u32 word1; /* tag */
+ u32 word2;
+ u32 word3;
+ u32 word4;
+ u32 word5;
+};
+
+#define HBG_RX_DESC_W2_PKT_LEN_M GENMASK(31, 16)
+
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
index 9c48be9cd469..712af839bbba 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
@@ -14,6 +14,9 @@
#define hbg_queue_is_full(head, tail, ring) ((head) == ((tail) + 1) % (ring)->len)
#define hbg_queue_is_empty(head, tail) ((head) == (tail))
#define hbg_queue_next_prt(p, ring) (((p) + 1) % (ring)->len)
+#define hbg_queue_move_next(p, ring) ({ \
+ typeof(ring) _ring = (ring); \
+ _ring->p = hbg_queue_next_prt(_ring->p, _ring); })
static int hbg_dma_map(struct hbg_buffer *buffer)
{
@@ -119,6 +122,20 @@ static void hbg_buffer_free_skb(struct hbg_buffer *buffer)
buffer->skb = NULL;
}
+static int hbg_buffer_alloc_skb(struct hbg_buffer *buffer)
+{
+ u32 len = hbg_spec_max_frame_len(buffer->priv, buffer->dir);
+ struct hbg_priv *priv = buffer->priv;
+
+ buffer->skb = netdev_alloc_skb(priv->netdev, len);
+ if (unlikely(!buffer->skb))
+ return -ENOMEM;
+
+ buffer->skb_len = len;
+ memset(buffer->skb->data, 0, HBG_PACKET_HEAD_SIZE);
+ return 0;
+}
+
static void hbg_buffer_free(struct hbg_buffer *buffer)
{
hbg_dma_unmap(buffer);
@@ -165,6 +182,120 @@ static int hbg_napi_tx_recycle(struct napi_struct *napi, int budget)
return packet_done;
}
+static int hbg_rx_fill_one_buffer(struct hbg_priv *priv)
+{
+ struct hbg_ring *ring = &priv->rx_ring;
+ struct hbg_buffer *buffer;
+ int ret;
+
+ if (hbg_queue_is_full(ring->ntc, ring->ntu, ring))
+ return 0;
+
+ buffer = &ring->queue[ring->ntu];
+ ret = hbg_buffer_alloc_skb(buffer);
+ if (unlikely(ret))
+ return ret;
+
+ ret = hbg_dma_map(buffer);
+ if (unlikely(ret)) {
+ hbg_buffer_free_skb(buffer);
+ return ret;
+ }
+
+ hbg_hw_fill_buffer(priv, buffer->skb_dma);
+ hbg_queue_move_next(ntu, ring);
+ return 0;
+}
+
+static int hbg_rx_fill_buffers(struct hbg_priv *priv)
+{
+ struct hbg_ring *ring = &priv->rx_ring;
+ u32 fifo_left;
+ int ret;
+
+ if (hbg_fifo_is_full(priv, ring->dir))
+ return 0;
+
+ fifo_left = hbg_get_spec_fifo_max_num(priv, ring->dir) -
+ hbg_hw_get_fifo_used_num(priv, ring->dir);
+ while (fifo_left) {
+ ret = hbg_rx_fill_one_buffer(priv);
+ if (ret)
+ return ret;
+
+ fifo_left--;
+ }
+
+ return 0;
+}
+
+static bool hbg_sync_data_from_hw(struct hbg_priv *priv,
+ struct hbg_buffer *buffer)
+{
+ struct hbg_rx_desc *rx_desc;
+
+ /* make sure HW write desc complete */
+ dma_rmb();
+
+ dma_sync_single_for_cpu(&priv->pdev->dev, buffer->skb_dma,
+ buffer->skb_len, DMA_FROM_DEVICE);
+
+ rx_desc = (struct hbg_rx_desc *)buffer->skb->data;
+ return FIELD_GET(HBG_RX_DESC_W2_PKT_LEN_M, rx_desc->word2) != 0;
+}
+
+static int hbg_napi_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct hbg_ring *ring = container_of(napi, struct hbg_ring, napi);
+ struct hbg_priv *priv = ring->priv;
+ struct hbg_rx_desc *rx_desc;
+ struct hbg_buffer *buffer;
+ u32 packet_done = 0;
+ u32 pkt_len;
+
+ if (unlikely(!hbg_nic_is_open(priv))) {
+ napi_complete(napi);
+ return 0;
+ }
+
+ while (packet_done < budget) {
+ if (unlikely(hbg_queue_is_empty(ring->ntc, ring->ntu)))
+ break;
+
+ buffer = &ring->queue[ring->ntc];
+ if (unlikely(!buffer->skb))
+ goto next_buffer;
+
+ if (unlikely(!hbg_sync_data_from_hw(priv, buffer)))
+ break;
+
+ hbg_dma_unmap(buffer);
+
+ skb_reserve(buffer->skb, HBG_PACKET_HEAD_SIZE + NET_IP_ALIGN);
+
+ rx_desc = (struct hbg_rx_desc *)buffer->skb->data;
+ pkt_len = FIELD_GET(HBG_RX_DESC_W2_PKT_LEN_M, rx_desc->word2);
+ skb_put(buffer->skb, pkt_len);
+ buffer->skb->protocol = eth_type_trans(buffer->skb, priv->netdev);
+
+ priv->netdev->stats.rx_bytes += pkt_len;
+ priv->netdev->stats.rx_packets++;
+ netif_receive_skb(buffer->skb);
+ buffer->skb = NULL;
+ hbg_rx_fill_one_buffer(priv);
+
+next_buffer:
+ hbg_queue_move_next(ntc, ring);
+ packet_done++;
+ }
+
+ hbg_rx_fill_buffers(priv);
+ if (likely(napi_complete_done(napi, packet_done)))
+ hbg_hw_irq_enable(priv, HBG_INT_MSK_RX_B, true);
+
+ return packet_done;
+}
+
static void hbg_ring_uninit(struct hbg_ring *ring)
{
struct hbg_buffer *buffer;
@@ -242,15 +373,50 @@ static int hbg_tx_ring_init(struct hbg_priv *priv)
return 0;
}
+static int hbg_rx_ring_init(struct hbg_priv *priv)
+{
+ struct hbg_ring *rx_ring = &priv->rx_ring;
+ int ret;
+
+ ret = hbg_ring_init(priv, rx_ring, HBG_DIR_RX);
+ if (ret)
+ return ret;
+
+ netif_napi_add(priv->netdev, &priv->rx_ring.napi, hbg_napi_rx_poll);
+ return 0;
+}
+
int hbg_txrx_init(struct hbg_priv *priv)
{
int ret;
ret = hbg_tx_ring_init(priv);
- if (ret)
+ if (ret) {
dev_err(&priv->pdev->dev,
"failed to init tx ring, ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = hbg_rx_ring_init(priv);
+ if (ret) {
+ dev_err(&priv->pdev->dev,
+ "failed to init rx ring, ret = %d\n", ret);
+ goto err_uninit_tx;
+ }
+
+ ret = hbg_rx_fill_buffers(priv);
+ if (ret) {
+ dev_err(&priv->pdev->dev,
+ "failed to fill rx buffers, ret = %d\n", ret);
+ goto err_uninit_rx;
+ }
+ return 0;
+
+err_uninit_rx:
+ hbg_ring_uninit(&priv->rx_ring);
+err_uninit_tx:
+ hbg_ring_uninit(&priv->tx_ring);
return ret;
}
@@ -259,4 +425,5 @@ void hbg_txrx_uninit(void *data)
struct hbg_priv *priv = data;
hbg_ring_uninit(&priv->tx_ring);
+ hbg_ring_uninit(&priv->rx_ring);
}
--
2.33.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH V5 net-next 08/11] net: hibmcge: Implement some ethtool_ops functions
2024-08-27 13:14 [PATCH V5 net-next 00/11] Add support of HIBMCGE Ethernet Driver Jijie Shao
` (6 preceding siblings ...)
2024-08-27 13:14 ` [PATCH V5 net-next 07/11] net: hibmcge: Implement rx_poll function to receive packets Jijie Shao
@ 2024-08-27 13:14 ` Jijie Shao
2024-08-27 13:14 ` [PATCH V5 net-next 09/11] net: hibmcge: Add a Makefile and update Kconfig for hibmcge Jijie Shao
` (2 subsequent siblings)
10 siblings, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-27 13:14 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: shenjian15, wangpeiyang1, liuyonglong, chenhao418, shaojijie,
sudongming1, xujunsheng, shiyongbang, libaihan, andrew, jdamato,
horms, jonathan.cameron, shameerali.kolothum.thodi, salil.mehta,
netdev, linux-kernel
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>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
.../ethernet/hisilicon/hibmcge/hbg_ethtool.c | 17 +++++++++++++++++
.../ethernet/hisilicon/hibmcge/hbg_ethtool.h | 11 +++++++++++
.../net/ethernet/hisilicon/hibmcge/hbg_main.c | 2 ++
3 files changed, 30 insertions(+)
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_ethtool.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c
new file mode 100644
index 000000000000..c3370114aef3
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (c) 2024 Hisilicon Limited.
+
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include "hbg_ethtool.h"
+
+static const struct ethtool_ops hbg_ethtool_ops = {
+ .get_link = ethtool_op_get_link,
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .set_link_ksettings = phy_ethtool_set_link_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 9af508987937..b4dc423648a8 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
@@ -6,6 +6,7 @@
#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_mdio.h"
@@ -216,6 +217,7 @@ static int hbg_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
return dev_err_probe(dev, ret, "failed to register netdev\n");
+ hbg_ethtool_set_ops(netdev);
return 0;
}
--
2.33.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH V5 net-next 09/11] net: hibmcge: Add a Makefile and update Kconfig for hibmcge
2024-08-27 13:14 [PATCH V5 net-next 00/11] Add support of HIBMCGE Ethernet Driver Jijie Shao
` (7 preceding siblings ...)
2024-08-27 13:14 ` [PATCH V5 net-next 08/11] net: hibmcge: Implement some ethtool_ops functions Jijie Shao
@ 2024-08-27 13:14 ` Jijie Shao
2024-08-27 13:14 ` [PATCH V5 net-next 10/11] net: hibmcge: Add maintainer " Jijie Shao
2024-08-27 13:14 ` [PATCH V5 net-next 11/11] net: add ndo_validate_addr check in dev_set_mac_address Jijie Shao
10 siblings, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-27 13:14 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: shenjian15, wangpeiyang1, liuyonglong, chenhao418, shaojijie,
sudongming1, xujunsheng, shiyongbang, libaihan, andrew, jdamato,
horms, jonathan.cameron, shameerali.kolothum.thodi, salil.mehta,
netdev, linux-kernel
Add a Makefile and update Kconfig to build hibmcge driver.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
drivers/net/ethernet/hisilicon/Kconfig | 16 +++++++++++++++-
drivers/net/ethernet/hisilicon/Makefile | 1 +
drivers/net/ethernet/hisilicon/hibmcge/Makefile | 10 ++++++++++
3 files changed, 26 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/hisilicon/hibmcge/Makefile
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
index 3312e1d93c3b..66444794ce86 100644
--- a/drivers/net/ethernet/hisilicon/Kconfig
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -7,7 +7,6 @@ config NET_VENDOR_HISILICON
bool "Hisilicon devices"
default y
depends on OF || ACPI
- depends on ARM || ARM64 || COMPILE_TEST
help
If you have a network (Ethernet) card belonging to this class, say Y.
@@ -18,6 +17,8 @@ config NET_VENDOR_HISILICON
if NET_VENDOR_HISILICON
+if ARM || ARM64 || COMPILE_TEST
+
config HIX5HD2_GMAC
tristate "Hisilicon HIX5HD2 Family Network Device Support"
select PHYLIB
@@ -141,4 +142,17 @@ config HNS3_ENET
endif #HNS3
+endif # ARM || ARM64 || COMPILE_TEST
+
+config HIBMCGE
+ tristate "Hisilicon BMC Gigabit Ethernet Device Support"
+ depends on PCI && PCI_MSI
+ select PHYLIB
+ help
+ If you wish to compile a kernel for a BMC with HIBMC-xx_gmac
+ then you should answer Y to this. This makes this driver suitable for use
+ on certain boards such as the HIBMC-210.
+
+ If you are unsure, say N.
+
endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
index 7f76d412047a..0e2cadfea8ff 100644
--- a/drivers/net/ethernet/hisilicon/Makefile
+++ b/drivers/net/ethernet/hisilicon/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_HNS_MDIO) += hns_mdio.o
obj-$(CONFIG_HNS) += hns/
obj-$(CONFIG_HNS3) += hns3/
obj-$(CONFIG_HISI_FEMAC) += hisi_femac.o
+obj-$(CONFIG_HIBMCGE) += hibmcge/
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/Makefile b/drivers/net/ethernet/hisilicon/hibmcge/Makefile
new file mode 100644
index 000000000000..ea223b7207af
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Makefile for the HISILICON BMC GE network device drivers.
+#
+
+ccflags-y += -I$(src)
+
+obj-$(CONFIG_HIBMCGE) += hibmcge.o
+
+hibmcge-objs = hbg_main.o hbg_hw.o hbg_mdio.o hbg_irq.o hbg_txrx.o hbg_ethtool.o
--
2.33.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH V5 net-next 10/11] net: hibmcge: Add maintainer for hibmcge
2024-08-27 13:14 [PATCH V5 net-next 00/11] Add support of HIBMCGE Ethernet Driver Jijie Shao
` (8 preceding siblings ...)
2024-08-27 13:14 ` [PATCH V5 net-next 09/11] net: hibmcge: Add a Makefile and update Kconfig for hibmcge Jijie Shao
@ 2024-08-27 13:14 ` Jijie Shao
2024-08-27 13:14 ` [PATCH V5 net-next 11/11] net: add ndo_validate_addr check in dev_set_mac_address Jijie Shao
10 siblings, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-27 13:14 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: shenjian15, wangpeiyang1, liuyonglong, chenhao418, shaojijie,
sudongming1, xujunsheng, shiyongbang, libaihan, andrew, jdamato,
horms, jonathan.cameron, shameerali.kolothum.thodi, salil.mehta,
netdev, linux-kernel
Add myself as the maintainer for the hibmcge ethernet driver.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 5dbf23cf11c8..2691a08e16db 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10123,6 +10123,13 @@ S: Maintained
W: http://www.hisilicon.com
F: drivers/net/ethernet/hisilicon/hns3/
+HISILICON NETWORK HIBMCGE DRIVER
+M: Jijie Shao <shaojijie@huawei.com>
+L: netdev@vger.kernel.org
+S: Maintained
+W: http://www.hisilicon.com
+F: drivers/net/ethernet/hisilicon/hibmcge/
+
HISILICON NETWORK SUBSYSTEM DRIVER
M: Yisen Zhuang <yisen.zhuang@huawei.com>
M: Salil Mehta <salil.mehta@huawei.com>
--
2.33.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH V5 net-next 11/11] net: add ndo_validate_addr check in dev_set_mac_address
2024-08-27 13:14 [PATCH V5 net-next 00/11] Add support of HIBMCGE Ethernet Driver Jijie Shao
` (9 preceding siblings ...)
2024-08-27 13:14 ` [PATCH V5 net-next 10/11] net: hibmcge: Add maintainer " Jijie Shao
@ 2024-08-27 13:14 ` Jijie Shao
10 siblings, 0 replies; 28+ messages in thread
From: Jijie Shao @ 2024-08-27 13:14 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: shenjian15, wangpeiyang1, liuyonglong, chenhao418, shaojijie,
sudongming1, xujunsheng, shiyongbang, libaihan, andrew, jdamato,
horms, jonathan.cameron, shameerali.kolothum.thodi, salil.mehta,
netdev, linux-kernel
If driver implements ndo_validate_addr,
core should check the mac address before ndo_set_mac_address.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
net/core/dev.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/net/core/dev.c b/net/core/dev.c
index e7260889d4cb..7e9c3017e705 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -9087,6 +9087,11 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa,
return -EOPNOTSUPP;
if (sa->sa_family != dev->type)
return -EINVAL;
+ if (ops->ndo_validate_addr) {
+ err = ops->ndo_validate_addr(dev);
+ if (err)
+ return err;
+ }
if (!netif_device_present(dev))
return -ENODEV;
err = dev_pre_changeaddr_notify(dev, sa->sa_data, extack);
--
2.33.0
^ permalink raw reply related [flat|nested] 28+ messages in thread