All of lore.kernel.org
 help / color / mirror / Atom feed
From: Srinivas Neeli <srinivas.neeli@amd.com>
To: Nagadheeraj Rottela <nagadheeraj.rottela@amd.com>,
	Andrew Lunn <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	"Eric Dumazet" <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Richard Cochran <richardcochran@gmail.com>,
	Michal Simek <michal.simek@amd.com>,
	"Sebastian Andrzej Siewior" <bigeasy@linutronix.de>,
	Clark Williams <clrkwllms@kernel.org>,
	Steven Rostedt <rostedt@goodmis.org>
Cc: <netdev@vger.kernel.org>, <devicetree@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-rt-devel@lists.linux.dev>,
	Srinivas Neeli <srinivas.neeli@amd.com>,
	<neelisrinivas18@gmail.com>, <git@amd.com>
Subject: [PATCH net-next v2 5/8] net: xilinx: tsn: bring up the endpoint MCDMA channels
Date: Wed, 9 Sep 2026 00:49:53 +0530	[thread overview]
Message-ID: <20260909-patches_v2_external-v2-5-3a40babaff4c@amd.com> (raw)
In-Reply-To: <20260909-patches_v2_external-v2-0-3a40babaff4c@amd.com>

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 <nagadheeraj.rottela@amd.com>
Signed-off-by: Nagadheeraj Rottela <nagadheeraj.rottela@amd.com>
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(+)

diff --git a/drivers/net/ethernet/xilinx/tsn/Kconfig b/drivers/net/ethernet/xilinx/tsn/Kconfig
index b8939d8a92ef..292f17694404 100644
--- a/drivers/net/ethernet/xilinx/tsn/Kconfig
+++ b/drivers/net/ethernet/xilinx/tsn/Kconfig
@@ -6,6 +6,7 @@
 config XILINX_TSN
 	tristate "Xilinx TSN Ethernet driver"
 	depends on OF_ADDRESS
+	depends on XILINX_DMA
 	help
 	  This driver supports the AMD/Xilinx Time-Sensitive Networking
 	  (TSN) Endpoint Ethernet MAC IP. It provides the wrapper device
diff --git a/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c b/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c
index 24025b1f6e66..23a15df71baa 100644
--- a/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c
+++ b/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c
@@ -6,16 +6,24 @@
  */
 
 #include <linux/bitops.h>
+#include <linux/circ_buf.h>
+#include <linux/dma/xilinx_dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
+#include <linux/io.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/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/types.h>
 
@@ -30,6 +38,47 @@
 #define TSN_MAX_VLAN_FRAME_SIZE		(ETH_DATA_LEN + VLAN_ETH_HLEN + \
 					 ETH_FCS_LEN)
 
+#define TX_BD_NUM_DEFAULT		64
+#define RX_BD_NUM_DEFAULT		128
+
+/**
+ * struct skbuf_dma_descriptor - skb container for each in-flight DMA descriptor
+ * @sgl: scatter-gather list backing the DMA mapping
+ * @desc: dmaengine descriptor handle
+ * @dma_address: physical address of the first sgl entry (RX path)
+ * @skb: SKB owning the buffer
+ * @sg_len: number of valid entries in @sgl (TX path)
+ */
+struct skbuf_dma_descriptor {
+	struct scatterlist sgl[MAX_SKB_FRAGS + 1];
+	struct dma_async_tx_descriptor *desc;
+	dma_addr_t dma_address;
+	struct sk_buff *skb;
+	int sg_len;
+};
+
+/**
+ * struct xlnx_tsn_ep_dma_chan - one DMA channel and its SKB ring
+ * @skb_ring: per-slot SKB descriptors
+ * @ep: pointer back to the owning EP instance
+ * @chan: dmaengine channel handle
+ * @dma_dev: device used for DMA mapping (the DMA engine, not the EP)
+ * @ring_head: producer index
+ * @ring_tail: consumer index
+ * @ring_size: number of slots in @skb_ring
+ * @is_tx: true for TX channels, false for RX
+ */
+struct xlnx_tsn_ep_dma_chan {
+	struct skbuf_dma_descriptor **skb_ring;
+	struct xlnx_tsn_ep *ep;
+	struct dma_chan *chan;
+	struct device *dma_dev;
+	u32 ring_head;
+	u32 ring_tail;
+	u32 ring_size;
+	bool is_tx;
+};
+
 /**
  * struct xlnx_tsn_ep - EP MAC private data, embedded in net_device priv area
  * @ndev: the conduit netdev ("ep0" for the first IP instance)
@@ -39,6 +88,9 @@
  * @tx_dma_chan_map: logical TX queue index -> physical DMA channel number
  * @rx_chan_num: RX ring index -> physical DMA channel number
  * @max_frm_size: maximum frame size accepted on RX
+ * @tx_chans: array of TX channels (size @num_tx_queues)
+ * @rx_chans: array of RX channels (size @num_rx_queues)
+ * @closing: set in ndo_stop so the RX completion callback stops re-arming
  */
 struct xlnx_tsn_ep {
 	struct net_device *ndev;
@@ -48,8 +100,19 @@ struct xlnx_tsn_ep {
 	u32 tx_dma_chan_map[TSN_MAX_TX_QUEUE];
 	u32 rx_chan_num[TSN_MAX_RX_QUEUE];
 	u32 max_frm_size;
+
+	struct xlnx_tsn_ep_dma_chan **tx_chans;
+	struct xlnx_tsn_ep_dma_chan **rx_chans;
+
+	bool closing;
 };
 
+static inline struct skbuf_dma_descriptor *
+ep_get_desc(struct xlnx_tsn_ep_dma_chan *xchan, int idx)
+{
+	return xchan->skb_ring[idx];
+}
+
 static netdev_tx_t ep_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
 	dev_kfree_skb_any(skb);
@@ -57,8 +120,22 @@ static netdev_tx_t ep_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	return NETDEV_TX_OK;
 }
 
+static int ep_init_dmaengine(struct xlnx_tsn_ep *ep);
+static void ep_exit_dmaengine(struct xlnx_tsn_ep *ep);
+
 static int ep_open(struct net_device *ndev)
 {
+	struct xlnx_tsn_ep *ep = netdev_priv(ndev);
+	int ret;
+
+	WRITE_ONCE(ep->closing, false);
+
+	ret = ep_init_dmaengine(ep);
+	if (ret) {
+		netdev_err(ndev, "failed to initialize DMA engine\n");
+		return ret;
+	}
+
 	netif_tx_start_all_queues(ndev);
 
 	return 0;
@@ -66,7 +143,11 @@ static int ep_open(struct net_device *ndev)
 
 static int ep_stop(struct net_device *ndev)
 {
+	struct xlnx_tsn_ep *ep = netdev_priv(ndev);
+
 	netif_tx_disable(ndev);
+	WRITE_ONCE(ep->closing, true);
+	ep_exit_dmaengine(ep);
 
 	return 0;
 }
@@ -88,6 +169,196 @@ static const struct ethtool_ops ep_ethtool_ops = {
 	.get_drvinfo	= ep_get_drvinfo,
 };
 
+static struct xlnx_tsn_ep_dma_chan *
+ep_alloc_dma_chan(struct xlnx_tsn_ep *ep, const char *name, bool is_tx,
+		  int ring_size)
+{
+	struct xlnx_tsn_ep_dma_chan *chan;
+	struct dma_chan *err_chan;
+	int i;
+
+	chan = kzalloc_obj(*chan);
+	if (!chan)
+		return ERR_PTR(-ENOMEM);
+
+	chan->chan = dma_request_chan(ep->dev, name);
+	if (IS_ERR(chan->chan)) {
+		err_chan = chan->chan;
+		kfree(chan);
+		return ERR_CAST(err_chan);
+	}
+
+	chan->skb_ring = kcalloc(ring_size, sizeof(*chan->skb_ring), GFP_KERNEL);
+	if (!chan->skb_ring) {
+		dma_release_channel(chan->chan);
+		kfree(chan);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	for (i = 0; i < ring_size; i++) {
+		chan->skb_ring[i] = kzalloc_obj(*chan->skb_ring[i]);
+		if (!chan->skb_ring[i]) {
+			while (--i >= 0)
+				kfree(chan->skb_ring[i]);
+			kfree(chan->skb_ring);
+			dma_release_channel(chan->chan);
+			kfree(chan);
+			return ERR_PTR(-ENOMEM);
+		}
+	}
+
+	chan->is_tx = is_tx;
+	chan->ep = ep;
+	chan->ring_size = ring_size;
+	chan->dma_dev = dmaengine_get_dma_device(chan->chan);
+
+	return chan;
+}
+
+static void ep_free_dma_chan(struct xlnx_tsn_ep_dma_chan *chan)
+{
+	int i;
+
+	if (!chan)
+		return;
+
+	if (chan->chan)
+		dmaengine_terminate_sync(chan->chan);
+
+	if (chan->is_tx) {
+		while (chan->ring_tail != chan->ring_head) {
+			struct skbuf_dma_descriptor *skbuf_dma;
+
+			skbuf_dma = chan->skb_ring[chan->ring_tail &
+						  (chan->ring_size - 1)];
+			if (skbuf_dma && skbuf_dma->skb) {
+				dma_unmap_sg(chan->dma_dev, skbuf_dma->sgl,
+					     skbuf_dma->sg_len, DMA_TO_DEVICE);
+				dev_kfree_skb_any(skbuf_dma->skb);
+				skbuf_dma->skb = NULL;
+			}
+			chan->ring_tail++;
+		}
+	}
+
+	if (chan->skb_ring) {
+		for (i = 0; i < chan->ring_size; i++) {
+			struct skbuf_dma_descriptor *skbuf_dma = chan->skb_ring[i];
+
+			if (skbuf_dma && !chan->is_tx && skbuf_dma->skb) {
+				dma_unmap_single(chan->dma_dev,
+						 skbuf_dma->dma_address,
+						 chan->ep->max_frm_size,
+						 DMA_FROM_DEVICE);
+				dev_kfree_skb_any(skbuf_dma->skb);
+			}
+			kfree(chan->skb_ring[i]);
+		}
+		kfree(chan->skb_ring);
+	}
+	if (chan->chan)
+		dma_release_channel(chan->chan);
+
+	kfree(chan);
+}
+
+static void ep_exit_dmaengine(struct xlnx_tsn_ep *ep)
+{
+	int i;
+
+	if (ep->tx_chans) {
+		for (i = 0; i < ep->num_tx_queues; i++)
+			ep_free_dma_chan(ep->tx_chans[i]);
+		kfree(ep->tx_chans);
+		ep->tx_chans = NULL;
+	}
+	if (ep->rx_chans) {
+		for (i = 0; i < ep->num_rx_queues; i++)
+			ep_free_dma_chan(ep->rx_chans[i]);
+		kfree(ep->rx_chans);
+		ep->rx_chans = NULL;
+	}
+}
+
+static int ep_init_dmaengine(struct xlnx_tsn_ep *ep)
+{
+	int tx_allocated = 0, rx_allocated = 0;
+	char name[16];
+	int i, ret;
+
+	ep->tx_chans = kcalloc(ep->num_tx_queues, sizeof(*ep->tx_chans),
+			       GFP_KERNEL);
+	if (!ep->tx_chans)
+		return -ENOMEM;
+
+	ep->rx_chans = kcalloc(ep->num_rx_queues, sizeof(*ep->rx_chans),
+			       GFP_KERNEL);
+	if (!ep->rx_chans) {
+		ret = -ENOMEM;
+		goto err_free_tx;
+	}
+
+	for (i = 0; i < ep->num_tx_queues; i++) {
+		snprintf(name, sizeof(name), "tx_chan%u", ep->tx_dma_chan_map[i]);
+		ep->tx_chans[i] = ep_alloc_dma_chan(ep, name, true,
+						    TX_BD_NUM_DEFAULT);
+		if (IS_ERR(ep->tx_chans[i])) {
+			ret = PTR_ERR(ep->tx_chans[i]);
+			ep->tx_chans[i] = NULL;
+			goto err_free_chans;
+		}
+		tx_allocated++;
+	}
+
+	for (i = 0; i < ep->num_rx_queues; i++) {
+		snprintf(name, sizeof(name), "rx_chan%u", ep->rx_chan_num[i]);
+		ep->rx_chans[i] = ep_alloc_dma_chan(ep, name, false,
+						    RX_BD_NUM_DEFAULT);
+		if (IS_ERR(ep->rx_chans[i])) {
+			ret = PTR_ERR(ep->rx_chans[i]);
+			ep->rx_chans[i] = NULL;
+			goto err_free_chans;
+		}
+		rx_allocated++;
+	}
+
+	return 0;
+
+err_free_chans:
+	while (--rx_allocated >= 0)
+		ep_free_dma_chan(ep->rx_chans[rx_allocated]);
+	while (--tx_allocated >= 0)
+		ep_free_dma_chan(ep->tx_chans[tx_allocated]);
+	kfree(ep->rx_chans);
+	ep->rx_chans = NULL;
+err_free_tx:
+	kfree(ep->tx_chans);
+	ep->tx_chans = NULL;
+	return ret;
+}
+
+static int ep_reset_dma_controller(struct xlnx_tsn_ep *ep)
+{
+	struct xilinx_vdma_config cfg = { .reset = 1 };
+	struct dma_chan *reset_chan;
+	char name[16];
+	int ret;
+
+	snprintf(name, sizeof(name), "tx_chan%u", ep->tx_dma_chan_map[0]);
+	reset_chan = dma_request_chan(ep->dev, name);
+	if (IS_ERR(reset_chan))
+		return dev_err_probe(ep->dev, PTR_ERR(reset_chan),
+				     "failed to request %s for reset\n", name);
+
+	ret = xilinx_vdma_channel_set_config(reset_chan, &cfg);
+	dma_release_channel(reset_chan);
+	if (ret < 0)
+		return dev_err_probe(ep->dev, ret,
+				     "failed to reset DMA controller\n");
+
+	return 0;
+}
+
 /*
  * 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
@@ -285,6 +556,16 @@ static int xlnx_tsn_ep_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_free_ndev;
 
+	/*
+	 * Request one DMA channel at probe time to reset the controller and to
+	 * gate on the MCDMA provider being bound. This keeps -EPROBE_DEFER in
+	 * the probe path, so the netdev is only registered once the provider is
+	 * available and ndo_open never sees a deferral.
+	 */
+	ret = ep_reset_dma_controller(ep);
+	if (ret)
+		goto err_free_ndev;
+
 	ret = of_get_mac_address(dev->of_node, mac_addr);
 	if (ret == -EPROBE_DEFER) {
 		goto err_free_ndev;

-- 
2.43.0



  parent reply	other threads:[~2026-09-08 19:21 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-08 19:19 [PATCH net-next v2 0/8] Add Xilinx TSN Endpoint Ethernet MAC driver Srinivas Neeli
2026-09-08 19:19 ` [PATCH net-next v2 1/8] dt-bindings: net: add Xilinx TSN Endpoint Ethernet MAC Srinivas Neeli
2026-09-12 20:36   ` netdev-bot+sashiko
2026-09-24  9:29     ` Neeli, Srinivas
2026-09-18 18:15   ` Rob Herring
2026-09-08 19:19 ` [PATCH net-next v2 2/8] net: xilinx: tsn: add TSN endpoint wrapper driver Srinivas Neeli
2026-09-12 20:36   ` netdev-bot+sashiko
2026-09-24  9:39     ` Neeli, Srinivas
2026-09-08 19:19 ` [PATCH net-next v2 3/8] net: xilinx: tsn: add endpoint MAC driver skeleton Srinivas Neeli
2026-09-12 20:36   ` netdev-bot+sashiko
2026-09-16  0:23   ` Jakub Kicinski
2026-09-08 19:19 ` [PATCH net-next v2 4/8] net: xilinx: tsn: parse endpoint DMA channel configuration Srinivas Neeli
2026-09-09 19:21   ` sashiko-bot
2026-09-12 20:36   ` netdev-bot+sashiko
2026-09-24 10:05     ` Neeli, Srinivas
2026-09-08 19:19 ` Srinivas Neeli [this message]
2026-09-12 20:36   ` [PATCH net-next v2 5/8] net: xilinx: tsn: bring up the endpoint MCDMA channels netdev-bot+sashiko
2026-09-08 19:19 ` [PATCH net-next v2 6/8] net: xilinx: tsn: add the endpoint RX data path Srinivas Neeli
2026-09-09 19:21   ` sashiko-bot
2026-09-12 20:36   ` netdev-bot+sashiko
2026-09-16  0:25   ` Jakub Kicinski
2026-09-08 19:19 ` [PATCH net-next v2 7/8] net: xilinx: tsn: add the endpoint TX " Srinivas Neeli
2026-09-09 19:21   ` sashiko-bot
2026-09-12 20:36   ` netdev-bot+sashiko
2026-09-08 19:19 ` [PATCH net-next v2 8/8] net: xilinx: tsn: deliver endpoint RX frames to DSA user ports Srinivas Neeli
2026-09-12 20:36   ` netdev-bot+sashiko
2026-09-16  0:22 ` [PATCH net-next v2 0/8] Add Xilinx TSN Endpoint Ethernet MAC driver Jakub Kicinski
2026-09-16  8:01   ` Neeli, Srinivas
2026-09-16 23:57     ` Jakub Kicinski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260909-patches_v2_external-v2-5-3a40babaff4c@amd.com \
    --to=srinivas.neeli@amd.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=bigeasy@linutronix.de \
    --cc=clrkwllms@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=edumazet@google.com \
    --cc=git@amd.com \
    --cc=krzk+dt@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rt-devel@lists.linux.dev \
    --cc=michal.simek@amd.com \
    --cc=nagadheeraj.rottela@amd.com \
    --cc=neelisrinivas18@gmail.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=richardcochran@gmail.com \
    --cc=robh@kernel.org \
    --cc=rostedt@goodmis.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.