Introduce a new network driver for the AMD LogiCORE 100M/1G TSN
Subsystem IP, also known as the TSN Endpoint Ethernet MAC IP, which
implements IEEE 802.1 Time-Sensitive Networking (TSN) features for
deterministic and low-latency Ethernet communication in real-time and
industrial automation use cases.
The v1 posting sent all 20 patches (wrapper, endpoint and the DSA switch)
as one series. Following the review feedback to split it, this v2 sends
only the wrapper and endpoint side (patches 1-8). The DSA switch driver,
tag protocol, MDIO, phylink, PTP and bridge offloads will follow as a
separate series once this base lands.
IP Core Overview:
The AMD LogiCORE 100M/1G TSN Subsystem IP solution (named as TSN Endpoint
Ethernet MAC IP in the IP catalog) implements IEEE 802.1 Time Sensitive
Networking (TSN) Standards and provides a low latency Bridged Endpoint or
Endpoint only solutions. The bridged endpoint solution consists of a 3-port
switch that connects to an endpoint including Linux software drivers. For
Bridged Endpoint (Switch Endpoint), two ports connects to the network and
one port connects to an internal Endpoint. It supports the use of
GMII/RGMII interfaces connecting to a physical-side interface (PHY) chip
with full duplex 100 Mb/s and 1 Gb/s operations.
Features:
- IP Customizable to generate TSN Endpoint or Bridged Endpoint solution
- Supports interface to network software stack
- Designed to comply with the following IEEE standards
- 802.1AS : Precision Time Protocol (PTP) synchronization
- 802.1Qav : Credit-Based Shaper for traffic shaping
- 802.1Qbv : Time-Aware Shaper for scheduled traffic
- 802.1Qbu : Frame Preemption for ultra-low latency
- 802.1CB : Frame Replication and Elimination for redundancy
- 802.1Qcc : Stream Reservation Protocol (SRP)
- IP is customizable to enable various TSN feature-sets
- Supports 2 to 8 priority queues for scheduled, reserved (IEEE 802.1 Qav
Credit Based Shaper) and best effort traffic classes
- Provides feature rich Ethernet Switch that caters to various network
needs
* 3-port Switch (2-external, 1-internal)
* Programmable cut-through and store-forward operations
* 4-port Switch (2-external, 2-internal) extension through
'Endpoint Extension' and 'Endpoint Packet Switching' features
- Solution validated on ZCU102 & ZC702 AMD Evaluation boards.
Sample hardware architecture diagram for Bidge End Point like below:
+------------------+
| MCDMA |
+---------+--------+
Q0---Q7
|
+------------------------------------------------------------ +
| | TSN sub system(Bridge End Point) |
| | |
| +------+----+ Port 0 +-----------------------+ |
| | EndPoint |<--------->| TSN Switch | |
| | (EP) | +----+-------------+----+ |
| +-----------+ | | |
| | | |
| Port 1 Port 2 |
| | | |
| +-----------+ +-----------+ |
| | MAC-1 | | MAC-2 | |
| | (ETH1) | | (ETH2) | |
| +-----+-----+ +-----+-----+ |
| | | |
| | | |
+-------------------------------------------------------------+
| |
RGMII RGMII
| |
+-----------+ +-----------+
| PHY1 | | PHY2 |
| (Port 0) | | (Port 2) |
+-----------+ +-----------+
Software Driver Overview:
This series adds the wrapper and endpoint side of the IP. The switch-facing
part is exposed through the Distributed Switch Architecture (DSA) framework
in a follow-up series, so that MAC1 and MAC2 show up to Linux as switch
ports with the endpoint acting as the CPU/conduit port. The endpoint frames
are already classified by source-port id and tagged with DSA port metadata
here, so the switch series can consume them without further endpoint
change.
Wrapper driver:
- Binds the top-level IP node, acquires and enables the IP-block clocks.
- Spawns the endpoint (and, later, switch) blocks as child platform
devices via devm_of_platform_populate() and carries no netdev or
register access of its own.
Endpoint driver:
- Implements the conduit net_device that Linux DMA traffic flows through
on its way to and from the switch fabric.
- Manages multi-queue TX and RX descriptor rings using the dmaengine
framework for high-performance packet transfer.
- Classifies each received frame by the source-port id carried in the RX
descriptor sideband metadata and attaches DSA port metadata so the DSA
core can demux it to the right user port netdev.
Changes in v2:
- Split the original 20-patch series. This v2 carries only the wrapper and
endpoint (patches 1-8). The DSA switch driver, tag protocol and offloads
will be posted as a separate follow-up series.
- binding: describe only the Bridged Endpoint configuration, drop reg from
the wrapper node (it has none of its own, ranges covers the child
windows), rename ep-mac@ to ethernet@ and mdio-mac1/2 to mdio-1/2, cap
the DMA channels at 8 (dma-names maxItems 16, tx_chan/rx_chan [0-7],
dma-channel-num max 7), use a ports/ethernet-ports container and require
port@0, port@1 and port@2, add reg (enum [1, 2]) and require reg,
phy-mode and phy-handle on the external ports (gmii/rgmii/rgmii-id), and
add minItems on the switch interrupts.
- Kconfig: depend on OF_ADDRESS instead of OF && HAS_IOMEM, point the help
text at the companion DSA driver rather than a symbol that does not exist
yet, and drop the mod_devicetable.h include (platform_device.h pulls it
in).
- endpoint: name the netdev ep%d instead of a fixed "ep", drop the mapped
but unused register window, use dev_kfree_skb_any() on the drop path, and
drop the mod_devicetable.h include.
- endpoint DMA: check xlnx,dma-channel-num against the channels dma-names
lists, require one queue per tx_chan and num-priorities equal to the TX
channel count, cap TX/RX at 8, error out on an unknown dma-names entry,
and drop the unreachable count guards.
- endpoint MCDMA bring-up: request a channel at probe time so -EPROBE_DEFER
stays in the probe path and never reaches ndo_open, and request channels
by their real dma-names index instead of a dense 0..N-1 sequence.
- endpoint RX: serialise the refill against teardown under a per-channel
lock, recover a drained ring with a refill timer instead of stalling,
fail ndo_open if a channel cannot arm any descriptor, take the ring size
from the channel, shut the refill timer down with timer_shutdown_sync()
at teardown, and return true from the refill helper when it arms a
descriptor.
- endpoint TX: take the scatterlist count from skb_to_sgvec(), check
dmaengine_submit() before advancing the ring and charging BQL, publish
the ring head and tail with WRITE_ONCE(), move tx_lock into the
per-channel struct so independent TX queues do not share one lock, use
the spinlock_bh guard class in the completion callback, and use
dmaengine_prep_slave_sg().
- endpoint RX to DSA: attach the port metadata only when the netdev is a
DSA conduit, take a counted dst reference (dst_hold() + skb_dst_set()) so
the dst survives the __netif_rx() backlog handoff without skb_dst_force()
in softirq, and release it with dst_release() at teardown.
Previous versions:
v1 (full 20-patch series, now split):
https://lore.kernel.org/lkml/20260807104431.157230-1-nagadheeraj.rottela@amd.com/
Test information:
- Validated these changes on the net-next branch with the MCDMA metadata
patches cherry-picked from linux-next, along with the series listed below
https://lore.kernel.org/all/20260713072146.45269-1-srinivas.neeli@amd.com/
- The endpoint RX path depends on AXI MCDMA metadata support in the
dmaengine driver. On plain net-next, xilinx_mcdma_prep_slave_sg() does
not attach metadata_ops and desc_metadata_modes stays 0 for
XDMA_TYPE_AXIMCDMA, so dmaengine_desc_get_metadata_ptr() returns
-ENOTSUPP and ep_dma_rx_cb() would drop every received frame. The series
therefore depends on the MCDMA metadata support that extends metadata_ops
to XDMA_TYPE_AXIMCDMA and returns the descriptor status word (index 0 is
status, index 1 is the sideband word carrying TID/TDEST/TUSER, index 2
onward is the app words). That dmaengine work is a prerequisite for RX to
function and the TSN patches touch no dmaengine code. It will be posted
to the dmaengine tree and this series carries a Depends-on note plus a
lore link once it is on-list.
- Performed ping, iperf3 and multi-queue TX/RX tests on the ZynqMP ZCU102
platform, with the endpoint conduit up and both external MACs cabled to
peer NICs. Also ran teardown and probe-defer stress under KASAN and
lockdep with no use-after-free or locking splats.
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Nagadheeraj Rottela (1):
net: xilinx: tsn: deliver endpoint RX frames to DSA user ports
Srinivas Neeli (7):
dt-bindings: net: add Xilinx TSN Endpoint Ethernet MAC
net: xilinx: tsn: add TSN endpoint wrapper driver
net: xilinx: tsn: add endpoint MAC driver skeleton
net: xilinx: tsn: parse endpoint DMA channel configuration
net: xilinx: tsn: bring up the endpoint MCDMA channels
net: xilinx: tsn: add the endpoint RX data path
net: xilinx: tsn: add the endpoint TX data path
.../net/xlnx,tsn-endpoint-ethernet-mac.yaml | 364 +++++++
MAINTAINERS | 8 +
drivers/net/ethernet/xilinx/Kconfig | 1 +
drivers/net/ethernet/xilinx/Makefile | 1 +
drivers/net/ethernet/xilinx/tsn/Kconfig | 16 +
drivers/net/ethernet/xilinx/tsn/Makefile | 2 +
drivers/net/ethernet/xilinx/tsn/xilinx_tsn.h | 15 +
drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c | 1034 ++++++++++++++++++++
drivers/net/ethernet/xilinx/tsn/xilinx_tsn_main.c | 107 ++
9 files changed, 1548 insertions(+)
---
base-commit: bc6fe9d301d59fe5cb236d646abe792bffae5fa6
change-id: 20260831-patches_v2_external-36421e557430
prerequisite-message-id: 20260818174151.1608971-1-srinivas.neeli@amd.com
prerequisite-patch-id: abfd1f0259ea7524a5b6a4a7c98b116a213910d0
prerequisite-patch-id: 64bc1fc9008bff333d8cab78d018d93db31c2ee7
prerequisite-patch-id: 3e7fee96146b9a89dbd70f83942e4acf61cfd730
prerequisite-patch-id: 87d626e07a12df2d50163e12e34879868c750253
Best regards,
--
Srinivas Neeli [off-list ref]
The TSN Endpoint Ethernet MAC IP groups several sub-nodes under one IP
node with a set of IP-wide clocks. Each sub-node needs its own driver,
and the shared clocks must be running before any of them probe.
Add a thin wrapper platform driver for the IP node. Enable the IP-wide
clocks from the device tree and bind each sub-node to its own driver.
Co-developed-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in v2:
- Depend on OF_ADDRESS instead of OF && HAS_IOMEM.
- Point the Kconfig help text at the companion DSA driver, not a symbol that
does not exist yet at this patch.
- Drop the mod_devicetable.h include, platform_device.h already pulls it in.
---
MAINTAINERS | 1 +
drivers/net/ethernet/xilinx/Kconfig | 1 +
drivers/net/ethernet/xilinx/Makefile | 1 +
drivers/net/ethernet/xilinx/tsn/Kconfig | 15 ++++
drivers/net/ethernet/xilinx/tsn/Makefile | 2 +
drivers/net/ethernet/xilinx/tsn/xilinx_tsn_main.c | 104 ++++++++++++++++++++++
6 files changed, 124 insertions(+)
The TSN Endpoint MAC owns the IP's host-side MCDMA data path and is the
netdev physically wired to the CPU. The DSA switch needs this netdev to
exist as its conduit.
Add a platform driver (compatible "xlnx,tsn-ep-mac") for the endpoint.
Register the netdev named "ep", set its MAC address, and provide minimal
netdev and ethtool ops. ndo_open starts the queues and ndo_start_xmit
drops frames.
Co-developed-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in v2:
- Name the netdev ep%d with NET_NAME_ENUM instead of a fixed "ep", so a second
IP instance does not clash on register_netdev(). The %d enumeration is what
NET_NAME_ENUM describes, and it matches the eth%d naming in net/dsa/user.c.
- Drop the mapped but unused register window (ep->regs and its ioremap).
It was intended for QBV support, which is not part of this series, so the
endpoint driver does not use it. The mapping will be re-added when QBV
support lands.
- Use dev_kfree_skb_any() on the drop path, not dev_kfree_skb().
- Drop the mod_devicetable.h include, platform_device.h already pulls it in.
---
drivers/net/ethernet/xilinx/tsn/Makefile | 2 +-
drivers/net/ethernet/xilinx/tsn/xilinx_tsn.h | 15 +++
drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c | 143 ++++++++++++++++++++++
drivers/net/ethernet/xilinx/tsn/xilinx_tsn_main.c | 3 +
4 files changed, 162 insertions(+), 1 deletion(-)
@@ -0,0 +1,143 @@+// SPDX-License-Identifier: GPL-2.0+/*+*AMD/XilinxTSNEndpointMACdriver.+*+*Copyright(C)2026AdvancedMicroDevices,Inc.+*/++#include<linux/etherdevice.h>+#include<linux/ethtool.h>+#include<linux/if_ether.h>+#include<linux/kernel.h>+#include<linux/module.h>+#include<linux/netdevice.h>+#include<linux/of.h>+#include<linux/of_net.h>+#include<linux/platform_device.h>+#include<linux/string.h>+#include<linux/types.h>++#include"xilinx_tsn.h"++#define DRIVER_NAME "xilinx_tsn_ep"++/**+*structxlnx_tsn_ep-EPMACprivatedata,embeddedinnet_deviceprivarea+*@ndev:theconduitnetdev("ep0"forthefirstIPinstance)+*@dev:backingdevice+*/+structxlnx_tsn_ep{+structnet_device*ndev;+structdevice*dev;+};++staticnetdev_tx_tep_start_xmit(structsk_buff*skb,structnet_device*ndev)+{+dev_kfree_skb_any(skb);+DEV_STATS_INC(ndev,tx_dropped);+returnNETDEV_TX_OK;+}++staticintep_open(structnet_device*ndev)+{+netif_tx_start_all_queues(ndev);++return0;+}++staticintep_stop(structnet_device*ndev)+{+netif_tx_disable(ndev);++return0;+}++staticvoidep_get_drvinfo(structnet_device*ndev,structethtool_drvinfo*ed)+{+strscpy(ed->driver,DRIVER_NAME,sizeof(ed->driver));+}++staticconststructnet_device_opsep_netdev_ops={+.ndo_open=ep_open,+.ndo_stop=ep_stop,+.ndo_start_xmit=ep_start_xmit,+.ndo_validate_addr=eth_validate_addr,+.ndo_set_mac_address=eth_mac_addr,+};++staticconststructethtool_opsep_ethtool_ops={+.get_drvinfo=ep_get_drvinfo,+};++staticintxlnx_tsn_ep_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structnet_device*ndev;+structxlnx_tsn_ep*ep;+u8mac_addr[ETH_ALEN];+intret;++ndev=alloc_netdev(sizeof(*ep),"ep%d",NET_NAME_ENUM,ether_setup);+if(!ndev)+return-ENOMEM;++SET_NETDEV_DEV(ndev,dev);+ndev->netdev_ops=&ep_netdev_ops;+ndev->ethtool_ops=&ep_ethtool_ops;+ndev->features=NETIF_F_SG;++ep=netdev_priv(ndev);+ep->ndev=ndev;+ep->dev=dev;++ret=of_get_mac_address(dev->of_node,mac_addr);+if(ret==-EPROBE_DEFER){+gotoerr_free_ndev;+}elseif(!ret&&is_valid_ether_addr(mac_addr)){+eth_hw_addr_set(ndev,mac_addr);+}else{+eth_hw_addr_random(ndev);+dev_info(dev,"no valid MAC in DT, using random address %pM\n",+ndev->dev_addr);+}++platform_set_drvdata(pdev,ep);++ret=register_netdev(ndev);+if(ret){+dev_err_probe(dev,ret,"failed to register net device\n");+gotoerr_free_ndev;+}++return0;++err_free_ndev:+free_netdev(ndev);+returnret;+}++staticvoidxlnx_tsn_ep_remove(structplatform_device*pdev)+{+structxlnx_tsn_ep*ep=platform_get_drvdata(pdev);++if(!ep)+return;++unregister_netdev(ep->ndev);+free_netdev(ep->ndev);+}++staticconststructof_device_idxlnx_tsn_ep_of_match[]={+{.compatible="xlnx,tsn-ep-mac"},+{}+};+MODULE_DEVICE_TABLE(of,xlnx_tsn_ep_of_match);++structplatform_driverxlnx_tsn_ep_driver={+.probe=xlnx_tsn_ep_probe,+.remove=xlnx_tsn_ep_remove,+.driver={+.name=DRIVER_NAME,+.of_match_table=xlnx_tsn_ep_of_match,+},+};
Add a binding for the Xilinx TSN Endpoint Ethernet MAC IP. The
top-level node describes the IP wrapper, which owns the six shared
IP clocks and the xlnx,num-priorities parameter, and has two
children:
- xlnx,tsn-ep-mac: the endpoint MAC, owning the host-side DMA
channels (dmas / dma-names / tx-queues-config).
- xlnx,tsn-switch: the three-port Ethernet switch covering the
switch fabric and the two per-MAC register windows. The MDIO
controllers and PTP register blocks sit at fixed offsets inside
the per-MAC windows, accessed through the mdio-1 / mdio-2
children and per-MAC interrupt-names entries. Each external
port@N references its PHY through a phy-handle in the matching
mdio-<N> child.
Co-developed-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in v2:
- Describe only the Bridged Endpoint configuration. The IP has so far been
simulated only as a bridged endpoint, so the binding and driver describe
that configuration alone. Support for an Endpoint-only design will be added
once that IP variant is simulated.
- Rename ep-mac@ to ethernet@ and mdio-mac1/mdio-mac2 to mdio-1/mdio-2, and
anchor the node names (^ethernet@[0-9a-f]+$, ^switch@[0-9a-f]+$), per
ethernet-controller.yaml and mdio.yaml.
- Cap TX at 8 and RX at 8 channels. The IP exposes 8 priorities, so it
drives at most 8 TX and 8 RX MCDMA channels. dma-names lists both
directions, so its combined maxItems is 16 (8 TX + 8 RX). V1 allowed up
to 16 per direction, which the hardware cannot use.
- Set additionalProperties: false on tx-queues-config and its queue nodes.
- Drop reg from the wrapper node. It has no registers of its own, ranges
already describes the child windows.
- Add minItems: 7 to the switch interrupts to match interrupt-names.
- Use a ^(ethernet-)?ports$ container with additionalProperties: false and its
own #address-cells/#size-cells, and require either ports or ethernet-ports.
- Require ethernet on the CPU port@0.
- Add reg (enum [1, 2]) on the external port@[1-2] ports and require reg,
phy-mode and phy-handle. MAC1 and MAC2 use gmii/rgmii/rgmii-id.
- Require port@0, port@1 and port@2. The IP is a fixed three-port bridged
design (port@0 CPU, port@1 MAC1, port@2 MAC2), so a device tree missing
any of them cannot describe working hardware.
- Add #address-cells and #size-cells (both const 1) on the wrapper node,
which the child ethernet@/switch@ windows need now that reg is dropped,
and set unevaluatedProperties: false on the endpoint node.
---
.../net/xlnx,tsn-endpoint-ethernet-mac.yaml | 364 +++++++++++++++++++++
MAINTAINERS | 7 +
2 files changed, 371 insertions(+)
@@ -0,0 +1,364 @@+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)+%YAML1.2+---+$id:http://devicetree.org/schemas/net/xlnx,tsn-endpoint-ethernet-mac.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:Xilinx TSN Endpoint Ethernet MAC++description:+TSN Endpoint Ethernet MAC IP implements IEEE 802.1 Time-Sensitive+Networking (TSN) standards and provides low-latency network connectivity+in the Bridged Endpoint configuration. The IP integrates a three-port+switch, with two ports connected to the external network and one port+connected to an internal endpoint. It also supports GMII/RGMII interfaces+for connection to an external PHY, enabling full-duplex operation at+100 Mb/s and 1 Gb/s speeds.++maintainers:+-Srinivas Neeli <srinivas.neeli@amd.com>+-Nagadheeraj Rottela <nagadheeraj.rottela@amd.com>++properties:+compatible:+const:xlnx,tsn-endpoint-ethernet-mac-3.0++clocks:+items:+-description:GTX clock for transmitter logic and RTC+-description:GTX 90-degree phase shifted clock for RGMII+-description:Host RX FIFO clock+-description:Host TX FIFO clock+-description:Reference clock for IDELAY control+-description:AXI-Lite register interface clock++clock-names:+items:+-const:gtx+-const:gtx90+-const:host_rxfifo+-const:host_txfifo+-const:ref+-const:s_axi++ranges:true++'#address-cells':+const:1++'#size-cells':+const:1++xlnx,num-priorities:+$ref:/schemas/types.yaml#/definitions/uint32+minimum:2+maximum:8+description:+Number of traffic classes (priorities) configured in the IP.+This is an IP configuration parameter that determines the number of+priority queues available for QoS scheduling. Traffic classes map to+IEEE 802.1Q priority levels (0-7).++patternProperties:+"^ethernet@[0-9a-f]+$":+type:object+$ref:/schemas/net/ethernet-controller.yaml#+unevaluatedProperties:false+description:+Endpoint MAC sub-node. Provides the host-side data path through the+DMA channels described by its dmas / dma-names.+properties:+compatible:+const:xlnx,tsn-ep-mac++reg:+maxItems:1++dmas:+minItems:2+maxItems:16++dma-names:+minItems:2+maxItems:16+items:+pattern:"^(tx|rx)_chan[0-7]$"+description:|+Names matching the entries in 'dmas', one per DMA channel and in+the same order. TX channel names use "tx_chan<N>" and RX channel+names use "rx_chan<N>", where N is 0..7. Example:"tx_chan0",+"tx_chan1",..., "rx_chan0", "rx_chan1", ....++tx-queues-config:+type:object+additionalProperties:false+description:+TX queue configuration node that maps IP priority queues to+DMA TX channels. The TSN IP supports multiple priority queues for+QoS scheduling, and each queue can be connected to a specific DMA+channel. This mapping defines which DMA TX channel is used to+transmit packets for each priority queue. For example, queue0+with xlnx,dma-channel-num set to 5 means priority queue 0 uses+tx_chan5 for data transfer.++patternProperties:+"^queue[0-7]$":+type:object+additionalProperties:false++description:+Each subnode represents a priority queue. The+xlnx,dma-channel-num property specifies which DMA TX channel+(tx_chan0 to tx_chan7) is connected to this queue for+transmitting packets.+properties:+xlnx,dma-channel-num:+$ref:/schemas/types.yaml#/definitions/uint32+description:+DMA channel that transmits this priority queue, given as+the N of the tx_chanN entry in dma-names.+minimum:0+maximum:7++required:+-xlnx,dma-channel-num++required:+-compatible+-reg+-dmas+-dma-names+-tx-queues-config++"^switch@[0-9a-f]+$":+type:object+$ref:/schemas/net/dsa/dsa.yaml#/$defs/ethernet-ports+description:+Three-port Ethernet switch sub-node. Contains the switch fabric+register window and the two per-MAC register windows. Each per-MAC+window houses an MDIO controller and a PTP register block at fixed+offsets. The IP's per-MAC and PTP-timer interrupts are exposed here.++properties:+compatible:+const:xlnx,tsn-switch++reg:+items:+-description:Switch fabric register window+-description:MAC1 register window (includes MDIO and PTP)+-description:MAC2 register window (includes MDIO and PTP)++reg-names:+items:+-const:switch+-const:mac1+-const:mac2++interrupts:+minItems:7+maxItems:7++interrupt-names:+items:+-const:ptp_rx_mac1+-const:ptp_tx_mac1+-const:mac_irq_mac1+-const:ptp_timer+-const:ptp_rx_mac2+-const:ptp_tx_mac2+-const:mac_irq_mac2++mdio-1:+$ref:/schemas/net/mdio.yaml#+description:+MDIO bus controller for MAC1. Registers reside inside the mac1+reg-names window.+unevaluatedProperties:false++mdio-2:+$ref:/schemas/net/mdio.yaml#+description:+MDIO bus controller for MAC2. Registers reside inside the mac2+reg-names window.+unevaluatedProperties:false++patternProperties:+"^(ethernet-)?ports$":+type:object+additionalProperties:false++properties:+'#address-cells':+const:1++'#size-cells':+const:0++port@0:+type:object+unevaluatedProperties:true+description:+CPU port. Wired internally to the endpoint MAC sibling.++properties:+reg:+const:0++phy-mode:+const:internal++required:+-ethernet++patternProperties:+"^port@[1-2]$":+type:object+unevaluatedProperties:true+description:+External Ethernet port. port@1 is wired to MAC1 and reached+through the mdio-1 bus, port@2 is wired to MAC2 and reached+through the mdio-2 bus.++properties:+reg:+enum:[1,2]++phy-mode:+enum:+-gmii+-rgmii+-rgmii-id++required:+-reg+-phy-mode+-phy-handle++required:+-port@0+-port@1+-port@2++required:+-compatible+-reg+-reg-names+-interrupts+-interrupt-names++oneOf:+-required:[ports]+-required:[ethernet-ports]++unevaluatedProperties:false++additionalProperties:false++required:+-compatible+-clocks+-clock-names+-xlnx,num-priorities+-ranges+-'#address-cells'+-'#size-cells'++examples:+-|+tsn@80040000 {+compatible = "xlnx,tsn-endpoint-ethernet-mac-3.0";+clocks = <&misc_clk_2>, <&misc_clk_2>, <&misc_clk_1>, <&misc_clk_1>,+<&misc_clk_3>, <&misc_clk_0>;+clock-names = "gtx", "gtx90", "host_rxfifo", "host_txfifo",+"ref","s_axi";+xlnx,num-priorities = <8>;+#address-cells = <1>;+#size-cells = <1>;+ranges = <0x0 0x80040000 0x40000>;++ep_mac:ethernet@16000 {+compatible = "xlnx,tsn-ep-mac";+reg = <0x16000 0xa000>;+dmas = <&axi_mcdma_0 0>, <&axi_mcdma_0 1>, <&axi_mcdma_0 2>,+<&axi_mcdma_0 3>, <&axi_mcdma_0 4>, <&axi_mcdma_0 5>,+<&axi_mcdma_0 6>, <&axi_mcdma_0 7>, <&axi_mcdma_0 16>,+<&axi_mcdma_0 17>, <&axi_mcdma_0 18>, <&axi_mcdma_0 19>,+<&axi_mcdma_0 20>, <&axi_mcdma_0 21>, <&axi_mcdma_0 22>,+<&axi_mcdma_0 23>;+dma-names = "tx_chan0", "tx_chan1", "tx_chan2", "tx_chan3",+"tx_chan4","tx_chan5","tx_chan6","tx_chan7",+"rx_chan0","rx_chan1","rx_chan2","rx_chan3",+"rx_chan4","rx_chan5","rx_chan6","rx_chan7";+tx-queues-config {+queue0 { xlnx,dma-channel-num = <0x7>; };+queue1 { xlnx,dma-channel-num = <0x6>; };+queue2 { xlnx,dma-channel-num = <0x5>; };+queue3 { xlnx,dma-channel-num = <0x4>; };+queue4 { xlnx,dma-channel-num = <0x3>; };+queue5 { xlnx,dma-channel-num = <0x2>; };+queue6 { xlnx,dma-channel-num = <0x1>; };+queue7 { xlnx,dma-channel-num = <0x0>; };+};+};++switch@38000 {+compatible = "xlnx,tsn-switch";+reg = <0x38000 0x8000>,+<0x00000 0x14000>,+<0x20000 0x14000>;+reg-names = "switch", "mac1", "mac2";++interrupt-parent = <&intc>;+interrupts = <0 2>, <2 2>, <4 2>, <6 2>,+<1 2>, <3 2>, <5 2>;+interrupt-names = "ptp_rx_mac1", "ptp_tx_mac1",+"mac_irq_mac1","ptp_timer",+"ptp_rx_mac2","ptp_tx_mac2",+"mac_irq_mac2";++mdio-1 {+#address-cells = <1>;+#size-cells = <0>;+phy0:ethernet-phy@0 {+reg = <0>;+};+};++mdio-2 {+#address-cells = <1>;+#size-cells = <0>;+phy1:ethernet-phy@1 {+reg = <1>;+};+};++ethernet-ports {+#address-cells = <1>;+#size-cells = <0>;++port@0 {+reg = <0>;+ethernet = <&ep_mac>;+phy-mode = "internal";++fixed-link {+speed = <1000>;+full-duplex;+};+};++port@1 {+reg = <1>;+phy-handle = <&phy0>;+phy-mode = "rgmii-id";+};++port@2 {+reg = <2>;+phy-handle = <&phy1>;+phy-mode = "rgmii-id";+};+};+};+};
The endpoint has one MCDMA channel per TX queue and per RX queue, and
the device tree lists them in "dma-names". The transmit path needs to
know which physical DMA channel backs each logical TX queue.
Count the TX and RX channels in "dma-names" and allocate the conduit
netdev with that many hardware queues. Parse the "tx-queues-config"
child node to map each logical TX queue to its physical DMA channel.
Co-developed-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in v2:
- Check xlnx,dma-channel-num against the channels that dma-names actually
lists, using a presence bitmask. Reject duplicates and out-of-range channels,
and index the channel arrays by logical queue.
- Require one queue node per tx_chan and num-priorities equal to the TX channel
count. This also gives xlnx,num-priorities a consumer.
- Cap TX and RX at 8 to match the binding.
- Error out on an unknown dma-names entry instead of skipping it.
- Drop the unreachable TX and RX count guards. The in-loop index and duplicate
checks already bound the count, so the channel arrays cannot be over-indexed.
---
drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c | 197 +++++++++++++++++++++++-
1 file changed, 196 insertions(+), 1 deletion(-)
Before the endpoint can move frames, each MCDMA channel needs a ring of
buffers and a live dmaengine channel.
Request every TX and RX channel named in the device tree when the
interface opens, and give each one a ring of preallocated SKB descriptor
slots. Reset the controller once at probe time through the first mapped
TX channel, which also gates probe on the MCDMA provider being bound so
-EPROBE_DEFER stays in the probe path and never reaches ndo_open. Release
the channels on stop, where dmaengine_terminate_sync() returns them to a
clean state. Add the XILINX_DMA dependency for the dmaengine and Xilinx
DMA APIs.
Co-developed-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in v2:
- Request a channel at probe time so -EPROBE_DEFER stays in the probe path and
ndo_open never hands a deferral back to userspace.
- Request the channels by their real dma-names index, not a dense 0..N-1
sequence.
- Reword the commit message so the reset reads as probe-time through the first
mapped TX channel, not on open.
---
drivers/net/ethernet/xilinx/tsn/Kconfig | 1 +
drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c | 281 ++++++++++++++++++++++++
2 files changed, 282 insertions(+)
With the RX channels armed, the endpoint can start receiving frames.
Post RX buffers to every RX channel on open and reap them from the
completion callback. Each inbound frame carries a TUSER port_id in the
descriptor sideband. Accept frames from MAC1 (port_id 1) and MAC2
(port_id 2) and pass them up the stack. Drop any other port_id, since
the bridged-only model never produces it. Drop and count frames with
bad descriptor status, an invalid length or no metadata pointer.
Refill each slot from the completion callback unless the interface is
closing, which keeps the ring full without a separate refill worker.
Serialise the RX refill against teardown. The completion callback re-arms
the ring, so ndo_stop() sets a closing flag under the same per-channel
lock the refill takes, and ep_free_dma_chan() takes that lock as a barrier
before it terminates the channel. No descriptor is submitted once teardown
has begun, so a single dmaengine_terminate_sync() is enough to quiesce the
channel.
The RX path uses __netif_rx() from the dmaengine completion callback
rather than NAPI. dmaengine owns the RX interrupt and ring and delivers
one descriptor per callback, so there is no poll context to run NAPI or
GRO, and the per-packet counters use the dev->stats atomics.
Co-developed-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in v2:
- Serialise the RX refill against teardown under a per-channel rx_lock. The
completion callback re-arms the ring, so ndo_stop() sets a closing flag under
the same lock and ep_free_dma_chan() takes it as a barrier before terminating.
- Recover a drained ring with a refill timer instead of leaving RX dead after
an allocation failure.
- Fail ndo_open if a channel cannot arm any RX descriptor.
- Set the closing flag on the ndo_open error path too, before tearing the
channels down.
- Take the ring size from the channel instead of a compile-time constant.
- Note in the commit message why RX is not NAPI based. dmaengine owns the RX
interrupt and ring and delivers one descriptor per callback, so there is no
poll context for NAPI or GRO, and the counters use the dev->stats atomics.
- Shut the refill timer down with timer_shutdown_sync() at teardown rather than
relying on timer_delete_sync() plus the closing flag.
- Return true from ep_rx_refill() when at least one descriptor is armed, false
otherwise, following the usual bool convention.
- Init the refill timer and rx_lock only for RX channels.
---
drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c | 216 +++++++++++++++++++++++-
1 file changed, 215 insertions(+), 1 deletion(-)
Add the transmit path so the endpoint can send frames.
Look up the physical DMA channel for the frame's queue through the map
built at probe, map the frame and submit it to that channel. Stop the
queue when the ring runs out of space, and wake it from the completion
callback once a slot frees up.
Co-developed-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Nagadheeraj Rottela <redacted>
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in v2:
- Take the scatterlist entry count from skb_to_sgvec() instead of assuming
nr_frags + 1, so dma_map_sg() cannot walk past the end of the list.
- Check dmaengine_submit() with dma_submit_error() before advancing the ring
head and charging BQL, and drop the frame if the submit fails.
- Publish the ring head with WRITE_ONCE(), paired with the READ_ONCE() on the
completion side.
- Take the ring size from the channel instead of the compile-time constant.
- Use dmaengine_prep_slave_sg() instead of calling the ops member directly.
- Move tx_lock into the per-channel struct, mirroring rx_lock, so independent
TX queues do not serialise on one device-wide lock.
- Narrow the tx_lock kernel-doc to what it actually protects.
- Mark the ring_tail store in the completion callback with WRITE_ONCE() to
match the READ_ONCE() readers.
- Use the spinlock_bh guard class in the completion callback so it matches the
spin_lock_bh() in the xmit path.
- Index tx_chans directly by logical queue in ep_start_xmit() and drop the
phys_chan/dma_dev locals, so the transmit path no longer re-derives the
channel through tx_dma_chan_map.
---
drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c | 150 +++++++++++++++++++++++-
1 file changed, 149 insertions(+), 1 deletion(-)
From: Nagadheeraj Rottela <redacted>
The DSA core routes an RX frame to a user port from the port metadata
attached to the skb. Without that metadata, frames from the two MACs
cannot reach their swpN netdevs.
Allocate a METADATA_HW_PORT_MUX entry per MAC port at probe and attach
the matching entry to each RX frame by its TUSER port_id. Frames from
MAC1 and MAC2 now reach the correct user netdev, which completes the
conduit data path.
The RX callback runs in softirq and hands the frame to __netif_rx(),
which queues it on the backlog instead of consuming it inline, so the
attached dst must be a counted reference (dst_hold() + skb_dst_set())
to survive the handoff.
Signed-off-by: Nagadheeraj Rottela <redacted>
Co-developed-by: Srinivas Neeli <srinivas.neeli@amd.com>
Signed-off-by: Srinivas Neeli <srinivas.neeli@amd.com>
---
Changes in v2:
- Attach the RX port metadata only when the netdev is a DSA conduit
(netdev_uses_dsa()), so no dev-less dst leaks into the stack when no switch
is bound.
- Take a real reference on the per-port metadata_dst with dst_hold() +
skb_dst_set() before the __netif_rx() backlog handoff, instead of
skb_dst_set_noref() + skb_dst_force(). The entries stay live for the driver
lifetime, so this drops the rcu_read_lock() that skb_dst_force() would need
in the softirq callback.
- Release the per-port metadata_dst with dst_release() at teardown, not
metadata_dst_free(), so an skb still referencing it is not freed early.
---
drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c | 55 ++++++++++++++++++++++++-
1 file changed, 54 insertions(+), 1 deletion(-)
@@ -939,16 +983,24 @@ static int xlnx_tsn_ep_probe(struct platform_device *pdev)ndev->dev_addr);}+ret=ep_alloc_port_md(ep);+if(ret){+dev_err_probe(dev,ret,"failed to allocate per-port metadata\n");+gotoerr_free_ndev;+}+platform_set_drvdata(pdev,ep);ret=register_netdev(ndev);if(ret){dev_err_probe(dev,ret,"failed to register net device\n");-gotoerr_free_ndev;+gotoerr_free_md;}return0;+err_free_md:+ep_free_port_md(ep);err_free_ndev:free_netdev(ndev);returnret;
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The return value of dmaengine_submit() is ignored in the RX path, leading to permanent RX ring desynchronization and data corruption if submission fails.
- [Medium] Out-of-bounds read of DMA descriptor metadata due to missing length validation.
--
commit 5fd8b30c57440a41e4ad90bda57e202d89a1aa7b
Author: Srinivas Neeli [off-list ref]
Subject: net: xilinx: tsn: add the endpoint RX data path
This commit adds the RX data path for the Xilinx TSN endpoint, allowing it to
receive frames. It posts RX buffers to each channel upon opening and reaps
them via a completion callback, serializing the refill against teardown.
[Severity: High]
Does this code ignore the return value of dmaengine_submit() in the RX path?
If dmaengine_submit() fails and returns an error cookie (e.g., due to an
exhausted DMA descriptor pool), the hardware will not receive the descriptor.
However, because the software pre-increments xchan->ring_head and ignores
the error, it assumes the descriptor is safely armed.
When the next successful descriptor completes, the software will read from
xchan->ring_tail, mistakenly unmapping and passing the SKB of the failed
descriptor to the network stack. Could this lead to passing uninitialized
kernel memory up the network stack and shifting all subsequent packet payloads
by one descriptor?
[ ... ]
[Severity: Medium]
Is it possible for the DMA driver to return a valid metadata pointer with a
short payload where meta_len is less than 8 bytes (2 words)?
If meta_len does not cover metadata[1], this access could result in an
out-of-bounds memory read. Does this code need to verify that meta_len is
large enough before accessing metadata[1]?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260909-patches_v2_external-v2-0-3a40babaff4c@amd.com?part=6
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Probe failure for standard-compliant DT nodes due to unit-address stripping.
--
commit ce208f79c4ab3c3d36c98c28fc4120175bc8c6d9
Author: Srinivas Neeli [off-list ref]
net: xilinx: tsn: parse endpoint DMA channel configuration
This commit introduces device tree parsing for the endpoint DMA channels in
the Xilinx TSN driver. It counts the available TX and RX channels and maps
logical TX queues to their physical DMA channels based on child nodes.
+/*
+ * Parse the "tx-queues-config" child of the EP node. The logical queue
+ * index is taken from the "queue<N>" node name, so the mapping does not
+ * depend on the order the child nodes appear in the device tree.
+ */
+static int ep_parse_tx_queue_config(struct xlnx_tsn_ep *ep,
+ struct device_node *txcfg_np, u16 tx_present)
+{
+ DECLARE_BITMAP(queue_seen, TSN_MAX_TX_QUEUE) = {};
+ DECLARE_BITMAP(chan_seen, TSN_MAX_TX_QUEUE) = {};
+ unsigned int count = 0;
+ int ret;
+
+ for_each_child_of_node_scoped(txcfg_np, qnode) {
+ u32 chan, queue;
+
+ if (!str_has_prefix(qnode->name, "queue") ||
+ kstrtou32(qnode->name + strlen("queue"), 10, &queue) ||
+ queue >= ep->num_tx_queues)
[Severity: Medium]
Will this correctly parse standard device tree unit addresses?
The Open Firmware (OF) core strips the unit-address (the `@...` portion)
when assigning the `name` field during node initialization. This means
that for a standard-compliant node like `queue@0`, `qnode->name` will be
exactly `"queue"`.
If `qnode->name` is exactly `"queue"`, skipping the prefix length leaves an
empty string (`"\0"`). Calling kstrtou32() on an empty string returns
-EINVAL, which would erroneously abort the probe for compliant DTs.
Could this logic be adjusted to handle unit-address stripping, or perhaps
read the logical queue index from a standard DT property like `reg` instead
of relying on string parsing of the node name?
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Incorrect SKB free function used for dropped packets on DMA error.
--
commit dba43ed8ae5a63ef3cc1b455fd12d3567126babd
Author: Srinivas Neeli [off-list ref]
net: xilinx: tsn: add the endpoint TX data path
Add the transmit path so the endpoint can send frames. Look up the physical
DMA channel for the frame's queue through the map built at probe, map the
frame and submit it to that channel. Stop the queue when the ring runs out
of space, and wake it from the completion callback once a slot frees up.
[Severity: Medium]
Does this code use the correct SKB free function on the error path?
When the DMA transmission fails (result->result != DMA_TRANS_NOERROR) in
ep_dma_tx_cb() inside drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c, the
packet is effectively dropped. However, dev_consume_skb_any() is used here
unconditionally.
Tracing tools like dropwatch rely on dev_kfree_skb_any() to distinguish
dropped packets from successfully transmitted ones (which use
dev_consume_skb_any()). Calling dev_consume_skb_any() on the error path
will cause these drops to be incorrectly reported as successful transmissions.
Should the error path use dev_kfree_skb_any() instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260909-patches_v2_external-v2-0-3a40babaff4c@amd.com?part=7