Merge branch 'add-support-of-hibmcge-ethernet-driver'

Jijie Shao says:

====================
Add support of HIBMCGE Ethernet Driver

This patch set adds the support of Hisilicon BMC Gigabit Ethernet Driver.

This patch set includes basic Rx/Tx functionality. It also includes
the registration and interrupt codes.

This work provides the initial support to the HIBMCGE and
would incrementally add features or enhancements.
====================

Link: https://patch.msgid.link/20241015123516.4035035-1-shaojijie@huawei.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2024-10-21 11:26:55 +02:00
commit 7cb08476e1
17 changed files with 1737 additions and 1 deletions

View File

@ -10278,6 +10278,12 @@ 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
F: drivers/net/ethernet/hisilicon/hibmcge/
HISILICON NETWORK SUBSYSTEM DRIVER
M: Jian Shen <shenjian15@huawei.com>
M: Salil Mehta <salil.mehta@huawei.com>

View File

@ -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,19 @@ 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
select MOTORCOMM_PHY
select REALTEK_PHY
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

View File

@ -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/

View File

@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Makefile for the HISILICON BMC GE network device drivers.
#
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

View File

@ -0,0 +1,131 @@
/* 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>
#include "hbg_reg.h"
#define HBG_STATUS_DISABLE 0x0
#define HBG_STATUS_ENABLE 0x1
#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
#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,
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,
};
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 */
HBG_HW_EVENT_RESET,
};
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_irq_info {
const char *name;
u32 mask;
bool re_enable;
bool need_print;
u64 count;
void (*irq_handle)(struct hbg_priv *priv, struct hbg_irq_info *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;
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;
struct hbg_vector vectors;
struct hbg_ring tx_ring;
struct hbg_ring rx_ring;
};
#endif

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1,271 @@
// 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"
#define HBG_HW_EVENT_WAIT_TIMEOUT_US (2 * 1000 * 1000)
#define HBG_HW_EVENT_WAIT_INTERVAL_US (10 * 1000)
/* little endian or big endian.
* ctrl means packet description, data means skb packet data
*/
#define HBG_ENDIAN_CTRL_LE_DATA_BE 0x0
#define HBG_PCU_FRAME_LEN_PLUS 4
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);
}
int hbg_hw_event_notify(struct hbg_priv *priv,
enum hbg_hw_event_type event_type)
{
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 *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;
}
specs->mac_id = hbg_reg_read(priv, HBG_REG_MAC_ID_ADDR);
specs->phy_addr = hbg_reg_read(priv, HBG_REG_PHY_ID_ADDR);
specs->mdio_frequency = hbg_reg_read(priv, HBG_REG_MDIO_FREQ_ADDR);
specs->max_mtu = hbg_reg_read(priv, HBG_REG_MAX_MTU_ADDR);
specs->min_mtu = hbg_reg_read(priv, HBG_REG_MIN_MTU_ADDR);
specs->vlan_layers = hbg_reg_read(priv, HBG_REG_VLAN_LAYERS_ADDR);
specs->rx_fifo_num = hbg_reg_read(priv, HBG_REG_RX_FIFO_NUM_ADDR);
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 *)specs->mac_addr.sa_data);
if (!is_valid_ether_addr((u8 *)specs->mac_addr.sa_data))
return -EADDRNOTAVAIL;
specs->max_frame_len = HBG_PCU_CACHE_LINE_SIZE + specs->max_mtu;
specs->rx_buf_size = HBG_PACKET_HEAD_SIZE + specs->max_frame_len;
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);
hbg_field_modify(status, HBG_INT_MSK_TX_B,
hbg_reg_read(priv, HBG_REG_CF_IND_TXINT_STAT_ADDR));
hbg_field_modify(status, 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_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)
{
max_frame_len = max_t(u32, max_frame_len, ETH_DATA_LEN);
/* lower two bits of value must be set to 0 */
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);
}
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);
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;
}
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_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,
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_ctrl(struct hbg_priv *priv)
{
u32 ctrl = 0;
ctrl |= FIELD_PREP(HBG_REG_TRANSMIT_CTRL_AN_EN_B, HBG_STATUS_ENABLE);
ctrl |= FIELD_PREP(HBG_REG_TRANSMIT_CTRL_CRC_ADD_B, HBG_STATUS_ENABLE);
ctrl |= FIELD_PREP(HBG_REG_TRANSMIT_CTRL_PAD_EN_B, HBG_STATUS_ENABLE);
hbg_reg_write(priv, HBG_REG_TRANSMIT_CTRL_ADDR, ctrl);
}
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_control(struct hbg_priv *priv)
{
hbg_hw_init_rx_ctrl(priv);
/* parse from L2 layer */
hbg_reg_write_field(priv, HBG_REG_RX_PKT_MODE_ADDR,
HBG_REG_RX_PKT_MODE_PARSE_MODE_M, 0x1);
hbg_reg_write_field(priv, HBG_REG_RECV_CTRL_ADDR,
HBG_REG_RECV_CTRL_STRIP_PAD_EN_B,
HBG_STATUS_ENABLE);
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)
{
int ret;
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_ctrl(priv);
return 0;
}

View File

@ -0,0 +1,59 @@
/* 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);
}
#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_event_notify(struct hbg_priv *priv,
enum hbg_hw_event_type event_type);
int hbg_hw_init(struct hbg_priv *priv);
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);
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);
void hbg_hw_fill_buffer(struct hbg_priv *priv, u32 buffer_dma_addr);
#endif

View File

@ -0,0 +1,127 @@
// 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);
}
static void hbg_irq_handle_tx(struct hbg_priv *priv,
struct hbg_irq_info *irq_info)
{
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, 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),
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->re_enable)
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;
/* used pcim_enable_device(), so the vectors become device managed */
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 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 requested, 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 id\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 request irq: %s\n",
irq_names_map[i]);
}
vectors->info_array = hbg_irqs;
vectors->info_array_len = ARRAY_SIZE(hbg_irqs);
return 0;
}

View File

@ -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

View File

@ -0,0 +1,253 @@
// SPDX-License-Identifier: GPL-2.0+
// 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"
#include "hbg_ethtool.h"
#include "hbg_hw.h"
#include "hbg_irq.h"
#include "hbg_mdio.h"
#include "hbg_txrx.h"
static void hbg_change_mtu(struct hbg_priv *priv, int new_mtu);
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 *netdev)
{
struct hbg_priv *priv = netdev_priv(netdev);
int ret;
ret = hbg_txrx_init(priv);
if (ret)
return ret;
hbg_all_irq_enable(priv, true);
hbg_hw_mac_enable(priv, HBG_STATUS_ENABLE);
netif_start_queue(netdev);
hbg_phy_start(priv);
return 0;
}
/* This function only can be called after hbg_txrx_uninit() */
static int hbg_hw_txrx_clear(struct hbg_priv *priv)
{
int ret;
/* After ring buffers have been released,
* do a reset to release hw fifo rx ring buffer
*/
ret = hbg_hw_event_notify(priv, HBG_HW_EVENT_RESET);
if (ret)
return ret;
/* After reset, regs need to be reconfigured */
hbg_hw_init(priv);
hbg_hw_set_uc_addr(priv, ether_addr_to_u64(priv->netdev->dev_addr));
hbg_change_mtu(priv, priv->netdev->mtu);
return 0;
}
static int hbg_net_stop(struct net_device *netdev)
{
struct hbg_priv *priv = netdev_priv(netdev);
hbg_phy_stop(priv);
netif_stop_queue(netdev);
hbg_hw_mac_enable(priv, HBG_STATUS_DISABLE);
hbg_all_irq_enable(priv, false);
hbg_txrx_uninit(priv);
return hbg_hw_txrx_clear(priv);
}
static int hbg_net_set_mac_address(struct net_device *netdev, void *addr)
{
struct hbg_priv *priv = netdev_priv(netdev);
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(netdev, 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 *netdev, int new_mtu)
{
struct hbg_priv *priv = netdev_priv(netdev);
if (netif_running(netdev))
return -EBUSY;
hbg_change_mtu(priv, new_mtu);
WRITE_ONCE(netdev->mtu, new_mtu);
dev_dbg(&priv->pdev->dev,
"change mtu from %u to %u\n", netdev->mtu, new_mtu);
return 0;
}
static void hbg_net_tx_timeout(struct net_device *netdev, unsigned int txqueue)
{
struct hbg_priv *priv = netdev_priv(netdev);
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(netdev, "%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)
{
int ret;
ret = hbg_hw_event_notify(priv, HBG_HW_EVENT_INIT);
if (ret)
return ret;
ret = hbg_hw_init(priv);
if (ret)
return ret;
ret = hbg_irq_init(priv);
if (ret)
return ret;
return hbg_mdio_init(priv);
}
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(dev, sizeof(struct hbg_priv));
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 = hbg_init(priv);
if (ret)
return ret;
netdev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
netdev->max_mtu = priv->dev_specs.max_mtu;
netdev->min_mtu = priv->dev_specs.min_mtu;
netdev->netdev_ops = &hbg_netdev_ops;
netdev->watchdog_timeo = 5 * HZ;
hbg_change_mtu(priv, ETH_DATA_LEN);
hbg_net_set_mac_address(priv->netdev, &priv->dev_specs.mac_addr);
hbg_ethtool_set_ops(netdev);
ret = devm_register_netdev(dev, netdev);
if (ret)
return dev_err_probe(dev, ret, "failed to register netdev\n");
netif_carrier_off(netdev);
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");

View File

@ -0,0 +1,222 @@
// 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
#define HBG_MDIO_OP_TIMEOUT_US (1 * 1000 * 1000)
#define HBG_MDIO_OP_INTERVAL_US (5 * 1000)
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_wait_ready(struct hbg_mac *mac)
{
struct hbg_priv *priv = HBG_MAC_GET_PRIV(mac);
u32 cmd = 0;
int ret;
ret = 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);
return ret ? -ETIMEDOUT : 0;
}
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_wait_ready(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_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);
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 void 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);
}
static void hbg_phy_adjust_link(struct net_device *netdev)
{
struct hbg_priv *priv = netdev_priv(netdev);
struct phy_device *phydev = netdev->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;
int ret;
ret = phy_connect_direct(priv->netdev, 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, 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);
}

View File

@ -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

View File

@ -0,0 +1,143 @@
/* 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
/* 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_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_CTRL_ADDR (HBG_REG_SGMII_BASE + 0x0060)
#define HBG_REG_TRANSMIT_CTRL_PAD_EN_B BIT(7)
#define HBG_REG_TRANSMIT_CTRL_CRC_ADD_B BIT(6)
#define HBG_REG_TRANSMIT_CTRL_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_CTRL_ADDR (HBG_REG_SGMII_BASE + 0x01E0)
#define HBG_REG_RECV_CTRL_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)
#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_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_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)
#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)
#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)
#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 */
HBG_PORT_MODE_SGMII_10M = 0x6,
HBG_PORT_MODE_SGMII_100M = 0x7,
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)
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

View File

@ -0,0 +1,409 @@
// SPDX-License-Identifier: GPL-2.0+
// Copyright (c) 2024 Hisilicon Limited.
#include <net/netdev_queues.h>
#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_used_num(head, tail, ring) ({ \
typeof(ring) _ring = (ring); \
((tail) + _ring->len - (head)) % _ring->len; })
#define hbg_queue_left_num(head, tail, ring) ({ \
typeof(ring) _r = (ring); \
_r->len - hbg_queue_used_num((head), (tail), _r) - 1; })
#define hbg_queue_is_empty(head, tail, ring) \
(hbg_queue_used_num((head), (tail), (ring)) == 0)
#define hbg_queue_is_full(head, tail, ring) \
(hbg_queue_left_num((head), (tail), (ring)) == 0)
#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); })
#define HBG_TX_STOP_THRS 2
#define HBG_TX_START_THRS (2 * HBG_TX_STOP_THRS)
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 *netdev)
{
struct hbg_ring *ring = netdev_get_tx_ring(netdev);
struct hbg_priv *priv = netdev_priv(netdev);
/* This smp_load_acquire() pairs with smp_store_release() in
* hbg_napi_tx_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(!skb->len ||
skb->len > hbg_spec_max_frame_len(priv, HBG_DIR_TX))) {
dev_kfree_skb_any(skb);
netdev->stats.tx_errors++;
return NETDEV_TX_OK;
}
if (!netif_subqueue_maybe_stop(netdev, 0,
hbg_queue_left_num(ntc, ntu, ring),
HBG_TX_STOP_THRS, HBG_TX_START_THRS))
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_napi_tx_recycle() called in tx interrupt handle process.
*/
smp_store_release(&ring->ntu, hbg_queue_next_prt(ntu, ring));
dev_sw_netstats_tx_add(netdev, 1, skb->len);
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 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);
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_net_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;
/* We need do cleanup even if budget is 0.
* Per NAPI documentation budget is for Rx.
* So We hardcode the amount of work Tx NAPI does to 128.
*/
budget = 128;
while (packet_done < budget) {
if (unlikely(hbg_queue_is_empty(ntc, ntu, ring)))
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_net_start_xmit() called in xmit process.
*/
smp_store_release(&ring->ntc, ntc);
netif_wake_queue(priv->netdev);
if (likely(packet_done < budget &&
napi_complete_done(napi, packet_done)))
hbg_hw_irq_enable(priv, HBG_INT_MSK_TX_B, true);
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 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;
while (packet_done < budget) {
if (unlikely(hbg_queue_is_empty(ring->ntc, ring->ntu, ring)))
break;
buffer = &ring->queue[ring->ntc];
if (unlikely(!buffer->skb))
goto next_buffer;
if (unlikely(!hbg_sync_data_from_hw(priv, buffer)))
break;
rx_desc = (struct hbg_rx_desc *)buffer->skb->data;
pkt_len = FIELD_GET(HBG_RX_DESC_W2_PKT_LEN_M, rx_desc->word2);
hbg_dma_unmap(buffer);
skb_reserve(buffer->skb, HBG_PACKET_HEAD_SIZE + NET_IP_ALIGN);
skb_put(buffer->skb, pkt_len);
buffer->skb->protocol = eth_type_trans(buffer->skb,
priv->netdev);
dev_sw_netstats_rx_add(priv->netdev, pkt_len);
napi_gro_receive(napi, buffer->skb);
buffer->skb = NULL;
next_buffer:
hbg_rx_fill_one_buffer(priv);
hbg_queue_move_next(ntc, ring);
packet_done++;
}
if (likely(packet_done < budget &&
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;
u32 i;
if (!ring->queue)
return;
napi_disable(&ring->napi);
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,
int (*napi_poll)(struct napi_struct *, int),
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;
if (dir == HBG_DIR_TX)
netif_napi_add_tx(priv->netdev, &ring->napi, napi_poll);
else
netif_napi_add(priv->netdev, &ring->napi, napi_poll);
napi_enable(&ring->napi);
return 0;
}
static int hbg_tx_ring_init(struct hbg_priv *priv)
{
struct hbg_ring *tx_ring = &priv->tx_ring;
if (!tx_ring->tout_log_buf)
tx_ring->tout_log_buf = devm_kmalloc(&priv->pdev->dev,
HBG_TX_TIMEOUT_BUF_LEN,
GFP_KERNEL);
if (!tx_ring->tout_log_buf)
return -ENOMEM;
return hbg_ring_init(priv, tx_ring, hbg_napi_tx_recycle, HBG_DIR_TX);
}
static int hbg_rx_ring_init(struct hbg_priv *priv)
{
int ret;
u32 i;
ret = hbg_ring_init(priv, &priv->rx_ring, hbg_napi_rx_poll, HBG_DIR_RX);
if (ret)
return ret;
for (i = 0; i < priv->rx_ring.len - 1; i++) {
ret = hbg_rx_fill_one_buffer(priv);
if (ret) {
hbg_ring_uninit(&priv->rx_ring);
return ret;
}
}
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;
}
ret = hbg_rx_ring_init(priv);
if (ret) {
dev_err(&priv->pdev->dev,
"failed to init rx ring, ret = %d\n", ret);
hbg_ring_uninit(&priv->tx_ring);
}
return ret;
}
void hbg_txrx_uninit(struct hbg_priv *priv)
{
hbg_ring_uninit(&priv->tx_ring);
hbg_ring_uninit(&priv->rx_ring);
}

View File

@ -0,0 +1,39 @@
/* 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 *netdev);
int hbg_txrx_init(struct hbg_priv *priv);
void hbg_txrx_uninit(struct hbg_priv *priv);
#endif