Skip to content

Commit

Permalink
Add Mellanox BlueField Gigabit Ethernet driver
Browse files Browse the repository at this point in the history
This patch adds build and driver logic for the "mlxbf_gige"
Ethernet driver from Mellanox Technologies. The second
generation BlueField SoC from Mellanox supports an
out-of-band GigaBit Ethernet management port to the Arm
subsystem.  This driver supports TCP/IP network connectivity
for that port, and provides back-end routines to handle
basic ethtool requests.

The driver interfaces to the Gigabit Ethernet block of
BlueField SoC via MMIO accesses to registers, which contain
control information or pointers describing transmit and
receive resources.  There is a single transmit queue, and
the port supports transmit ring sizes of 4 to 256 entries.
There is a single receive queue, and the port supports
receive ring sizes of 32 to 32K entries. The transmit and
receive rings are allocated from DMA coherent memory. There
is a 16-bit producer and consumer index per ring to denote
software ownership and hardware ownership, respectively.

The main driver logic such as probe(), remove(), and netdev
ops are in "mlxbf_gige_main.c".  Logic in "mlxbf_gige_rx.c"
and "mlxbf_gige_tx.c" handles the packet processing for
receive and transmit respectively.

The logic in "mlxbf_gige_ethtool.c" supports the handling
of some basic ethtool requests: get driver info, get ring
parameters, get registers, and get statistics.

The logic in "mlxbf_gige_mdio.c" is the driver controlling
the Mellanox BlueField hardware that interacts with a PHY
device via MDIO/MDC pins.  This driver does the following:
  - At driver probe time, it configures several BlueField MDIO
    parameters such as sample rate, full drive, voltage and MDC
  - It defines functions to read and write MDIO registers and
    registers the MDIO bus.
  - It defines the phy interrupt handler reporting a
    link up/down status change
  - This driver's probe is invoked from the main driver logic
    while the phy interrupt handler is registered in ndo_open.

Driver limitations
  - Only supports 1Gbps speed
  - Only supports GMII protocol
  - Supports maximum packet size of 2KB
  - Does not support scatter-gather buffering

Testing
  - Successful build of kernel for ARM64, ARM32, X86_64
  - Tested ARM64 build on FastModels & Palladium
  - Tested ARM64 build on several Mellanox boards that are built with
    the BlueField-2 SoC.  The testing includes coverage in the areas
    of networking (e.g. ping, iperf, ifconfig, route), file transfers
    (e.g. SCP), and various ethtool options relevant to this driver.

Signed-off-by: David Thompson <[email protected]>
Signed-off-by: Asmaa Mnebhi <[email protected]>
Reviewed-by: Liming Sun <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
dthompso authored and davem330 committed Jun 25, 2021
1 parent 19938ba commit f92e186
Show file tree
Hide file tree
Showing 13 changed files with 2,028 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/net/ethernet/mellanox/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ source "drivers/net/ethernet/mellanox/mlx4/Kconfig"
source "drivers/net/ethernet/mellanox/mlx5/core/Kconfig"
source "drivers/net/ethernet/mellanox/mlxsw/Kconfig"
source "drivers/net/ethernet/mellanox/mlxfw/Kconfig"
source "drivers/net/ethernet/mellanox/mlxbf_gige/Kconfig"

endif # NET_VENDOR_MELLANOX
1 change: 1 addition & 0 deletions drivers/net/ethernet/mellanox/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ obj-$(CONFIG_MLX4_CORE) += mlx4/
obj-$(CONFIG_MLX5_CORE) += mlx5/core/
obj-$(CONFIG_MLXSW_CORE) += mlxsw/
obj-$(CONFIG_MLXFW) += mlxfw/
obj-$(CONFIG_MLXBF_GIGE) += mlxbf_gige/
13 changes: 13 additions & 0 deletions drivers/net/ethernet/mellanox/mlxbf_gige/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
#
# Mellanox GigE driver configuration
#

config MLXBF_GIGE
tristate "Mellanox Technologies BlueField Gigabit Ethernet support"
depends on (ARM64 && ACPI) || COMPILE_TEST
select PHYLIB
help
The second generation BlueField SoC from Mellanox Technologies
supports an out-of-band Gigabit Ethernet management port to the
Arm subsystem.
11 changes: 11 additions & 0 deletions drivers/net/ethernet/mellanox/mlxbf_gige/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause

obj-$(CONFIG_MLXBF_GIGE) += mlxbf_gige.o

mlxbf_gige-y := mlxbf_gige_ethtool.o \
mlxbf_gige_gpio.o \
mlxbf_gige_intr.o \
mlxbf_gige_main.o \
mlxbf_gige_mdio.o \
mlxbf_gige_rx.o \
mlxbf_gige_tx.o
190 changes: 190 additions & 0 deletions drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/* SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause */

/* Header file for Gigabit Ethernet driver for Mellanox BlueField SoC
* - this file contains software data structures and any chip-specific
* data structures (e.g. TX WQE format) that are memory resident.
*
* Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES
*/

#ifndef __MLXBF_GIGE_H__
#define __MLXBF_GIGE_H__

#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/irqreturn.h>
#include <linux/netdevice.h>
#include <linux/irq.h>

/* The silicon design supports a maximum RX ring size of
* 32K entries. Based on current testing this maximum size
* is not required to be supported. Instead the RX ring
* will be capped at a realistic value of 1024 entries.
*/
#define MLXBF_GIGE_MIN_RXQ_SZ 32
#define MLXBF_GIGE_MAX_RXQ_SZ 1024
#define MLXBF_GIGE_DEFAULT_RXQ_SZ 128

#define MLXBF_GIGE_MIN_TXQ_SZ 4
#define MLXBF_GIGE_MAX_TXQ_SZ 256
#define MLXBF_GIGE_DEFAULT_TXQ_SZ 128

#define MLXBF_GIGE_DEFAULT_BUF_SZ 2048

#define MLXBF_GIGE_DMA_PAGE_SZ 4096
#define MLXBF_GIGE_DMA_PAGE_SHIFT 12

/* There are four individual MAC RX filters. Currently
* two of them are being used: one for the broadcast MAC
* (index 0) and one for local MAC (index 1)
*/
#define MLXBF_GIGE_BCAST_MAC_FILTER_IDX 0
#define MLXBF_GIGE_LOCAL_MAC_FILTER_IDX 1

/* Define for broadcast MAC literal */
#define BCAST_MAC_ADDR 0xFFFFFFFFFFFF

/* There are three individual interrupts:
* 1) Errors, "OOB" interrupt line
* 2) Receive Packet, "OOB_LLU" interrupt line
* 3) LLU and PLU Events, "OOB_PLU" interrupt line
*/
#define MLXBF_GIGE_ERROR_INTR_IDX 0
#define MLXBF_GIGE_RECEIVE_PKT_INTR_IDX 1
#define MLXBF_GIGE_LLU_PLU_INTR_IDX 2
#define MLXBF_GIGE_PHY_INT_N 3

#define MLXBF_GIGE_MDIO_DEFAULT_PHY_ADDR 0x3

#define MLXBF_GIGE_DEFAULT_PHY_INT_GPIO 12

struct mlxbf_gige_stats {
u64 hw_access_errors;
u64 tx_invalid_checksums;
u64 tx_small_frames;
u64 tx_index_errors;
u64 sw_config_errors;
u64 sw_access_errors;
u64 rx_truncate_errors;
u64 rx_mac_errors;
u64 rx_din_dropped_pkts;
u64 tx_fifo_full;
u64 rx_filter_passed_pkts;
u64 rx_filter_discard_pkts;
};

struct mlxbf_gige {
void __iomem *base;
void __iomem *llu_base;
void __iomem *plu_base;
struct device *dev;
struct net_device *netdev;
struct platform_device *pdev;
void __iomem *mdio_io;
struct mii_bus *mdiobus;
void __iomem *gpio_io;
struct irq_domain *irqdomain;
u32 phy_int_gpio_mask;
spinlock_t lock; /* for packet processing indices */
spinlock_t gpio_lock; /* for GPIO bus access */
u16 rx_q_entries;
u16 tx_q_entries;
u64 *tx_wqe_base;
dma_addr_t tx_wqe_base_dma;
u64 *tx_wqe_next;
u64 *tx_cc;
dma_addr_t tx_cc_dma;
dma_addr_t *rx_wqe_base;
dma_addr_t rx_wqe_base_dma;
u64 *rx_cqe_base;
dma_addr_t rx_cqe_base_dma;
u16 tx_pi;
u16 prev_tx_ci;
u64 error_intr_count;
u64 rx_intr_count;
u64 llu_plu_intr_count;
struct sk_buff *rx_skb[MLXBF_GIGE_MAX_RXQ_SZ];
struct sk_buff *tx_skb[MLXBF_GIGE_MAX_TXQ_SZ];
int error_irq;
int rx_irq;
int llu_plu_irq;
int phy_irq;
int hw_phy_irq;
bool promisc_enabled;
u8 valid_polarity;
struct napi_struct napi;
struct mlxbf_gige_stats stats;
};

/* Rx Work Queue Element definitions */
#define MLXBF_GIGE_RX_WQE_SZ 8

/* Rx Completion Queue Element definitions */
#define MLXBF_GIGE_RX_CQE_SZ 8
#define MLXBF_GIGE_RX_CQE_PKT_LEN_MASK GENMASK(10, 0)
#define MLXBF_GIGE_RX_CQE_VALID_MASK GENMASK(11, 11)
#define MLXBF_GIGE_RX_CQE_PKT_STATUS_MASK GENMASK(15, 12)
#define MLXBF_GIGE_RX_CQE_PKT_STATUS_MAC_ERR GENMASK(12, 12)
#define MLXBF_GIGE_RX_CQE_PKT_STATUS_TRUNCATED GENMASK(13, 13)
#define MLXBF_GIGE_RX_CQE_CHKSUM_MASK GENMASK(31, 16)

/* Tx Work Queue Element definitions */
#define MLXBF_GIGE_TX_WQE_SZ_QWORDS 2
#define MLXBF_GIGE_TX_WQE_SZ 16
#define MLXBF_GIGE_TX_WQE_PKT_LEN_MASK GENMASK(10, 0)
#define MLXBF_GIGE_TX_WQE_UPDATE_MASK GENMASK(31, 31)
#define MLXBF_GIGE_TX_WQE_CHKSUM_LEN_MASK GENMASK(42, 32)
#define MLXBF_GIGE_TX_WQE_CHKSUM_START_MASK GENMASK(55, 48)
#define MLXBF_GIGE_TX_WQE_CHKSUM_OFFSET_MASK GENMASK(63, 56)

/* Macro to return packet length of specified TX WQE */
#define MLXBF_GIGE_TX_WQE_PKT_LEN(tx_wqe_addr) \
(*((tx_wqe_addr) + 1) & MLXBF_GIGE_TX_WQE_PKT_LEN_MASK)

/* Tx Completion Count */
#define MLXBF_GIGE_TX_CC_SZ 8

/* List of resources in ACPI table */
enum mlxbf_gige_res {
MLXBF_GIGE_RES_MAC,
MLXBF_GIGE_RES_MDIO9,
MLXBF_GIGE_RES_GPIO0,
MLXBF_GIGE_RES_LLU,
MLXBF_GIGE_RES_PLU
};

/* Version of register data returned by mlxbf_gige_get_regs() */
#define MLXBF_GIGE_REGS_VERSION 1

int mlxbf_gige_mdio_probe(struct platform_device *pdev,
struct mlxbf_gige *priv);
void mlxbf_gige_mdio_remove(struct mlxbf_gige *priv);
irqreturn_t mlxbf_gige_mdio_handle_phy_interrupt(int irq, void *dev_id);
void mlxbf_gige_mdio_enable_phy_int(struct mlxbf_gige *priv);

void mlxbf_gige_set_mac_rx_filter(struct mlxbf_gige *priv,
unsigned int index, u64 dmac);
void mlxbf_gige_get_mac_rx_filter(struct mlxbf_gige *priv,
unsigned int index, u64 *dmac);
void mlxbf_gige_enable_promisc(struct mlxbf_gige *priv);
void mlxbf_gige_disable_promisc(struct mlxbf_gige *priv);
int mlxbf_gige_rx_init(struct mlxbf_gige *priv);
void mlxbf_gige_rx_deinit(struct mlxbf_gige *priv);
int mlxbf_gige_tx_init(struct mlxbf_gige *priv);
void mlxbf_gige_tx_deinit(struct mlxbf_gige *priv);
bool mlxbf_gige_handle_tx_complete(struct mlxbf_gige *priv);
netdev_tx_t mlxbf_gige_start_xmit(struct sk_buff *skb,
struct net_device *netdev);
struct sk_buff *mlxbf_gige_alloc_skb(struct mlxbf_gige *priv,
unsigned int map_len,
dma_addr_t *buf_dma,
enum dma_data_direction dir);
int mlxbf_gige_request_irqs(struct mlxbf_gige *priv);
void mlxbf_gige_free_irqs(struct mlxbf_gige *priv);
int mlxbf_gige_poll(struct napi_struct *napi, int budget);
extern const struct ethtool_ops mlxbf_gige_ethtool_ops;
void mlxbf_gige_update_tx_wqe_next(struct mlxbf_gige *priv);

int mlxbf_gige_gpio_init(struct platform_device *pdev, struct mlxbf_gige *priv);
void mlxbf_gige_gpio_free(struct mlxbf_gige *priv);

#endif /* !defined(__MLXBF_GIGE_H__) */
137 changes: 137 additions & 0 deletions drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_ethtool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause

/* Ethtool support for Mellanox Gigabit Ethernet driver
*
* Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES
*/

#include <linux/phy.h>

#include "mlxbf_gige.h"
#include "mlxbf_gige_regs.h"

/* Start of struct ethtool_ops functions */
static int mlxbf_gige_get_regs_len(struct net_device *netdev)
{
return MLXBF_GIGE_MMIO_REG_SZ;
}

static void mlxbf_gige_get_regs(struct net_device *netdev,
struct ethtool_regs *regs, void *p)
{
struct mlxbf_gige *priv = netdev_priv(netdev);

regs->version = MLXBF_GIGE_REGS_VERSION;

/* Read entire MMIO register space and store results
* into the provided buffer. Each 64-bit word is converted
* to big-endian to make the output more readable.
*
* NOTE: by design, a read to an offset without an existing
* register will be acknowledged and return zero.
*/
memcpy_fromio(p, priv->base, MLXBF_GIGE_MMIO_REG_SZ);
}

static void mlxbf_gige_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ering)
{
struct mlxbf_gige *priv = netdev_priv(netdev);

ering->rx_max_pending = MLXBF_GIGE_MAX_RXQ_SZ;
ering->tx_max_pending = MLXBF_GIGE_MAX_TXQ_SZ;
ering->rx_pending = priv->rx_q_entries;
ering->tx_pending = priv->tx_q_entries;
}

static const struct {
const char string[ETH_GSTRING_LEN];
} mlxbf_gige_ethtool_stats_keys[] = {
{ "hw_access_errors" },
{ "tx_invalid_checksums" },
{ "tx_small_frames" },
{ "tx_index_errors" },
{ "sw_config_errors" },
{ "sw_access_errors" },
{ "rx_truncate_errors" },
{ "rx_mac_errors" },
{ "rx_din_dropped_pkts" },
{ "tx_fifo_full" },
{ "rx_filter_passed_pkts" },
{ "rx_filter_discard_pkts" },
};

static int mlxbf_gige_get_sset_count(struct net_device *netdev, int stringset)
{
if (stringset != ETH_SS_STATS)
return -EOPNOTSUPP;
return ARRAY_SIZE(mlxbf_gige_ethtool_stats_keys);
}

static void mlxbf_gige_get_strings(struct net_device *netdev, u32 stringset,
u8 *buf)
{
if (stringset != ETH_SS_STATS)
return;
memcpy(buf, &mlxbf_gige_ethtool_stats_keys,
sizeof(mlxbf_gige_ethtool_stats_keys));
}

static void mlxbf_gige_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *estats,
u64 *data)
{
struct mlxbf_gige *priv = netdev_priv(netdev);

/* Fill data array with interface statistics
*
* NOTE: the data writes must be in
* sync with the strings shown in
* the mlxbf_gige_ethtool_stats_keys[] array
*
* NOTE2: certain statistics below are zeroed upon
* port disable, so the calculation below
* must include the "cached" value of the stat
* plus the value read directly from hardware.
* Cached statistics are currently:
* rx_din_dropped_pkts
* rx_filter_passed_pkts
* rx_filter_discard_pkts
*/
*data++ = priv->stats.hw_access_errors;
*data++ = priv->stats.tx_invalid_checksums;
*data++ = priv->stats.tx_small_frames;
*data++ = priv->stats.tx_index_errors;
*data++ = priv->stats.sw_config_errors;
*data++ = priv->stats.sw_access_errors;
*data++ = priv->stats.rx_truncate_errors;
*data++ = priv->stats.rx_mac_errors;
*data++ = (priv->stats.rx_din_dropped_pkts +
readq(priv->base + MLXBF_GIGE_RX_DIN_DROP_COUNTER));
*data++ = priv->stats.tx_fifo_full;
*data++ = (priv->stats.rx_filter_passed_pkts +
readq(priv->base + MLXBF_GIGE_RX_PASS_COUNTER_ALL));
*data++ = (priv->stats.rx_filter_discard_pkts +
readq(priv->base + MLXBF_GIGE_RX_DISC_COUNTER_ALL));
}

static void mlxbf_gige_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
pause->autoneg = AUTONEG_DISABLE;
pause->rx_pause = 1;
pause->tx_pause = 1;
}

const struct ethtool_ops mlxbf_gige_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_ringparam = mlxbf_gige_get_ringparam,
.get_regs_len = mlxbf_gige_get_regs_len,
.get_regs = mlxbf_gige_get_regs,
.get_strings = mlxbf_gige_get_strings,
.get_sset_count = mlxbf_gige_get_sset_count,
.get_ethtool_stats = mlxbf_gige_get_ethtool_stats,
.nway_reset = phy_ethtool_nway_reset,
.get_pauseparam = mlxbf_gige_get_pauseparam,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
};
Loading

0 comments on commit f92e186

Please sign in to comment.