Skip to content

Commit

Permalink
tsnep: Add TSN endpoint Ethernet MAC driver
Browse files Browse the repository at this point in the history
The TSN endpoint Ethernet MAC is a FPGA based network device for
real-time communication.

It is integrated as Ethernet controller with ethtool and PTP support.
For real-time communcation TC_SETUP_QDISC_TAPRIO is supported.

The device supports multiple TX/RX queue pairs. The first TX/RX queue
pair is used by the driver. All other TX/RX queue pairs shall be used
directly by real-time applications. Each TX/RX queue pair has its own
register set in a separate physical page and can be operated without any
driver interaction. This enables the direct use of TX/RX queue pairs by
real-time and/or safety applications running
- on dedicated CPU cores without any operating system
- on dedicated CPU cores with a separate operating system
- in user space without any Linux kernel interaction

The additional TX queues support timed transmission. The TX descriptor
ring contains relative timing information for every Ethernet frame.
Every TX descriptor defines the transmission time of the next frame with
an increment for the current transmission time (it does not define the
transmission time of its own frame). This way, the controller knows in
advance when a frame will be transmitted and can delay the fetching of the
descriptor and the frame as long as possible. Therefore, a frame can be
assigned to a descriptor as late as possible and the data can be as
up-to-date as possible, which is ideal for closed loop control. The
relative timing information of the next frame requires a periodic schedule
for frame transmission which is known in advance. This is usually the case
for time triggered real-time systems. E.g., for EtherCAT this mode of
timed transmission is used since 2011.

This driver provides a driver specific interface in tsnep_stream.c for
direct access to all but the first TX/RX queue pair. There are two
reasons for this interface. First: It enables the reservation or direct use
of TX/RX queue pairs by real-time application on dedicated CPU cores or
in user space. Second: The periodic schedule for timed transmission does
not fit to TC_SETUP_QDISC_ETF. ETF supports absolute transmission times
which are not known in advance and that is in stark contrast to relative
transmission times which are known in advance. Therefore, ETF cannot be
implemented with this controller. Additionally, this interface is used
for hardware testing.

Signed-off-by: Gerhard Engleder <gerhard@engleder-embedded.com>
  • Loading branch information
Gerhard Engleder authored and intel-lab-lkp committed Jul 26, 2021
1 parent c361df7 commit baa5afa
Show file tree
Hide file tree
Showing 12 changed files with 4,186 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/net/ethernet/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ config DNET
source "drivers/net/ethernet/dec/Kconfig"
source "drivers/net/ethernet/dlink/Kconfig"
source "drivers/net/ethernet/emulex/Kconfig"
source "drivers/net/ethernet/engleder/Kconfig"
source "drivers/net/ethernet/ezchip/Kconfig"
source "drivers/net/ethernet/faraday/Kconfig"
source "drivers/net/ethernet/freescale/Kconfig"
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ obj-$(CONFIG_DNET) += dnet.o
obj-$(CONFIG_NET_VENDOR_DEC) += dec/
obj-$(CONFIG_NET_VENDOR_DLINK) += dlink/
obj-$(CONFIG_NET_VENDOR_EMULEX) += emulex/
obj-$(CONFIG_NET_VENDOR_ENGLEDER) += engleder/
obj-$(CONFIG_NET_VENDOR_EZCHIP) += ezchip/
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
Expand Down
28 changes: 28 additions & 0 deletions drivers/net/ethernet/engleder/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# SPDX-License-Identifier: GPL-2.0
#
# Engleder network device configuration
#

config NET_VENDOR_ENGLEDER
bool "Engleder devices"
default y
help
If you have a network (Ethernet) card belonging to this class, say Y.

Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about Engleder devices. If you say Y, you will be asked
for your specific card in the following questions.

if NET_VENDOR_ENGLEDER

config TSNEP
tristate "TSN endpoint support"
select PHYLIB
help
Support for the Engleder TSN endpoint Ethernet MAC IP Core.

To compile this driver as a module, choose M here. The module will be
called tsnep.

endif # NET_VENDOR_ENGLEDER
9 changes: 9 additions & 0 deletions drivers/net/ethernet/engleder/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Engleder Ethernet drivers
#

obj-$(CONFIG_TSNEP) += tsnep.o

tsnep-objs := tsnep_main.o tsnep_ethtool.o tsnep_ptp.o tsnep_tc.o \
tsnep_stream.o tsnep_test.o
199 changes: 199 additions & 0 deletions drivers/net/ethernet/engleder/tsnep.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */

#ifndef _TSNEP_H
#define _TSNEP_H

#include "tsnep_hw.h"

#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/phy.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/miscdevice.h>

#define TSNEP "tsnep"

#define TSNEP_RING_SIZE 256
#define TSNEP_RING_ENTRIES_PER_PAGE (PAGE_SIZE / TSNEP_DESC_SIZE)
#define TSNEP_RING_PAGE_COUNT (TSNEP_RING_SIZE / TSNEP_RING_ENTRIES_PER_PAGE)

#define TSNEP_QUEUES 1
#define TSNEP_MAX_STREAMS 7

struct tsnep_stream {
struct tsnep_adapter *adapter;
phys_addr_t addr;
bool in_use;

/* DMA buffer */
struct mutex dma_buffer_lock;
struct rb_root dma_buffer;
};

struct tsnep_gcl {
void *addr;

u64 base_time;
u64 cycle_time;
u64 cycle_time_extension;

struct tsnep_gcl_operation operation[TSNEP_GCL_COUNT];
int count;

u64 change_limit;

u64 start_time;
bool change;
};

struct tsnep_tx_entry {
struct tsnep_tx_desc *desc;
struct tsnep_tx_desc_wb *desc_wb;
dma_addr_t desc_dma;
bool owner_user_flag;

u32 properties;

struct sk_buff *skb;
DEFINE_DMA_UNMAP_ADDR(dma);
DEFINE_DMA_UNMAP_LEN(len);
};

struct tsnep_tx {
struct tsnep_adapter *adapter;
void *addr;

void *page[TSNEP_RING_PAGE_COUNT];
dma_addr_t page_dma[TSNEP_RING_PAGE_COUNT];

/* TX ring lock */
spinlock_t lock;
struct tsnep_tx_entry entry[TSNEP_RING_SIZE];
int write;
int read;
u32 owner_counter;
int increment_owner_counter;

u32 packets;
u32 bytes;
u32 dropped;
};

struct tsnep_rx_entry {
struct tsnep_rx_desc *desc;
struct tsnep_rx_desc_wb *desc_wb;
dma_addr_t desc_dma;

u32 properties;

struct sk_buff *skb;
DEFINE_DMA_UNMAP_ADDR(dma);
DEFINE_DMA_UNMAP_LEN(len);
};

struct tsnep_rx {
struct tsnep_adapter *adapter;
void *addr;

void *page[TSNEP_RING_PAGE_COUNT];
dma_addr_t page_dma[TSNEP_RING_PAGE_COUNT];

struct tsnep_rx_entry entry[TSNEP_RING_SIZE];
int read;
u32 owner_counter;
int increment_owner_counter;

u32 packets;
u32 bytes;
u32 dropped;
u32 multicast;
};

struct tsnep_adapter {
struct net_device *netdev;
u8 mac_address[ETH_ALEN];
struct mii_bus *mdiobus;
phy_interface_t phy_mode;
struct phy_device *phydev;
int msg_enable;
bool loopback;
int loopback_speed;

struct platform_device *pdev;
void *addr;
unsigned long size;
int irq;
bool gmii2rgmii;

/* interrupt enable lock */
spinlock_t irq_lock;
u32 irq_enable;

/* management data lock */
struct mutex md_lock;
wait_queue_head_t md_wait;
bool md_active;

bool gate_control;
/* gate control lock */
struct mutex gate_control_lock;
bool gate_control_active;
struct tsnep_gcl gcl[2];
int next_gcl;

struct hwtstamp_config hwtstamp_config;
struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_clock_info;
/* ptp clock lock */
spinlock_t ptp_lock;

int num_tx_queues;
struct tsnep_tx tx[TSNEP_MAX_QUEUES];
int num_rx_queues;
struct tsnep_rx rx[TSNEP_MAX_QUEUES];

struct napi_struct napi;

int stream_count;
int index;
char name[16];
struct miscdevice misc;
/* stream char device lock */
struct mutex stream_lock;
struct tsnep_stream stream[TSNEP_MAX_STREAMS];
};

extern const struct ethtool_ops tsnep_ethtool_ops;

int tsnep_ptp_init(struct tsnep_adapter *adapter);
void tsnep_ptp_cleanup(struct tsnep_adapter *adapter);
int tsnep_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);

int tsnep_tc_init(struct tsnep_adapter *adapter);
void tsnep_tc_cleanup(struct tsnep_adapter *adapter);
int tsnep_tc_setup(struct net_device *netdev, enum tc_setup_type type,
void *type_data);

int tsnep_stream_init(struct tsnep_adapter *adapter);
void tsnep_stream_cleanup(struct tsnep_adapter *adapter);

int tsnep_get_test_count(void);
void tsnep_get_test_strings(u8 *data);
void tsnep_self_test(struct net_device *netdev, struct ethtool_test *eth_test,
u64 *data);

int tsnep_read_md(struct tsnep_adapter *adapter, int phy, int reg, u16 *data);
int tsnep_write_md(struct tsnep_adapter *adapter, int phy, int reg, u16 data);
int tsnep_enable_loopback(struct tsnep_adapter *adapter, int speed);
int tsnep_disable_loopback(struct tsnep_adapter *adapter);

void tsnep_enable_irq(struct tsnep_adapter *adapter, u32 mask);
void tsnep_disable_irq(struct tsnep_adapter *adapter, u32 mask);

void tsnep_get_system_time(struct tsnep_adapter *adapter, u64 *time);

#endif /* _TSNEP_H */
Loading

0 comments on commit baa5afa

Please sign in to comment.