forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tsnep: Add TSN endpoint Ethernet MAC driver
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
1 parent
c361df7
commit baa5afa
Showing
12 changed files
with
4,186 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 */ |
Oops, something went wrong.