Skip to content

Commit 89934db

Browse files
vineethchowdharyPaolo Abeni
authored andcommitted
net: macb: Add TAPRIO traffic scheduling support
Implement Time-Aware Traffic Scheduling (TAPRIO) offload support for Cadence MACB/GEM ethernet controllers to enable IEEE 802.1Qbv compliant time-sensitive networking (TSN) capabilities. Key Features: 1. Enhanced Scheduled Traffic (ENST) Register Management - Per-queue ENST registers: ENST_START_TIME, ENST_ON_TIME, ENST_OFF_TIME - Centralized control via ENST_CONTROL for gate enable/disable - Infrastructure enhancements: * Extended macb_queue structure with ENST timing control registers * Mapped ENST register offsets into queue management framework * Introduced macb_queue_enst_config for per-entry TC configuration - Timing conversion utility: * enst_ns_to_hw_units(): Converts nanoseconds to hardware units * Timing values are programmed as hardware units based on link speed * Conversion formula: time_bytes = time_ns / divisor * Speed-specific divisors: 1Gbps=8, 100Mbps=80, 10Mbps=800 - Hardware limit utility: * enst_max_hw_interval(): Returns max interval for given speed 2. TAPRIO Configuration via "tc qdisc replace" - macb_taprio_setup_replace(): Configures TAPRIO hardware offload - Parameter validation checks performed: * TC entry limit validation against available hardware queues * Base time non-negativity enforcement * Speed-adaptive timing constraint verification * Cycle time vs. total gate time consistency checks * Single-queue gate mask enforcement per scheduling entry - Programming sequence: * GEM doesn't support changing ENST registers if ENST is enabled, hence disable ENST before programming * Atomic timing register configuration (START_TIME, ON_TIME, OFF_TIME) * Enable queues via ENST_CONTROL 3. TAPRIO Cleanup via "tc qdisc destroy" - macb_taprio_destroy(): Safely removes TAPRIO configuration - Restores default queue behavior - Cleanup steps: * Reset TC state * Disable ENST * Clear timing registers * Ensure atomic updates with locking 4. Traffic Control Offload Infrastructure - macb_setup_taprio(): TAPRIO command dispatcher * Verifies hardware support * Handles runtime suspend state - macb_setup_tc(): TC_SETUP_QDISC_TAPRIO entry point - Supports REPLACE and DESTROY operations Tested on Xilinx Versal platforms with QBV-capable MACB controllers. Signed-off-by: Vineeth Karumanchi <vineeth.karumanchi@amd.com> Link: https://patch.msgid.link/20250814071058.3062453-2-vineeth.karumanchi@amd.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
1 parent 6089970 commit 89934db

File tree

2 files changed

+289
-0
lines changed

2 files changed

+289
-0
lines changed

drivers/net/ethernet/cadence/macb.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,13 @@
184184
#define GEM_DCFG8 0x029C /* Design Config 8 */
185185
#define GEM_DCFG10 0x02A4 /* Design Config 10 */
186186
#define GEM_DCFG12 0x02AC /* Design Config 12 */
187+
#define GEM_ENST_START_TIME_Q0 0x0800 /* ENST Q0 start time */
188+
#define GEM_ENST_START_TIME_Q1 0x0804 /* ENST Q1 start time */
189+
#define GEM_ENST_ON_TIME_Q0 0x0820 /* ENST Q0 on time */
190+
#define GEM_ENST_ON_TIME_Q1 0x0824 /* ENST Q1 on time */
191+
#define GEM_ENST_OFF_TIME_Q0 0x0840 /* ENST Q0 off time */
192+
#define GEM_ENST_OFF_TIME_Q1 0x0844 /* ENST Q1 off time */
193+
#define GEM_ENST_CONTROL 0x0880 /* ENST control register */
187194
#define GEM_USX_CONTROL 0x0A80 /* High speed PCS control register */
188195
#define GEM_USX_STATUS 0x0A88 /* High speed PCS status register */
189196

@@ -221,6 +228,13 @@
221228
#define GEM_IDR(hw_q) (0x0620 + ((hw_q) << 2))
222229
#define GEM_IMR(hw_q) (0x0640 + ((hw_q) << 2))
223230

231+
#define GEM_ENST_START_TIME(hw_q) (0x0800 + ((hw_q) << 2))
232+
#define GEM_ENST_ON_TIME(hw_q) (0x0820 + ((hw_q) << 2))
233+
#define GEM_ENST_OFF_TIME(hw_q) (0x0840 + ((hw_q) << 2))
234+
235+
/* Bitfields in ENST_CONTROL */
236+
#define GEM_ENST_DISABLE_QUEUE_OFFSET 16
237+
224238
/* Bitfields in NCR */
225239
#define MACB_LB_OFFSET 0 /* reserved */
226240
#define MACB_LB_SIZE 1
@@ -554,6 +568,23 @@
554568
#define GEM_HIGH_SPEED_OFFSET 26
555569
#define GEM_HIGH_SPEED_SIZE 1
556570

571+
/* Bitfields in ENST_START_TIME_Qx. */
572+
#define GEM_START_TIME_SEC_OFFSET 30
573+
#define GEM_START_TIME_SEC_SIZE 2
574+
#define GEM_START_TIME_NSEC_OFFSET 0
575+
#define GEM_START_TIME_NSEC_SIZE 30
576+
577+
/* Bitfields in ENST_ON_TIME_Qx. */
578+
#define GEM_ON_TIME_OFFSET 0
579+
#define GEM_ON_TIME_SIZE 17
580+
581+
/* Bitfields in ENST_OFF_TIME_Qx. */
582+
#define GEM_OFF_TIME_OFFSET 0
583+
#define GEM_OFF_TIME_SIZE 17
584+
585+
/* Hardware ENST timing registers granularity */
586+
#define ENST_TIME_GRANULARITY_NS 8
587+
557588
/* Bitfields in USX_CONTROL. */
558589
#define GEM_USX_CTRL_SPEED_OFFSET 14
559590
#define GEM_USX_CTRL_SPEED_SIZE 3
@@ -1219,6 +1250,11 @@ struct macb_queue {
12191250
unsigned int RBQP;
12201251
unsigned int RBQPH;
12211252

1253+
/* ENST register offsets for this queue */
1254+
unsigned int ENST_START_TIME;
1255+
unsigned int ENST_ON_TIME;
1256+
unsigned int ENST_OFF_TIME;
1257+
12221258
/* Lock to protect tx_head and tx_tail */
12231259
spinlock_t tx_ptr_lock;
12241260
unsigned int tx_head, tx_tail;
@@ -1397,6 +1433,19 @@ static inline bool gem_has_ptp(struct macb *bp)
13971433
return IS_ENABLED(CONFIG_MACB_USE_HWSTAMP) && (bp->caps & MACB_CAPS_GEM_HAS_PTP);
13981434
}
13991435

1436+
/* ENST Helper functions */
1437+
static inline u64 enst_ns_to_hw_units(size_t ns, u32 speed_mbps)
1438+
{
1439+
return DIV_ROUND_UP((ns) * (speed_mbps),
1440+
(ENST_TIME_GRANULARITY_NS * 1000));
1441+
}
1442+
1443+
static inline u64 enst_max_hw_interval(u32 speed_mbps)
1444+
{
1445+
return DIV_ROUND_UP(GENMASK(GEM_ON_TIME_SIZE - 1, 0) *
1446+
ENST_TIME_GRANULARITY_NS * 1000, (speed_mbps));
1447+
}
1448+
14001449
/**
14011450
* struct macb_platform_data - platform data for MACB Ethernet used for PCI registration
14021451
* @pclk: platform clock
@@ -1407,4 +1456,21 @@ struct macb_platform_data {
14071456
struct clk *hclk;
14081457
};
14091458

1459+
/**
1460+
* struct macb_queue_enst_config - Configuration for Enhanced Scheduled Traffic
1461+
* @start_time_mask: Bitmask representing the start time for the queue
1462+
* @on_time_bytes: "on" time nsec expressed in bytes
1463+
* @off_time_bytes: "off" time nsec expressed in bytes
1464+
* @queue_id: Identifier for the queue
1465+
*
1466+
* This structure holds the configuration parameters for an ENST queue,
1467+
* used to control time-based transmission scheduling in the MACB driver.
1468+
*/
1469+
struct macb_queue_enst_config {
1470+
u32 start_time_mask;
1471+
u32 on_time_bytes;
1472+
u32 off_time_bytes;
1473+
u8 queue_id;
1474+
};
1475+
14101476
#endif /* _MACB_H */

drivers/net/ethernet/cadence/macb_main.c

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <linux/reset.h>
3737
#include <linux/firmware/xlnx-zynqmp.h>
3838
#include <linux/inetdevice.h>
39+
#include <net/pkt_sched.h>
3940
#include "macb.h"
4041

4142
/* This structure is only used for MACB on SiFive FU540 devices */
@@ -4084,6 +4085,223 @@ static void macb_restore_features(struct macb *bp)
40844085
macb_set_rxflow_feature(bp, features);
40854086
}
40864087

4088+
static int macb_taprio_setup_replace(struct net_device *ndev,
4089+
struct tc_taprio_qopt_offload *conf)
4090+
{
4091+
u64 total_on_time = 0, start_time_sec = 0, start_time = conf->base_time;
4092+
u32 configured_queues = 0, speed = 0, start_time_nsec;
4093+
struct macb_queue_enst_config *enst_queue;
4094+
struct tc_taprio_sched_entry *entry;
4095+
struct macb *bp = netdev_priv(ndev);
4096+
struct ethtool_link_ksettings kset;
4097+
struct macb_queue *queue;
4098+
size_t i;
4099+
int err;
4100+
4101+
if (conf->num_entries > bp->num_queues) {
4102+
netdev_err(ndev, "Too many TAPRIO entries: %zu > %d queues\n",
4103+
conf->num_entries, bp->num_queues);
4104+
return -EINVAL;
4105+
}
4106+
4107+
if (start_time < 0) {
4108+
netdev_err(ndev, "Invalid base_time: must be 0 or positive, got %lld\n",
4109+
conf->base_time);
4110+
return -ERANGE;
4111+
}
4112+
4113+
/* Get the current link speed */
4114+
err = phylink_ethtool_ksettings_get(bp->phylink, &kset);
4115+
if (unlikely(err)) {
4116+
netdev_err(ndev, "Failed to get link settings: %d\n", err);
4117+
return err;
4118+
}
4119+
4120+
speed = kset.base.speed;
4121+
if (unlikely(speed <= 0)) {
4122+
netdev_err(ndev, "Invalid speed: %d\n", speed);
4123+
return -EINVAL;
4124+
}
4125+
4126+
enst_queue = kcalloc(conf->num_entries, sizeof(*enst_queue), GFP_KERNEL);
4127+
if (unlikely(!enst_queue))
4128+
return -ENOMEM;
4129+
4130+
/* Pre-validate all entries before making any hardware changes */
4131+
for (i = 0; i < conf->num_entries; i++) {
4132+
entry = &conf->entries[i];
4133+
4134+
if (entry->command != TC_TAPRIO_CMD_SET_GATES) {
4135+
netdev_err(ndev, "Entry %zu: unsupported command %d\n",
4136+
i, entry->command);
4137+
err = -EOPNOTSUPP;
4138+
goto cleanup;
4139+
}
4140+
4141+
/* Validate gate_mask: must be nonzero, single queue, and within range */
4142+
if (!is_power_of_2(entry->gate_mask)) {
4143+
netdev_err(ndev, "Entry %zu: gate_mask 0x%x is not a power of 2 (only one queue per entry allowed)\n",
4144+
i, entry->gate_mask);
4145+
err = -EINVAL;
4146+
goto cleanup;
4147+
}
4148+
4149+
/* gate_mask must not select queues outside the valid queue_mask */
4150+
if (entry->gate_mask & ~bp->queue_mask) {
4151+
netdev_err(ndev, "Entry %zu: gate_mask 0x%x exceeds queue range (max_queues=%d)\n",
4152+
i, entry->gate_mask, bp->num_queues);
4153+
err = -EINVAL;
4154+
goto cleanup;
4155+
}
4156+
4157+
/* Check for start time limits */
4158+
start_time_sec = start_time;
4159+
start_time_nsec = do_div(start_time_sec, NSEC_PER_SEC);
4160+
if (start_time_sec > GENMASK(GEM_START_TIME_SEC_SIZE - 1, 0)) {
4161+
netdev_err(ndev, "Entry %zu: Start time %llu s exceeds hardware limit\n",
4162+
i, start_time_sec);
4163+
err = -ERANGE;
4164+
goto cleanup;
4165+
}
4166+
4167+
/* Check for on time limit */
4168+
if (entry->interval > enst_max_hw_interval(speed)) {
4169+
netdev_err(ndev, "Entry %zu: interval %u ns exceeds hardware limit %llu ns\n",
4170+
i, entry->interval, enst_max_hw_interval(speed));
4171+
err = -ERANGE;
4172+
goto cleanup;
4173+
}
4174+
4175+
/* Check for off time limit*/
4176+
if ((conf->cycle_time - entry->interval) > enst_max_hw_interval(speed)) {
4177+
netdev_err(ndev, "Entry %zu: off_time %llu ns exceeds hardware limit %llu ns\n",
4178+
i, conf->cycle_time - entry->interval,
4179+
enst_max_hw_interval(speed));
4180+
err = -ERANGE;
4181+
goto cleanup;
4182+
}
4183+
4184+
enst_queue[i].queue_id = order_base_2(entry->gate_mask);
4185+
enst_queue[i].start_time_mask =
4186+
(start_time_sec << GEM_START_TIME_SEC_OFFSET) |
4187+
start_time_nsec;
4188+
enst_queue[i].on_time_bytes =
4189+
enst_ns_to_hw_units(entry->interval, speed);
4190+
enst_queue[i].off_time_bytes =
4191+
enst_ns_to_hw_units(conf->cycle_time - entry->interval, speed);
4192+
4193+
configured_queues |= entry->gate_mask;
4194+
total_on_time += entry->interval;
4195+
start_time += entry->interval;
4196+
}
4197+
4198+
/* Check total interval doesn't exceed cycle time */
4199+
if (total_on_time > conf->cycle_time) {
4200+
netdev_err(ndev, "Total ON %llu ns exceeds cycle time %llu ns\n",
4201+
total_on_time, conf->cycle_time);
4202+
err = -EINVAL;
4203+
goto cleanup;
4204+
}
4205+
4206+
netdev_dbg(ndev, "TAPRIO setup: %zu entries, base_time=%lld ns, cycle_time=%llu ns\n",
4207+
conf->num_entries, conf->base_time, conf->cycle_time);
4208+
4209+
/* All validations passed - proceed with hardware configuration */
4210+
scoped_guard(spinlock_irqsave, &bp->lock) {
4211+
/* Disable ENST queues if running before configuring */
4212+
gem_writel(bp, ENST_CONTROL,
4213+
bp->queue_mask << GEM_ENST_DISABLE_QUEUE_OFFSET);
4214+
4215+
for (i = 0; i < conf->num_entries; i++) {
4216+
queue = &bp->queues[enst_queue[i].queue_id];
4217+
/* Configure queue timing registers */
4218+
queue_writel(queue, ENST_START_TIME,
4219+
enst_queue[i].start_time_mask);
4220+
queue_writel(queue, ENST_ON_TIME,
4221+
enst_queue[i].on_time_bytes);
4222+
queue_writel(queue, ENST_OFF_TIME,
4223+
enst_queue[i].off_time_bytes);
4224+
}
4225+
4226+
/* Enable ENST for all configured queues in one write */
4227+
gem_writel(bp, ENST_CONTROL, configured_queues);
4228+
}
4229+
4230+
netdev_info(ndev, "TAPRIO configuration completed successfully: %zu entries, %d queues configured\n",
4231+
conf->num_entries, hweight32(configured_queues));
4232+
4233+
cleanup:
4234+
kfree(enst_queue);
4235+
return err;
4236+
}
4237+
4238+
static void macb_taprio_destroy(struct net_device *ndev)
4239+
{
4240+
struct macb *bp = netdev_priv(ndev);
4241+
struct macb_queue *queue;
4242+
u32 enst_disable_mask;
4243+
unsigned int q;
4244+
4245+
netdev_reset_tc(ndev);
4246+
enst_disable_mask = bp->queue_mask << GEM_ENST_DISABLE_QUEUE_OFFSET;
4247+
4248+
scoped_guard(spinlock_irqsave, &bp->lock) {
4249+
/* Single disable command for all queues */
4250+
gem_writel(bp, ENST_CONTROL, enst_disable_mask);
4251+
4252+
/* Clear all queue ENST registers in batch */
4253+
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
4254+
queue_writel(queue, ENST_START_TIME, 0);
4255+
queue_writel(queue, ENST_ON_TIME, 0);
4256+
queue_writel(queue, ENST_OFF_TIME, 0);
4257+
}
4258+
}
4259+
netdev_info(ndev, "TAPRIO destroy: All gates disabled\n");
4260+
}
4261+
4262+
static int macb_setup_taprio(struct net_device *ndev,
4263+
struct tc_taprio_qopt_offload *taprio)
4264+
{
4265+
struct macb *bp = netdev_priv(ndev);
4266+
int err = 0;
4267+
4268+
if (unlikely(!(ndev->hw_features & NETIF_F_HW_TC)))
4269+
return -EOPNOTSUPP;
4270+
4271+
/* Check if Device is in runtime suspend */
4272+
if (unlikely(pm_runtime_suspended(&bp->pdev->dev))) {
4273+
netdev_err(ndev, "Device is in runtime suspend\n");
4274+
return -EOPNOTSUPP;
4275+
}
4276+
4277+
switch (taprio->cmd) {
4278+
case TAPRIO_CMD_REPLACE:
4279+
err = macb_taprio_setup_replace(ndev, taprio);
4280+
break;
4281+
case TAPRIO_CMD_DESTROY:
4282+
macb_taprio_destroy(ndev);
4283+
break;
4284+
default:
4285+
err = -EOPNOTSUPP;
4286+
}
4287+
4288+
return err;
4289+
}
4290+
4291+
static int macb_setup_tc(struct net_device *dev, enum tc_setup_type type,
4292+
void *type_data)
4293+
{
4294+
if (!dev || !type_data)
4295+
return -EINVAL;
4296+
4297+
switch (type) {
4298+
case TC_SETUP_QDISC_TAPRIO:
4299+
return macb_setup_taprio(dev, type_data);
4300+
default:
4301+
return -EOPNOTSUPP;
4302+
}
4303+
}
4304+
40874305
static const struct net_device_ops macb_netdev_ops = {
40884306
.ndo_open = macb_open,
40894307
.ndo_stop = macb_close,
@@ -4101,6 +4319,7 @@ static const struct net_device_ops macb_netdev_ops = {
41014319
.ndo_features_check = macb_features_check,
41024320
.ndo_hwtstamp_set = macb_hwtstamp_set,
41034321
.ndo_hwtstamp_get = macb_hwtstamp_get,
4322+
.ndo_setup_tc = macb_setup_tc,
41044323
};
41054324

41064325
/* Configure peripheral capabilities according to device tree
@@ -4327,6 +4546,10 @@ static int macb_init(struct platform_device *pdev)
43274546
#endif
43284547
}
43294548

4549+
queue->ENST_START_TIME = GEM_ENST_START_TIME(hw_q);
4550+
queue->ENST_ON_TIME = GEM_ENST_ON_TIME(hw_q);
4551+
queue->ENST_OFF_TIME = GEM_ENST_OFF_TIME(hw_q);
4552+
43304553
/* get irq: here we use the linux queue index, not the hardware
43314554
* queue index. the queue irq definitions in the device tree
43324555
* must remove the optional gaps that could exist in the

0 commit comments

Comments
 (0)