Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
From: Vladimir Oltean <olteanv@gmail.com>
Date: 2020-08-22 14:39:30
Also in:
linux-devicetree
Hi Kurt, On Thu, Aug 20, 2020 at 10:11:15AM +0200, Kurt Kanzenbach wrote:
quoted hunk ↗ jump to hunk
The switch has support for the 802.1Qbv Time Aware Shaper (TAS). Traffic schedules may be configured individually on each front port. Each port has eight egress queues. The traffic is mapped to a traffic class respectively via the PCP field of a VLAN tagged frame. The TAPRIO Qdisc already implements that. Therefore, this interface can simply be reused. Add .port_setup_tc() accordingly. The activation of a schedule on a port is split into two parts: * Programming the necessary gate control list (GCL) * Setup hrtimer for starting the schedule The hardware supports starting a schedule up to eight seconds in the future. The TAPRIO interface provides an absolute base time. Therefore, hrtimers are leveraged. Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de> --- drivers/net/dsa/hirschmann/hellcreek.c | 294 +++++++++++++++++++++++++ drivers/net/dsa/hirschmann/hellcreek.h | 21 ++ 2 files changed, 315 insertions(+)diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c index 745ca60342b4..e5b54f42c635 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.c +++ b/drivers/net/dsa/hirschmann/hellcreek.c@@ -22,7 +22,9 @@ #include <linux/spinlock.h> #include <linux/delay.h> #include <linux/ktime.h> +#include <linux/time.h> #include <net/dsa.h> +#include <net/pkt_sched.h> #include "hellcreek.h" #include "hellcreek_ptp.h"@@ -153,6 +155,15 @@ static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid, hellcreek_write(hellcreek, val, HR_VIDCFG); } +static void hellcreek_select_tgd(struct hellcreek *hellcreek, int port) +{ + u16 val = 0; + + val |= port << TR_TGDSEL_TDGSEL_SHIFT; + + hellcreek_write(hellcreek, val, TR_TGDSEL); +} + static int hellcreek_wait_until_ready(struct hellcreek *hellcreek) { u16 val;@@ -958,6 +969,24 @@ static void __hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek) } } +static void hellcreek_setup_tc_mapping(struct hellcreek *hellcreek, + struct net_device *netdev) +{ + int i, j; + + /* Setup mapping between traffic classes and port queues. */ + for (i = 0; i < netdev_get_num_tc(netdev); ++i) { + for (j = 0; j < netdev->tc_to_txq[i].count; ++j) { + const int queue = j + netdev->tc_to_txq[i].offset; + + hellcreek_select_prio(hellcreek, i); + hellcreek_write(hellcreek, + queue << HR_PRTCCFG_PCP_TC_MAP_SHIFT, + HR_PRTCCFG); + } + } +}
What other driver have you seen that does this?
+
+static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
+ const struct tc_taprio_qopt_offload *taprio)
+{
+ struct net_device *netdev = dsa_to_port(ds, port)->slave;
+ struct hellcreek *hellcreek = ds->priv;
+ struct hellcreek_port *hellcreek_port;
+ struct hellcreek_schedule *schedule;
+ unsigned long flags;
+ ktime_t start;
+ u16 ctrl;
+
+ hellcreek_port = &hellcreek->ports[port];
+
+ /* Convert taprio data to hellcreek schedule */
+ schedule = hellcreek_taprio_to_schedule(taprio);
+ if (IS_ERR(schedule))
+ return PTR_ERR(schedule);
+
+ dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n",
+ port);
+
+ /* Cancel an in flight timer */
+ hrtimer_cancel(&hellcreek_port->cycle_start_timer);
+
+ spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+ if (hellcreek_port->current_schedule) {
+ kfree(hellcreek_port->current_schedule->entries);
+ kfree(hellcreek_port->current_schedule);
+ }
+
+ hellcreek_port->current_schedule = schedule;
+
+ /* First select port */
+ hellcreek_select_tgd(hellcreek, port);
+
+ /* Setup traffic class <-> queue mapping */
+ hellcreek_setup_tc_mapping(hellcreek, netdev);
+
+ /* Enable gating and set the admin state to forward everything in the
+ * mean time
+ */
+ ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN;
+ hellcreek_write(hellcreek, ctrl, TR_TGDCTRL);
+
+ /* Cancel pending schedule */
+ hellcreek_write(hellcreek, 0x00, TR_ESTCMD);
+
+ /* Setup a new schedule */
+ hellcreek_setup_gcl(hellcreek, port, schedule);
+
+ /* Configure cycle time */
+ hellcreek_set_cycle_time(hellcreek, schedule);
+
+ /* Setup timer for schedule switch: The IP core only allows to set a
+ * cycle start timer 8 seconds in the future. This is why we setup the
+ * hritmer to base_time - 5 seconds. Then, we have enough time to
+ * activate IP core's EST timer.
+ */
+ start = ktime_sub_ns(schedule->base_time, (u64)5 * NSEC_PER_SEC);
+ hrtimer_start_range_ns(&hellcreek_port->cycle_start_timer, start,
+ NSEC_PER_SEC, HRTIMER_MODE_ABS);
+
+ spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+ return 0;
+}Thanks, -Vladimir