[PATCH net-next v2 4/8] net: xilinx: tsn: parse endpoint DMA channel configuration
From: Srinivas Neeli <srinivas.neeli@amd.com>
Date: 2026-09-08 19:20:36
Also in:
linux-arm-kernel, linux-devicetree, linux-rt-devel, lkml
Subsystem:
networking drivers, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
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(-)
diff --git a/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c b/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c
index 089f17a126f5..24025b1f6e66 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,31 @@ #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 ("ep0" for the first IP instance) * @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: 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 */ 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 rx_chan_num[TSN_MAX_RX_QUEUE]; + u32 max_frm_size; }; static netdev_tx_t ep_start_xmit(struct sk_buff *skb, struct net_device *ndev)
@@ -69,15 +88,173 @@ 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_queues(struct device *dev, u32 *out_tx, u32 *out_rx, + u16 *tx_present, u32 *rx_chan_num) +{ + 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_chan_num[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 rx_chan_num[TSN_MAX_RX_QUEUE]; struct device *dev = &pdev->dev; + struct device_node *txcfg_np; + u32 num_tx, num_rx, num_prio; + 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_queues(dev, &num_tx, &num_rx, &tx_present, rx_chan_num); + 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 != num_prio) + return dev_err_probe(dev, -EINVAL, + "TX channel count %u must equal num-priorities %u\n", + num_tx, num_prio); - ndev = alloc_netdev(sizeof(*ep), "ep%d", NET_NAME_ENUM, ether_setup); + ndev = alloc_netdev_mqs(sizeof(*ep), "ep%d", NET_NAME_ENUM, + ether_setup, num_tx, num_rx); if (!ndev) return -ENOMEM;
@@ -89,6 +266,24 @@ 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; + ep->num_rx_queues = num_rx; + ep->max_frm_size = TSN_MAX_VLAN_FRAME_SIZE; + memcpy(ep->rx_chan_num, rx_chan_num, num_rx * sizeof(*rx_chan_num)); + + 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.43.0