net: dsa: hellcreek: Offload per-tc max SDU from tc-taprio
authorKurt Kanzenbach <kurt@linutronix.de>
Wed, 28 Sep 2022 09:52:01 +0000 (12:52 +0300)
committerJakub Kicinski <kuba@kernel.org>
Fri, 30 Sep 2022 01:52:06 +0000 (18:52 -0700)
Add support for configuring the max SDU per priority and per port. If not
specified, keep the default.

Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/dsa/hirschmann/hellcreek.c
drivers/net/dsa/hirschmann/hellcreek.h

index 19e61d4112b30b0ebf833d725230516673448646..951f7935c872a600e680bebd3905639c43b74df6 100644 (file)
@@ -128,6 +128,16 @@ static void hellcreek_select_prio(struct hellcreek *hellcreek, int prio)
        hellcreek_write(hellcreek, val, HR_PSEL);
 }
 
+static void hellcreek_select_port_prio(struct hellcreek *hellcreek, int port,
+                                      int prio)
+{
+       u16 val = port << HR_PSEL_PTWSEL_SHIFT;
+
+       val |= prio << HR_PSEL_PRTCWSEL_SHIFT;
+
+       hellcreek_write(hellcreek, val, HR_PSEL);
+}
+
 static void hellcreek_select_counter(struct hellcreek *hellcreek, int counter)
 {
        u16 val = counter << HR_CSEL_SHIFT;
@@ -1537,6 +1547,45 @@ out:
        return ret;
 }
 
+static void hellcreek_setup_maxsdu(struct hellcreek *hellcreek, int port,
+                                  const struct tc_taprio_qopt_offload *schedule)
+{
+       int tc;
+
+       for (tc = 0; tc < 8; ++tc) {
+               u32 max_sdu = schedule->max_sdu[tc] + VLAN_ETH_HLEN - ETH_FCS_LEN;
+               u16 val;
+
+               if (!schedule->max_sdu[tc])
+                       continue;
+
+               dev_dbg(hellcreek->dev, "Configure max-sdu %u for tc %d on port %d\n",
+                       max_sdu, tc, port);
+
+               hellcreek_select_port_prio(hellcreek, port, tc);
+
+               val = (max_sdu & HR_PTPRTCCFG_MAXSDU_MASK) << HR_PTPRTCCFG_MAXSDU_SHIFT;
+
+               hellcreek_write(hellcreek, val, HR_PTPRTCCFG);
+       }
+}
+
+static void hellcreek_reset_maxsdu(struct hellcreek *hellcreek, int port)
+{
+       int tc;
+
+       for (tc = 0; tc < 8; ++tc) {
+               u16 val;
+
+               hellcreek_select_port_prio(hellcreek, port, tc);
+
+               val = (HELLCREEK_DEFAULT_MAX_SDU & HR_PTPRTCCFG_MAXSDU_MASK)
+                       << HR_PTPRTCCFG_MAXSDU_SHIFT;
+
+               hellcreek_write(hellcreek, val, HR_PTPRTCCFG);
+       }
+}
+
 static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port,
                                const struct tc_taprio_qopt_offload *schedule)
 {
@@ -1720,7 +1769,10 @@ static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
        }
        hellcreek_port->current_schedule = taprio_offload_get(taprio);
 
-       /* Then select port */
+       /* Configure max sdu */
+       hellcreek_setup_maxsdu(hellcreek, port, hellcreek_port->current_schedule);
+
+       /* Select tdg */
        hellcreek_select_tgd(hellcreek, port);
 
        /* Enable gating and keep defaults */
@@ -1772,7 +1824,10 @@ static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port)
                hellcreek_port->current_schedule = NULL;
        }
 
-       /* Then select port */
+       /* Reset max sdu */
+       hellcreek_reset_maxsdu(hellcreek, port);
+
+       /* Select tgd */
        hellcreek_select_tgd(hellcreek, port);
 
        /* Disable gating and return to regular switching flow */
@@ -1809,12 +1864,29 @@ static bool hellcreek_validate_schedule(struct hellcreek *hellcreek,
        return true;
 }
 
+static int hellcreek_tc_query_caps(struct tc_query_caps_base *base)
+{
+       switch (base->type) {
+       case TC_SETUP_QDISC_TAPRIO: {
+               struct tc_taprio_caps *caps = base->caps;
+
+               caps->supports_queue_max_sdu = true;
+
+               return 0;
+       }
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port,
                                   enum tc_setup_type type, void *type_data)
 {
        struct hellcreek *hellcreek = ds->priv;
 
        switch (type) {
+       case TC_QUERY_CAPS:
+               return hellcreek_tc_query_caps(type_data);
        case TC_SETUP_QDISC_TAPRIO: {
                struct tc_taprio_qopt_offload *taprio = type_data;
 
index 9e303b8ab13c9e5dc6b281642b95b67bb605e435..4a678f7d61ae0fd3ed461f0a2653be3de427d238 100644 (file)
@@ -37,6 +37,7 @@
 #define HELLCREEK_VLAN_UNTAGGED_MEMBER 0x1
 #define HELLCREEK_VLAN_TAGGED_MEMBER   0x3
 #define HELLCREEK_NUM_EGRESS_QUEUES    8
+#define HELLCREEK_DEFAULT_MAX_SDU      1536
 
 /* Register definitions */
 #define HR_MODID_C                     (0 * 2)
 #define HR_PRTCCFG_PCP_TC_MAP_SHIFT    0
 #define HR_PRTCCFG_PCP_TC_MAP_MASK     GENMASK(2, 0)
 
+#define HR_PTPRTCCFG                   (0xa9 * 2)
+#define HR_PTPRTCCFG_SET_QTRACK                BIT(15)
+#define HR_PTPRTCCFG_REJECT            BIT(14)
+#define HR_PTPRTCCFG_MAXSDU_SHIFT      0
+#define HR_PTPRTCCFG_MAXSDU_MASK       GENMASK(10, 0)
+
 #define HR_CSEL                                (0x8d * 2)
 #define HR_CSEL_SHIFT                  0
 #define HR_CSEL_MASK                   GENMASK(7, 0)