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>,
"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>,
Eric Dumazet <edumazet@kernel.org>,
"Eric Dumazet" <edumazet@kernel.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 v3 4/8] net: xilinx: tsn: parse endpoint DMA channel configuration
Date: Fri, 16 Oct 2026 15:02:03 +0530 [thread overview]
Message-ID: <20261016-patches_v2_external-v3-4-e33d3960ca1e@amd.com> (raw)
In-Reply-To: <20261016-patches_v2_external-v3-0-e33d3960ca1e@amd.com>
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 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 channel.
The device tree describes one "queueN" child per TX channel under
"tx-queues-config". Each child names its channel with
"xlnx,dma-channel-num", given as the N of the tx_chanN entry in
"dma-names". Every logical TX queue must be described exactly once and
name a distinct channel that "dma-names" lists, and the TX channel count
must equal "xlnx,num-priorities" on the parent IP node. Probe fails with
dev_err_probe() when any of that does not hold.
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 v3:
- State the device tree requirements in the commit body. One "queueN"
child per TX channel naming its channel with "xlnx,dma-channel-num",
every TX queue described exactly once, and the TX channel count equal
to "xlnx,num-priorities". They were only in the v2 changelog before.
- Correct the kernel-doc for tx_dma_chan_map. It holds the N of a tx_chanN
entry in "dma-names", not a hardware channel number.
- Name the counting helper ep_count_dma_channels() and its probe results
num_tx_dma_channels and num_rx_dma_channels. Both count MCDMA channels
listed in "dma-names", which "queues" did not convey.
- Call the RX map rx_dma_chan_map, matching tx_dma_chan_map. Both hold the
N of a chanN entry in "dma-names", so one concept now has one name.
- Keep the RX map in a probe local rather than copying it into the private
struct. Nothing outside probe reads it.
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 | 198 +++++++++++++++++++++++-
1 file changed, 197 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c b/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c
index 609d444c6835..2adee07f6f51 100644
--- a/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c
+++ b/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c
@@ -5,9 +5,11 @@
* Copyright (C) 2026 Advanced Micro Devices, Inc.
*/
+#include <linux/bitops.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
@@ -21,14 +23,30 @@
#define DRIVER_NAME "xilinx_tsn_ep"
+#define TSN_DMA_CH_INVALID 0xFFU
+#define TSN_MAX_TX_QUEUE 8
+#define TSN_MAX_RX_QUEUE 8
+
+#define TSN_MAX_VLAN_FRAME_SIZE (ETH_DATA_LEN + VLAN_ETH_HLEN + \
+ ETH_FCS_LEN)
+
/**
* struct xlnx_tsn_ep - EP MAC private data, embedded in net_device priv area
* @ndev: the conduit netdev
* @dev: backing device
+ * @num_tx_queues: number of TX DMA channels (one per priority)
+ * @num_rx_queues: number of RX DMA channels
+ * @tx_dma_chan_map: TX queue to tx_chanN map, or TSN_DMA_CH_INVALID when
+ * the queue has no channel
+ * @max_frm_size: maximum frame size accepted on RX
*/
struct xlnx_tsn_ep {
struct net_device *ndev;
struct device *dev;
+ u32 num_tx_queues;
+ u32 num_rx_queues;
+ u32 tx_dma_chan_map[TSN_MAX_TX_QUEUE];
+ u32 max_frm_size;
};
static netdev_tx_t ep_start_xmit(struct sk_buff *skb, struct net_device *ndev)
@@ -69,15 +87,176 @@ static const struct ethtool_ops ep_ethtool_ops = {
.get_drvinfo = ep_get_drvinfo,
};
+/*
+ * 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)
+ return dev_err_probe(ep->dev, -EINVAL,
+ "tx-config: invalid queue node %pOFn (have %u queues)\n",
+ qnode, ep->num_tx_queues);
+
+ if (test_and_set_bit(queue, queue_seen))
+ return dev_err_probe(ep->dev, -EINVAL,
+ "tx-config: queue %u described twice\n",
+ queue);
+
+ ret = of_property_read_u32(qnode, "xlnx,dma-channel-num", &chan);
+ if (ret)
+ return dev_err_probe(ep->dev, ret,
+ "tx-config: queue %u missing xlnx,dma-channel-num\n",
+ queue);
+
+ if (chan >= TSN_MAX_TX_QUEUE || !(tx_present & BIT(chan)))
+ return dev_err_probe(ep->dev, -EINVAL,
+ "tx-config: queue %u maps to channel %u not present in dma-names\n",
+ queue, chan);
+
+ if (test_and_set_bit(chan, chan_seen))
+ return dev_err_probe(ep->dev, -EINVAL,
+ "tx-config: channel %u already assigned to another queue\n",
+ chan);
+
+ ep->tx_dma_chan_map[queue] = chan;
+ count++;
+ }
+
+ if (count != ep->num_tx_queues)
+ return dev_err_probe(ep->dev, -EINVAL,
+ "tx-config: described %u queues but expected %u\n",
+ count, ep->num_tx_queues);
+
+ return 0;
+}
+
+static int ep_count_dma_channels(struct device *dev, u32 *out_tx, u32 *out_rx,
+ u16 *tx_present, u32 *rx_dma_chan_map)
+{
+ u32 tx = 0, rx = 0;
+ u16 rx_present = 0;
+ int n, i, ret;
+
+ n = of_property_count_strings(dev->of_node, "dma-names");
+ if (n < 0)
+ return dev_err_probe(dev, n, "failed to read dma-names\n");
+
+ for (i = 0; i < n; i++) {
+ const char *name;
+ size_t plen;
+ u32 idx;
+
+ ret = of_property_read_string_index(dev->of_node, "dma-names",
+ i, &name);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read dma-names[%d]\n", i);
+
+ plen = str_has_prefix(name, "tx_chan");
+ if (plen) {
+ if (kstrtou32(name + plen, 10, &idx) ||
+ idx >= TSN_MAX_TX_QUEUE)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid TX channel name %s\n",
+ name);
+
+ if (*tx_present & BIT(idx))
+ return dev_err_probe(dev, -EINVAL,
+ "duplicate TX channel %s\n",
+ name);
+
+ *tx_present |= BIT(idx);
+ tx++;
+ continue;
+ }
+
+ plen = str_has_prefix(name, "rx_chan");
+ if (plen) {
+ if (kstrtou32(name + plen, 10, &idx) ||
+ idx >= TSN_MAX_RX_QUEUE)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid RX channel name %s\n",
+ name);
+
+ if (rx_present & BIT(idx))
+ return dev_err_probe(dev, -EINVAL,
+ "duplicate RX channel %s\n",
+ name);
+
+ rx_present |= BIT(idx);
+ rx_dma_chan_map[rx] = idx;
+ rx++;
+ continue;
+ }
+
+ return dev_err_probe(dev, -EINVAL,
+ "unrecognised dma-names entry %s\n", name);
+ }
+
+ if (!tx)
+ return dev_err_probe(dev, -EINVAL,
+ "no TX channels in dma-names\n");
+
+ if (!rx)
+ return dev_err_probe(dev, -EINVAL,
+ "no RX channels in dma-names\n");
+
+ *out_tx = tx;
+ *out_rx = rx;
+
+ return 0;
+}
+
static int xlnx_tsn_ep_probe(struct platform_device *pdev)
{
+ u32 num_tx_dma_channels, num_rx_dma_channels, num_prio;
+ u32 rx_dma_chan_map[TSN_MAX_RX_QUEUE];
struct device *dev = &pdev->dev;
+ struct device_node *txcfg_np;
+ struct device_node *ip_np;
struct net_device *ndev;
struct xlnx_tsn_ep *ep;
u8 mac_addr[ETH_ALEN];
+ u16 tx_present = 0;
int ret;
+ int i;
+
+ ret = ep_count_dma_channels(dev, &num_tx_dma_channels,
+ &num_rx_dma_channels, &tx_present,
+ rx_dma_chan_map);
+ if (ret)
+ return ret;
+
+ ip_np = of_get_parent(dev->of_node);
+ if (!ip_np)
+ return dev_err_probe(dev, -EINVAL, "missing parent IP node\n");
+
+ ret = of_property_read_u32(ip_np, "xlnx,num-priorities", &num_prio);
+ of_node_put(ip_np);
+ if (ret)
+ return dev_err_probe(dev, ret, "missing xlnx,num-priorities\n");
+
+ if (num_tx_dma_channels != num_prio)
+ return dev_err_probe(dev, -EINVAL,
+ "TX channel count %u must equal num-priorities %u\n",
+ num_tx_dma_channels, num_prio);
- ndev = alloc_netdev(sizeof(*ep), "eth%d", NET_NAME_UNKNOWN, ether_setup);
+ ndev = alloc_netdev_mqs(sizeof(*ep), "eth%d", NET_NAME_UNKNOWN,
+ ether_setup, num_tx_dma_channels,
+ num_rx_dma_channels);
if (!ndev)
return -ENOMEM;
@@ -89,6 +268,23 @@ static int xlnx_tsn_ep_probe(struct platform_device *pdev)
ep = netdev_priv(ndev);
ep->ndev = ndev;
ep->dev = dev;
+ ep->num_tx_queues = num_tx_dma_channels;
+ ep->num_rx_queues = num_rx_dma_channels;
+ ep->max_frm_size = TSN_MAX_VLAN_FRAME_SIZE;
+
+ for (i = 0; i < TSN_MAX_TX_QUEUE; i++)
+ ep->tx_dma_chan_map[i] = TSN_DMA_CH_INVALID;
+
+ txcfg_np = of_get_child_by_name(dev->of_node, "tx-queues-config");
+ if (!txcfg_np) {
+ ret = dev_err_probe(dev, -EINVAL,
+ "missing tx-queues-config node\n");
+ goto err_free_ndev;
+ }
+ ret = ep_parse_tx_queue_config(ep, txcfg_np, tx_present);
+ of_node_put(txcfg_np);
+ if (ret)
+ goto err_free_ndev;
ret = of_get_mac_address(dev->of_node, mac_addr);
if (ret == -EPROBE_DEFER) {
--
2.44.4
next prev parent reply other threads:[~2026-10-16 9:33 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-16 9:31 [PATCH net-next v3 0/8] Add Xilinx TSN Endpoint Ethernet MAC driver Srinivas Neeli
2026-10-16 9:32 ` [PATCH net-next v3 1/8] dt-bindings: net: add Xilinx TSN Endpoint Ethernet MAC Srinivas Neeli
2026-10-16 9:32 ` [PATCH net-next v3 2/8] net: xilinx: tsn: add TSN endpoint wrapper driver Srinivas Neeli
2026-10-16 9:32 ` [PATCH net-next v3 3/8] net: xilinx: tsn: add endpoint MAC driver skeleton Srinivas Neeli
2026-10-16 9:32 ` Srinivas Neeli [this message]
2026-10-16 9:32 ` [PATCH net-next v3 5/8] net: xilinx: tsn: bring up the endpoint MCDMA channels Srinivas Neeli
2026-10-16 9:32 ` [PATCH net-next v3 6/8] net: xilinx: tsn: add the endpoint RX data path Srinivas Neeli
2026-10-16 9:32 ` [PATCH net-next v3 7/8] net: xilinx: tsn: add the endpoint TX " Srinivas Neeli
2026-10-16 9:32 ` [PATCH net-next v3 8/8] net: xilinx: tsn: deliver endpoint RX frames to DSA user ports Srinivas Neeli
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=20261016-patches_v2_external-v3-4-e33d3960ca1e@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@kernel.org \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®