net: dsa: felix: configure default-prio and dscp priorities
authorVladimir Oltean <vladimir.oltean@nxp.com>
Fri, 11 Mar 2022 21:15:20 +0000 (23:15 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 14 Mar 2022 10:36:15 +0000 (10:36 +0000)
Follow the established programming model for this driver and provide
shims in the felix DSA driver which call the implementations from the
ocelot switch lib. The ocelot switchdev driver wasn't integrated with
dcbnl due to lack of hardware availability.

The switch doesn't have any fancy QoS classification enabled by default.
The provided getters will create a default-prio app table entry of 0,
and no dscp entry. However, the getters have been made to actually
retrieve the hardware configuration rather than static values, to be
future proof in case DSA will need this information from more call paths.

For default-prio, there is a single field per port, in ANA_PORT_QOS_CFG,
called QOS_DEFAULT_VAL.

DSCP classification is enabled per-port, again via ANA_PORT_QOS_CFG
(field QOS_DSCP_ENA), and individual DSCP values are configured as
trusted or not through register ANA_DSCP_CFG (replicated 64 times).
An untrusted DSCP value falls back to other QoS classification methods.
If trusted, the selected ANA_DSCP_CFG register also holds the QoS class
in the QOS_DSCP_VAL field.

The hardware also supports DSCP remapping (DSCP value X is translated to
DSCP value Y before the QoS class is determined based on the app table
entry for Y) and DSCP packet rewriting. The dcbnl framework, for being
so flexible in other useless areas, doesn't appear to support this.
So this functionality has been left out.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/ocelot/felix.c
drivers/net/ethernet/mscc/ocelot.c
include/soc/mscc/ocelot.h

index 35b436a..13d6b17 100644 (file)
@@ -1799,6 +1799,44 @@ felix_mrp_del_ring_role(struct dsa_switch *ds, int port,
        return ocelot_mrp_del_ring_role(ocelot, port, mrp);
 }
 
+static int felix_port_get_default_prio(struct dsa_switch *ds, int port)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_port_get_default_prio(ocelot, port);
+}
+
+static int felix_port_set_default_prio(struct dsa_switch *ds, int port,
+                                      u8 prio)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_port_set_default_prio(ocelot, port, prio);
+}
+
+static int felix_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_port_get_dscp_prio(ocelot, port, dscp);
+}
+
+static int felix_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp,
+                                   u8 prio)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_port_add_dscp_prio(ocelot, port, dscp, prio);
+}
+
+static int felix_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp,
+                                   u8 prio)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_port_del_dscp_prio(ocelot, port, dscp, prio);
+}
+
 const struct dsa_switch_ops felix_switch_ops = {
        .get_tag_protocol               = felix_get_tag_protocol,
        .change_tag_protocol            = felix_change_tag_protocol,
@@ -1862,6 +1900,11 @@ const struct dsa_switch_ops felix_switch_ops = {
        .port_mrp_del_ring_role         = felix_mrp_del_ring_role,
        .tag_8021q_vlan_add             = felix_tag_8021q_vlan_add,
        .tag_8021q_vlan_del             = felix_tag_8021q_vlan_del,
+       .port_get_default_prio          = felix_port_get_default_prio,
+       .port_set_default_prio          = felix_port_set_default_prio,
+       .port_get_dscp_prio             = felix_port_get_dscp_prio,
+       .port_add_dscp_prio             = felix_port_add_dscp_prio,
+       .port_del_dscp_prio             = felix_port_del_dscp_prio,
 };
 
 struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port)
index 2113412..41dbb1e 100644 (file)
@@ -2907,6 +2907,122 @@ void ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
 }
 EXPORT_SYMBOL(ocelot_port_bridge_flags);
 
+int ocelot_port_get_default_prio(struct ocelot *ocelot, int port)
+{
+       int val = ocelot_read_gix(ocelot, ANA_PORT_QOS_CFG, port);
+
+       return ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL_X(val);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_get_default_prio);
+
+int ocelot_port_set_default_prio(struct ocelot *ocelot, int port, u8 prio)
+{
+       if (prio >= IEEE_8021QAZ_MAX_TCS)
+               return -ERANGE;
+
+       ocelot_rmw_gix(ocelot,
+                      ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL(prio),
+                      ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL_M,
+                      ANA_PORT_QOS_CFG,
+                      port);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ocelot_port_set_default_prio);
+
+int ocelot_port_get_dscp_prio(struct ocelot *ocelot, int port, u8 dscp)
+{
+       int qos_cfg = ocelot_read_gix(ocelot, ANA_PORT_QOS_CFG, port);
+       int dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, dscp);
+
+       /* Return error if DSCP prioritization isn't enabled */
+       if (!(qos_cfg & ANA_PORT_QOS_CFG_QOS_DSCP_ENA))
+               return -EOPNOTSUPP;
+
+       if (qos_cfg & ANA_PORT_QOS_CFG_DSCP_TRANSLATE_ENA) {
+               dscp = ANA_DSCP_CFG_DSCP_TRANSLATE_VAL_X(dscp_cfg);
+               /* Re-read ANA_DSCP_CFG for the translated DSCP */
+               dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, dscp);
+       }
+
+       /* If the DSCP value is not trusted, the QoS classification falls back
+        * to VLAN PCP or port-based default.
+        */
+       if (!(dscp_cfg & ANA_DSCP_CFG_DSCP_TRUST_ENA))
+               return -EOPNOTSUPP;
+
+       return ANA_DSCP_CFG_QOS_DSCP_VAL_X(dscp_cfg);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_get_dscp_prio);
+
+int ocelot_port_add_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio)
+{
+       int mask, val;
+
+       if (prio >= IEEE_8021QAZ_MAX_TCS)
+               return -ERANGE;
+
+       /* There is at least one app table priority (this one), so we need to
+        * make sure DSCP prioritization is enabled on the port.
+        * Also make sure DSCP translation is disabled
+        * (dcbnl doesn't support it).
+        */
+       mask = ANA_PORT_QOS_CFG_QOS_DSCP_ENA |
+              ANA_PORT_QOS_CFG_DSCP_TRANSLATE_ENA;
+
+       ocelot_rmw_gix(ocelot, ANA_PORT_QOS_CFG_QOS_DSCP_ENA, mask,
+                      ANA_PORT_QOS_CFG, port);
+
+       /* Trust this DSCP value and map it to the given QoS class */
+       val = ANA_DSCP_CFG_DSCP_TRUST_ENA | ANA_DSCP_CFG_QOS_DSCP_VAL(prio);
+
+       ocelot_write_rix(ocelot, val, ANA_DSCP_CFG, dscp);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ocelot_port_add_dscp_prio);
+
+int ocelot_port_del_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio)
+{
+       int dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, dscp);
+       int mask, i;
+
+       /* During a "dcb app replace" command, the new app table entry will be
+        * added first, then the old one will be deleted. But the hardware only
+        * supports one QoS class per DSCP value (duh), so if we blindly delete
+        * the app table entry for this DSCP value, we end up deleting the
+        * entry with the new priority. Avoid that by checking whether user
+        * space wants to delete the priority which is currently configured, or
+        * something else which is no longer current.
+        */
+       if (ANA_DSCP_CFG_QOS_DSCP_VAL_X(dscp_cfg) != prio)
+               return 0;
+
+       /* Untrust this DSCP value */
+       ocelot_write_rix(ocelot, 0, ANA_DSCP_CFG, dscp);
+
+       for (i = 0; i < 64; i++) {
+               int dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, i);
+
+               /* There are still app table entries on the port, so we need to
+                * keep DSCP enabled, nothing to do.
+                */
+               if (dscp_cfg & ANA_DSCP_CFG_DSCP_TRUST_ENA)
+                       return 0;
+       }
+
+       /* Disable DSCP QoS classification if there isn't any trusted
+        * DSCP value left.
+        */
+       mask = ANA_PORT_QOS_CFG_QOS_DSCP_ENA |
+              ANA_PORT_QOS_CFG_DSCP_TRANSLATE_ENA;
+
+       ocelot_rmw_gix(ocelot, 0, mask, ANA_PORT_QOS_CFG, port);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ocelot_port_del_dscp_prio);
+
 void ocelot_init_port(struct ocelot *ocelot, int port)
 {
        struct ocelot_port *ocelot_port = ocelot->ports[port];
index ee3c596..4d51e2a 100644 (file)
@@ -869,6 +869,11 @@ int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port,
                                 struct switchdev_brport_flags val);
 void ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
                              struct switchdev_brport_flags val);
+int ocelot_port_get_default_prio(struct ocelot *ocelot, int port);
+int ocelot_port_set_default_prio(struct ocelot *ocelot, int port, u8 prio);
+int ocelot_port_get_dscp_prio(struct ocelot *ocelot, int port, u8 dscp);
+int ocelot_port_add_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio);
+int ocelot_port_del_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio);
 int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
                            struct net_device *bridge, int bridge_num,
                            struct netlink_ext_ack *extack);