* [PATCH net-next v3 0/4] net: dsa: motorcomm: TC offload follow-ups
@ 2026-10-09 16:58 David Yang
2026-10-09 16:58 ` [PATCH net-next v3 1/4] net: dsa: motorcomm: Split TC module David Yang
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: David Yang @ 2026-10-09 16:58 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
This series continues the traffic control work on the Motorcomm YT9215S
DSA driver: it reorganizes the offload code and then adds ACL enablement
on demand and flower priority mapping.
v3:
- rebase
- reword commit messages and comments
- fix cleanup path in acl_add()
v2: https://lore.kernel.org/r/20261001204851.2576101-1-mmyangfl@gmail.com
- move type casting helper into chip.h
- remove flow statistics and broadcast/multicast policers
v1: https://lore.kernel.org/r/20260926211813.935723-1-mmyangfl@gmail.com
David Yang (4):
net: dsa: motorcomm: Split TC module
net: dsa: motorcomm: Use NSEC_PER_SEC
net: dsa: motorcomm: Enable ACL on demand
net: dsa: motorcomm: Add TC prio support
drivers/net/dsa/motorcomm/Makefile | 1 +
drivers/net/dsa/motorcomm/chip.c | 1662 ++--------------------------
drivers/net/dsa/motorcomm/chip.h | 352 +-----
drivers/net/dsa/motorcomm/tc.c | 1583 ++++++++++++++++++++++++++
drivers/net/dsa/motorcomm/tc.h | 381 +++++++
5 files changed, 2059 insertions(+), 1920 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/tc.c
create mode 100644 drivers/net/dsa/motorcomm/tc.h
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net-next v3 1/4] net: dsa: motorcomm: Split TC module
2026-10-09 16:58 [PATCH net-next v3 0/4] net: dsa: motorcomm: TC offload follow-ups David Yang
@ 2026-10-09 16:58 ` David Yang
2026-10-09 16:58 ` [PATCH net-next v3 2/4] net: dsa: motorcomm: Use NSEC_PER_SEC David Yang
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: David Yang @ 2026-10-09 16:58 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
Move the traffic control code from chip.c into a separate tc.c, to
reduce the length of chip.c.
Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/motorcomm/Makefile | 1 +
drivers/net/dsa/motorcomm/chip.c | 1662 ++--------------------------
drivers/net/dsa/motorcomm/chip.h | 350 +-----
drivers/net/dsa/motorcomm/tc.c | 1496 +++++++++++++++++++++++++
drivers/net/dsa/motorcomm/tc.h | 380 +++++++
5 files changed, 1969 insertions(+), 1920 deletions(-)
create mode 100644 drivers/net/dsa/motorcomm/tc.c
create mode 100644 drivers/net/dsa/motorcomm/tc.h
diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
index 9c1e3d538d21..9eaa59f699e8 100644
--- a/drivers/net/dsa/motorcomm/Makefile
+++ b/drivers/net/dsa/motorcomm/Makefile
@@ -6,3 +6,4 @@ yt921x-objs += mdio_bus.o
yt921x-objs += mib.o
yt921x-objs += pcs-921x.o
yt921x-objs += smi.o
+yt921x-objs += tc.o
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 91cc5647f2b6..e2b5c89f7e77 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -24,7 +24,6 @@
#include <net/dsa.h>
#include <net/dscp.h>
#include <net/ieee8021q.h>
-#include <net/pkt_cls.h>
#include "chip.h"
#include "leds.h"
@@ -32,6 +31,7 @@
#include "mib.h"
#include "pcs.h"
#include "smi.h"
+#include "tc.h"
static const struct yt921x_info yt921x_infos[] = {
{
@@ -98,16 +98,6 @@ struct yt921x_reg_mdio {
/* TODO: SPI/I2C */
-static u32 ethaddr_hi4_to_u32(const unsigned char *addr)
-{
- return (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3];
-}
-
-static u32 ethaddr_lo2_to_u32(const unsigned char *addr)
-{
- return (addr[4] << 8) | addr[5];
-}
-
static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
{
struct yt921x_reg_mdio *mdio = context;
@@ -132,1330 +122,100 @@ static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
YT921X_SMI_READ;
- res = __mdiobus_read(bus, addr, reg_data);
- if (res < 0)
- goto end;
- val = (u16)res;
- res = __mdiobus_read(bus, addr, reg_data);
- if (res < 0)
- goto end;
- val = (val << 16) | (u16)res;
-
- *valp = val;
- res = 0;
-
-end:
- mutex_unlock(&bus->mdio_lock);
- return res;
-}
-
-static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val)
-{
- struct yt921x_reg_mdio *mdio = context;
- struct mii_bus *bus = mdio->bus;
- int addr = mdio->addr;
- u32 reg_addr;
- u32 reg_data;
- int res;
-
- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-
- reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
- YT921X_SMI_WRITE;
- res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
- if (res)
- goto end;
- res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
- if (res)
- goto end;
-
- reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
- YT921X_SMI_WRITE;
- res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16));
- if (res)
- goto end;
- res = __mdiobus_write(bus, addr, reg_data, (u16)val);
- if (res)
- goto end;
-
- res = 0;
-
-end:
- mutex_unlock(&bus->mdio_lock);
- return res;
-}
-
-static const struct yt921x_reg_ops yt921x_reg_ops_mdio = {
- .read = yt921x_reg_mdio_read,
- .write = yt921x_reg_mdio_write,
-};
-
-/* TODO: SPI/I2C */
-
-static int
-yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)
-{
- /* Poor datasheet for EEE operations; don't ask if you are confused */
-
- bool enable = e->eee_enabled;
- u16 new_mask;
- int res;
-
- /* Enable / disable global EEE */
- new_mask = priv->eee_ports_mask;
- new_mask &= ~BIT(port);
- new_mask |= !enable ? 0 : BIT(port);
-
- if (!!new_mask != !!priv->eee_ports_mask) {
- res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC,
- YT921X_PON_STRAP_EEE, !!new_mask);
- if (res)
- return res;
- res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL,
- YT921X_PON_STRAP_EEE, !!new_mask);
- if (res)
- return res;
- }
-
- priv->eee_ports_mask = new_mask;
-
- /* Enable / disable port EEE */
- res = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL,
- YT921X_EEE_CTRL_ENn(port), enable);
- if (res)
- return res;
- res = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port),
- YT921X_EEE_VAL_DATA, enable);
- if (res)
- return res;
-
- return 0;
-}
-
-static int
-yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e)
-{
- struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
- int res;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_set_eee(priv, port, e);
- mutex_unlock(&priv->reg_lock);
-
- return res;
-}
-
-static int yt921x_mtu_fetch(struct yt921x_priv *priv, int port)
-{
- struct dsa_port *dp = dsa_to_port(&priv->ds, port);
-
- return dp->user ? READ_ONCE(dp->user->mtu) : ETH_DATA_LEN;
-}
-
-static int
-yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
-{
- /* Only serves as packet filter, since the frame size is always set to
- * maximum after reset
- */
-
- struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
- struct dsa_port *dp = dsa_to_port(ds, port);
- int frame_size;
- int res;
-
- frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN;
- if (dsa_port_is_cpu(dp))
- frame_size += YT921X_TAG_LEN;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port),
- YT921X_MAC_FRAME_SIZE_M,
- YT921X_MAC_FRAME_SIZE(frame_size));
- mutex_unlock(&priv->reg_lock);
-
- return res;
-}
-
-static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port)
-{
- /* Only called for user ports, exclude tag len here */
- return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN;
-}
-
-/* v * 2^e */
-static u64 ldexpu64(u64 v, int e)
-{
- return e >= 0 ? v << e : v >> -e;
-}
-
-/* slot (ns) * rate (/s) / 10^9 (ns/s) = 2^C * token * 4^unit */
-static u32 rate2token(u64 rate, unsigned int slot_ns, int unit, int C)
-{
- int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
-
- return div_u64(ldexpu64(slot_ns * rate, -e), 1000000000);
-}
-
-static u64 token2rate(u32 token, unsigned int slot_ns, int unit, int C)
-{
- int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
-
- return div_u64(ldexpu64(mul_u32_u32(1000000000, token), e), slot_ns);
-}
-
-/* burst = 2^C * token * 4^unit */
-static u32 burst2token(u64 burst, int unit, int C)
-{
- return ldexpu64(burst, -(2 * unit + C));
-}
-
-static u64 token2burst(u32 token, int unit, int C)
-{
- return ldexpu64(token, 2 * unit + C);
-}
-
-struct yt921x_marker {
- u32 cir;
- u32 cbs;
- u32 ebs;
- int unit;
- bool pkt_mode;
-};
-
-#define YT921X_MARKER_PKT_MODE BIT(0)
-#define YT921X_MARKER_SINGLE_BUCKET BIT(1)
-
-static int
-yt921x_marker_tfm(struct yt921x_marker *marker, u64 rate, u64 burst,
- unsigned int flags, unsigned int slot_ns, u32 cir_max,
- u32 cbs_max, int unit_max, struct yt921x_priv *priv, int port,
- struct netlink_ext_ack *extack)
-{
- const int C = flags & YT921X_MARKER_PKT_MODE ? YT921X_TOKEN_PKT_C :
- YT921X_TOKEN_BYTE_C;
- struct device *dev = yt921x_priv_to_device(priv);
- struct yt921x_marker m;
- u64 burst_est;
- u64 burst_sug;
- u64 burst_max;
- u64 rate_max;
-
- m.unit = unit_max;
- rate_max = token2rate(cir_max, slot_ns, m.unit, C);
- burst_max = token2burst(cbs_max, m.unit, C);
-
- /* Check for unusual values */
- if (rate > rate_max || burst > burst_max) {
- NL_SET_ERR_MSG_MOD(extack, "Unexpected tremendous rate");
- return -ERANGE;
- }
-
- /* Check for matching burst */
- burst_est = div_u64(slot_ns * rate, 1000000000);
- burst_sug = burst_est;
- if (flags & YT921X_MARKER_PKT_MODE)
- burst_sug++;
- else
- burst_sug += ETH_HLEN + yt921x_mtu_fetch(priv, port) +
- ETH_FCS_LEN;
- if (burst_sug > burst)
- NL_SET_ERR_MSG_FMT_MOD(extack,
- "Consider match rate %llu with burst at least %llu",
- rate, burst_sug);
-
- /* Select unit */
- for (; m.unit > 0; m.unit--) {
- if (rate > (rate_max >> 2) || burst > (burst_max >> 2))
- break;
- rate_max >>= 2;
- burst_max >>= 2;
- }
-
- /* Calculate information rate and bucket size */
- m.cir = rate2token(rate, slot_ns, m.unit, C);
- if (!m.cir)
- m.cir = 1;
- else if (WARN_ON(m.cir > cir_max))
- m.cir = cir_max;
- m.cbs = burst2token(burst, m.unit, C);
- if (!m.cbs)
- m.cbs = 1;
- else if (WARN_ON(m.cbs > cbs_max))
- m.cbs = cbs_max;
-
- /* Cut EBS */
- m.ebs = 0;
- if (!(flags & YT921X_MARKER_SINGLE_BUCKET)) {
- /* We don't have a chance to adjust rate when MTU is changed */
- if (flags & YT921X_MARKER_PKT_MODE)
- burst_est++;
- else
- burst_est += YT921X_FRAME_SIZE_MAX;
-
- if (burst_est < burst) {
- u32 pbs = m.cbs;
-
- m.cbs = burst2token(burst_est, m.unit, C);
- if (!m.cbs)
- m.cbs = 1;
- else if (WARN_ON(m.cbs > cbs_max))
- m.cbs = cbs_max;
-
- if (pbs > m.cbs)
- m.ebs = pbs - m.cbs;
- }
- }
-
- dev_dbg(dev,
- "slot %u ns, rate %llu, burst %llu -> unit %d, cir %u, cbs %u, ebs %u\n",
- slot_ns, rate, burst, m.unit, m.cir, m.cbs, m.ebs);
-
- m.pkt_mode = flags & YT921X_MARKER_PKT_MODE;
- *marker = m;
- return 0;
-}
-
-static int
-yt921x_marker_tfm_police(struct yt921x_marker *marker,
- const struct flow_action_police *police,
- unsigned int flags, struct yt921x_priv *priv, int port,
- struct netlink_ext_ack *extack)
-{
- bool pkt_mode = !!police->rate_pkt_ps;
- u64 burst;
- u64 rate;
-
- rate = pkt_mode ? police->rate_pkt_ps : police->rate_bytes_ps;
- burst = pkt_mode ? police->burst_pkt : police->burst;
- if (pkt_mode)
- flags |= YT921X_MARKER_PKT_MODE;
-
- return yt921x_marker_tfm(marker, rate, burst, flags,
- priv->meter_slot_ns, YT921X_METER_CIR_MAX,
- YT921X_METER_CBS_MAX, YT921X_METER_UNIT_MAX,
- priv, port, extack);
-}
-
-static int
-yt921x_marker_tfm_shape(struct yt921x_marker *marker, u64 rate, u64 burst,
- unsigned int flags, struct yt921x_priv *priv, int port,
- struct netlink_ext_ack *extack)
-{
- return yt921x_marker_tfm(marker, rate, burst, flags,
- priv->port_shape_slot_ns, YT921X_SHAPE_CIR_MAX,
- YT921X_SHAPE_CBS_MAX, YT921X_SHAPE_UNIT_MAX,
- priv, port, extack);
-}
-
-static int
-yt921x_police_validate(const struct flow_action_police *police,
- const struct flow_action *action,
- const struct flow_action_entry *act,
- struct netlink_ext_ack *extack)
-{
- if (police->exceed.act_id != FLOW_ACTION_DROP) {
- NL_SET_ERR_MSG_MOD(extack,
- "Offload not supported when exceed action is not drop");
- return -EOPNOTSUPP;
- }
-
- if (police->notexceed.act_id != FLOW_ACTION_PIPE &&
- police->notexceed.act_id != FLOW_ACTION_ACCEPT) {
- NL_SET_ERR_MSG_MOD(extack,
- "Offload not supported when conform action is not pipe or ok");
- return -EOPNOTSUPP;
- }
-
- if (police->notexceed.act_id == FLOW_ACTION_ACCEPT && action && act &&
- !flow_action_is_last_entry(action, act)) {
- NL_SET_ERR_MSG_MOD(extack,
- "Offload not supported when conform action is ok, but action is not last");
- return -EOPNOTSUPP;
- }
-
- /* mtu defaults to unlimited but we got 2040 here, don't know why */
- if (police->peakrate_bytes_ps || police->avrate || police->overhead) {
- NL_SET_ERR_MSG_MOD(extack,
- "Offload not supported when peakrate/avrate/overhead is configured");
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
-static int
-yt921x_meter_config(struct yt921x_priv *priv, unsigned int id,
- const struct yt921x_marker *marker)
-{
- u32 ctrls[3];
-
- ctrls[0] = 0;
- ctrls[1] = YT921X_METER_CTRLb_CIR(marker->cir);
- ctrls[2] = YT921X_METER_CTRLc_UNIT(marker->unit) |
- YT921X_METER_CTRLc_DROP_R |
- YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN |
- YT921X_METER_CTRLc_METER_EN;
- if (marker->pkt_mode)
- ctrls[2] |= YT921X_METER_CTRLc_PKT_MODE;
- update_ctrls_unaligned(&ctrls[0], &ctrls[1],
- YT921X_METER_CTRLab_EBS_M,
- YT921X_METER_CTRLab_EBS(marker->ebs));
- update_ctrls_unaligned(&ctrls[1], &ctrls[2],
- YT921X_METER_CTRLbc_CBS_M,
- YT921X_METER_CTRLbc_CBS(marker->cbs));
-
- return yt921x_reg96_write(priv, YT921X_METERn_CTRL(id), ctrls);
-}
-
-static void yt921x_dsa_port_policer_del(struct dsa_switch *ds, int port)
-{
- struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
- struct device *dev = yt921x_priv_to_device(priv);
- int res;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), 0);
- mutex_unlock(&priv->reg_lock);
-
- if (res)
- dev_err(dev, "Failed to %s port %d: %i\n", "delete policer on",
- port, res);
-}
-
-static int
-yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port,
- const struct flow_action_police *police,
- struct netlink_ext_ack *extack)
-{
- struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
- struct yt921x_marker marker;
- u32 ctrl;
- int res;
-
- res = yt921x_police_validate(police, NULL, NULL, extack);
- if (res)
- return res;
-
- res = yt921x_marker_tfm_police(&marker, police, 0, priv, port, extack);
- if (res)
- return res;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_meter_config(priv, port + YT921X_METER_NUM, &marker);
- if (res)
- goto end;
-
- ctrl = YT921X_PORT_METER_ID(port) | YT921X_PORT_METER_EN;
- res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), ctrl);
-end:
- mutex_unlock(&priv->reg_lock);
-
- return res;
-}
-
-static int
-yt921x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port,
- const struct tc_tbf_qopt_offload *qopt)
-{
- struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
- struct netlink_ext_ack *extack = qopt->extack;
- u32 ctrls[2];
- int res;
-
- if (qopt->parent != TC_H_ROOT)
- return -EOPNOTSUPP;
-
- switch (qopt->command) {
- case TC_TBF_STATS:
- /* Unfortunately the convention for TC_*_STATS is a mess,
- * neither 0 nor -EOPNOTSUPP is perfect here.
- */
- return -EOPNOTSUPP;
- case TC_TBF_DESTROY:
- ctrls[0] = 0;
- ctrls[1] = 0;
- break;
- case TC_TBF_REPLACE: {
- const struct tc_tbf_qopt_offload_replace_params *p;
- struct yt921x_marker marker;
-
- p = &qopt->replace_params;
-
- res = yt921x_marker_tfm_shape(&marker, p->rate.rate_bytes_ps,
- p->max_size,
- YT921X_MARKER_SINGLE_BUCKET,
- priv, port, extack);
- if (res)
- return res;
-
- ctrls[0] = YT921X_PORT_SHAPE_CTRLa_CIR(marker.cir) |
- YT921X_PORT_SHAPE_CTRLa_CBS(marker.cbs);
- ctrls[1] = YT921X_PORT_SHAPE_CTRLb_UNIT(marker.unit) |
- YT921X_PORT_SHAPE_CTRLb_EN;
- break;
- }
- default:
- return -EOPNOTSUPP;
- }
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg64_write(priv, YT921X_PORTn_SHAPE_CTRL(port), ctrls);
- mutex_unlock(&priv->reg_lock);
-
- return res;
-}
-
-static int
-yt921x_dsa_port_setup_tc(struct dsa_switch *ds, int port,
- enum tc_setup_type type, void *type_data)
-{
- switch (type) {
- case TC_SETUP_QDISC_TBF: {
- const struct tc_tbf_qopt_offload *qopt = type_data;
-
- return yt921x_dsa_port_setup_tc_tbf_port(ds, port, qopt);
- }
- default:
- return -EOPNOTSUPP;
- }
-}
-
-/* ACL: 48 blocks * 8 entries
- *
- * One rule can span multiple entries, but within a block.
- */
-
-static void
-yt921x_acl_entry_set(struct yt921x_acl_entry *entry, unsigned int offset,
- u32 flags, bool set)
-{
- if (set)
- entry->key[offset] |= flags;
- entry->mask[offset] |= flags;
-}
-
-static unsigned int
-yt921x_acl_entries_set_is_fragment(struct yt921x_acl_entry *entries,
- unsigned int size, bool set)
-{
- for (unsigned int i = 0; i < size; i++)
- switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
- case YT921X_ACL_TYPE_IPV4_DA:
- case YT921X_ACL_TYPE_IPV4_SA:
- yt921x_acl_entry_set(&entries[i], 1,
- YT921X_ACL_BINb_IPV4_FRAG, set);
- return size;
- case YT921X_ACL_TYPE_IPV6_DA3:
- case YT921X_ACL_TYPE_IPV6_SA3:
- yt921x_acl_entry_set(&entries[i], 1,
- YT921X_ACL_BINb_IPV6_xA3_FRAG,
- set);
- return size;
- case YT921X_ACL_TYPE_MISC:
- yt921x_acl_entry_set(&entries[i], 1,
- YT921X_ACL_BINb_MISC_FRAG, set);
- return size;
- case YT921X_ACL_TYPE_L4:
- yt921x_acl_entry_set(&entries[i], 1,
- YT921X_ACL_BINb_L4_FRAG, set);
- return size;
- }
-
- if (size >= YT921X_ACL_ENT_PER_BLK)
- return 0;
-
- entries[size] = (typeof(*entries)){};
- entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
- yt921x_acl_entry_set(&entries[size], 1, YT921X_ACL_BINb_MISC_FRAG, set);
-
- return size + 1;
-}
-
-static unsigned int
-yt921x_acl_entries_set_first_frag(struct yt921x_acl_entry *entries,
- unsigned int size, bool set)
-{
- for (unsigned int i = 0; i < size; i++)
- switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
- case YT921X_ACL_TYPE_IPV6_DA2:
- case YT921X_ACL_TYPE_IPV6_SA2:
- yt921x_acl_entry_set(&entries[i], 1,
- YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG,
- set);
- return size;
- case YT921X_ACL_TYPE_MISC:
- yt921x_acl_entry_set(&entries[i], 0,
- YT921X_ACL_BINa_MISC_FIRST_FRAG,
- set);
- return size;
- }
-
- if (size >= YT921X_ACL_ENT_PER_BLK)
- return 0;
-
- entries[size] = (typeof(*entries)){};
- entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
- yt921x_acl_entry_set(&entries[size], 0,
- YT921X_ACL_BINa_MISC_FIRST_FRAG, set);
-
- return size + 1;
-}
-
-static unsigned int
-yt921x_acl_entries_set_l3_type(struct yt921x_acl_entry *entries,
- unsigned int size, enum yt921x_l3_type type)
-{
- for (unsigned int i = 0; i < size; i++)
- switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
- case YT921X_ACL_TYPE_MAC_DA0:
- case YT921X_ACL_TYPE_MAC_SA0:
- entries[i].key[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE(type);
- entries[i].mask[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M;
- return size;
- case YT921X_ACL_TYPE_MISC:
- entries[i].key[0] |= YT921X_ACL_BINa_MISC_L3_TYPE(type);
- entries[i].mask[0] |= YT921X_ACL_BINa_MISC_L3_TYPE_M;
- return size;
- }
-
- if (size >= YT921X_ACL_ENT_PER_BLK)
- return 0;
-
- entries[size] = (typeof(*entries)){};
- entries[size].key[0] = YT921X_ACL_BINa_MISC_L3_TYPE(type);
- entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
- entries[size].mask[0] = YT921X_ACL_BINa_MISC_L3_TYPE_M;
-
- return size + 1;
-}
-
-static unsigned int
-yt921x_acl_entries_set_l4_type(struct yt921x_acl_entry *entries,
- unsigned int size, enum yt921x_l4_type type)
-{
- for (unsigned int i = 0; i < size; i++)
- switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
- case YT921X_ACL_TYPE_IPV4_DA:
- case YT921X_ACL_TYPE_IPV4_SA:
- entries[i].key[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE(type);
- entries[i].mask[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE_M;
- return size;
- case YT921X_ACL_TYPE_IPV6_DA0:
- case YT921X_ACL_TYPE_IPV6_DA1:
- case YT921X_ACL_TYPE_IPV6_DA2:
- case YT921X_ACL_TYPE_IPV6_DA3:
- case YT921X_ACL_TYPE_IPV6_SA0:
- case YT921X_ACL_TYPE_IPV6_SA1:
- case YT921X_ACL_TYPE_IPV6_SA2:
- case YT921X_ACL_TYPE_IPV6_SA3:
- entries[i].key[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE(type);
- entries[i].mask[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE_M;
- return size;
- case YT921X_ACL_TYPE_L4:
- entries[i].key[1] |= YT921X_ACL_BINb_L4_TYPE(type);
- entries[i].mask[1] |= YT921X_ACL_BINb_L4_TYPE_M;
- return size;
- case YT921X_ACL_TYPE_MISC:
- entries[i].key[1] |= YT921X_ACL_BINb_MISC_L4_TYPE(type);
- entries[i].mask[1] |= YT921X_ACL_BINb_MISC_L4_TYPE_M;
- return size;
- }
-
- if (size >= YT921X_ACL_ENT_PER_BLK)
- return 0;
-
- entries[size] = (typeof(*entries)){};
- entries[size].key[1] = YT921X_ACL_BINb_MISC_L4_TYPE(type) |
- YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
- entries[size].mask[1] = YT921X_ACL_BINb_MISC_L4_TYPE_M;
-
- return size + 1;
-}
-
-static struct yt921x_acl_entry *
-yt921x_acl_entries_new(struct yt921x_acl_entry *entries, unsigned int *sizep,
- u32 type)
-{
- unsigned int size = *sizep;
-
- if (size >= YT921X_ACL_ENT_PER_BLK)
- return NULL;
-
- entries[size] = (typeof(*entries)){};
- entries[size].key[1] = YT921X_ACL_KEYb_TYPE(type);
-
- (*sizep)++;
- return &entries[size];
-}
-
-static struct yt921x_acl_entry *
-yt921x_acl_entries_find(struct yt921x_acl_entry *entries, unsigned int *sizep,
- u32 type)
-{
- for (unsigned int i = 0; i < *sizep; i++)
- if (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1]) ==
- type)
- return &entries[i];
- return yt921x_acl_entries_new(entries, sizep, type);
-}
-
-static void
-yt921x_acl_rule_set_ports(struct yt921x_acl_rule *aclrule, u16 ord,
- u16 ports_mask)
-{
- struct yt921x_acl_entry *entries = aclrule->entries;
-
- for (unsigned int i = 0; i < hweight8(aclrule->mask); i++) {
- entries[i].key[1] |= YT921X_ACL_KEYb_SPORTS(ports_mask) |
- YT921X_ACL_KEYb_ORD(ord);
- }
-}
-
-struct yt921x_acl_rule_ext {
- struct yt921x_acl_rule r;
-
- struct yt921x_marker marker;
-};
-
-static int
-yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
- const struct flow_cls_offload *cls)
-{
- const struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
- struct yt921x_acl_entry *entries = ruleext->r.entries;
- struct netlink_ext_ack *extack = cls->common.extack;
- const struct flow_dissector *dissector;
- struct yt921x_acl_entry *entry;
- unsigned int size = 0;
- bool use_dport;
- bool use_sport;
-
- /* Incomplete and probably won't, since it supports custom u32 filters.
- * New adapters are welcome.
- */
- dissector = rule->match.dissector;
- if (dissector->used_keys &
- ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
- BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
- BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_PORTS_RANGE) |
- BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_IP) |
- BIT_ULL(FLOW_DISSECTOR_KEY_TCP))) {
- NL_SET_ERR_MSG_MOD(extack, "Unsupported keys used");
- return -EOPNOTSUPP;
- }
-
- /* Entries */
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
- struct flow_match_ipv4_addrs match;
-
- flow_rule_match_ipv4_addrs(rule, &match);
-
- if (match.mask->dst) {
- entry = yt921x_acl_entries_new(entries, &size,
- YT921X_ACL_TYPE_IPV4_DA);
- if (!entry)
- goto err;
-
- entry->key[0] |= ntohl(match.key->dst);
- entry->mask[0] |= ntohl(match.mask->dst);
- }
-
- if (match.mask->src) {
- entry = yt921x_acl_entries_new(entries, &size,
- YT921X_ACL_TYPE_IPV4_SA);
- if (!entry)
- goto err;
-
- entry->key[0] |= ntohl(match.key->src);
- entry->mask[0] |= ntohl(match.mask->src);
- }
- }
-
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
- struct flow_match_ipv6_addrs match;
-
- flow_rule_match_ipv6_addrs(rule, &match);
-
- for (unsigned int i = 0; i < 4; i++) {
- if (!match.mask->dst.s6_addr32[i])
- continue;
-
- entry = yt921x_acl_entries_new(entries, &size,
- YT921X_ACL_TYPE_IPV6_DA0 + i);
- if (!entry)
- goto err;
-
- entry->key[0] |= ntohl(match.key->dst.s6_addr32[i]);
- entry->mask[0] |= ntohl(match.mask->dst.s6_addr32[i]);
- }
-
- for (unsigned int i = 0; i < 4; i++) {
- if (!match.mask->src.s6_addr32[i])
- continue;
-
- entry = yt921x_acl_entries_new(entries, &size,
- YT921X_ACL_TYPE_IPV6_SA0 + i);
- if (!entry)
- goto err;
-
- entry->key[0] |= ntohl(match.key->src.s6_addr32[i]);
- entry->mask[0] |= ntohl(match.mask->src.s6_addr32[i]);
- }
- }
-
- use_dport = false;
- use_sport = false;
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
- struct flow_match_ports match;
-
- entry = yt921x_acl_entries_new(entries, &size,
- YT921X_ACL_TYPE_L4);
- if (!entry)
- goto err;
-
- flow_rule_match_ports(rule, &match);
-
- use_dport = !!match.mask->dst;
- use_sport = !!match.mask->src;
-
- entry->key[0] |= (ntohs(match.key->dst) << 16) |
- ntohs(match.key->src);
- entry->mask[0] |= (ntohs(match.mask->dst) << 16) |
- ntohs(match.mask->src);
- }
-
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) {
- struct flow_match_ports_range match;
-
- entry = yt921x_acl_entries_find(entries, &size,
- YT921X_ACL_TYPE_L4);
- if (!entry)
- goto err;
-
- flow_rule_match_ports_range(rule, &match);
-
- if ((use_dport && match.mask->tp.dst) ||
- (use_sport && match.mask->tp.src)) {
- NL_SET_ERR_MSG_MOD(extack,
- "Port mask and range are mutually exclusive");
- return -EINVAL;
- }
-
- if (match.mask->tp.dst) {
- entry->key[0] |= ntohs(match.key->tp_min.dst) << 16;
- entry->key[1] |= YT921X_ACL_KEYb_L4_DPORT_RANGE_EN;
- entry->mask[0] |= ntohs(match.key->tp_max.dst) << 16;
- }
-
- if (match.mask->tp.src) {
- entry->key[0] |= ntohs(match.key->tp_min.src);
- entry->key[1] |= YT921X_ACL_KEYb_L4_SPORT_RANGE_EN;
- entry->mask[0] |= ntohs(match.key->tp_max.src);
- }
- }
-
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
- struct flow_match_eth_addrs match;
- u32 mask;
-
- flow_rule_match_eth_addrs(rule, &match);
-
- mask = ethaddr_hi4_to_u32(match.mask->dst);
- if (mask) {
- entry = yt921x_acl_entries_new(entries, &size,
- YT921X_ACL_TYPE_MAC_DA0);
- if (!entry)
- goto err;
-
- entry->key[0] |= ethaddr_hi4_to_u32(match.key->dst);
- entry->mask[0] |= mask;
- }
-
- mask = ethaddr_hi4_to_u32(match.mask->src);
- if (mask) {
- entry = yt921x_acl_entries_new(entries, &size,
- YT921X_ACL_TYPE_MAC_SA0);
- if (!entry)
- goto err;
-
- entry->key[0] |= ethaddr_hi4_to_u32(match.key->src);
- entry->mask[0] |= mask;
- }
-
- mask = (ethaddr_lo2_to_u32(match.mask->dst) << 16) |
- ethaddr_lo2_to_u32(match.mask->src);
- if (mask) {
- entry = yt921x_acl_entries_new(entries, &size,
- YT921X_ACL_TYPE_MAC_DA1_SA1);
- if (!entry)
- goto err;
-
- entry->key[0] |= (ethaddr_lo2_to_u32(match.key->dst) << 16) |
- ethaddr_lo2_to_u32(match.key->src);
- entry->mask[0] |= mask;
- }
- }
-
- /* Entries + Misc */
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
- struct flow_match_basic match;
-
- flow_rule_match_basic(rule, &match);
-
- if (match.mask->n_proto) {
- enum yt921x_l3_type l3type = YT921X_L3_TYPE_OTHER;
-
- if (match.mask->n_proto == htons(~0))
- switch (match.key->n_proto) {
- case htons(ETH_P_IP):
- l3type = YT921X_L3_TYPE_IPV4;
- break;
- case htons(ETH_P_IPV6):
- l3type = YT921X_L3_TYPE_IPV6;
- break;
- case htons(ETH_P_ARP):
- l3type = YT921X_L3_TYPE_ARP;
- break;
- case htons(ETH_P_LLDP):
- l3type = YT921X_L3_TYPE_LLDP;
- break;
- case htons(ETH_P_PAE):
- l3type = YT921X_L3_TYPE_PAE;
- break;
- case htons(ETH_P_CFM):
- l3type = YT921X_L3_TYPE_ERP;
- break;
- }
-
- if (l3type != YT921X_L3_TYPE_OTHER) {
- size = yt921x_acl_entries_set_l3_type(entries,
- size,
- l3type);
- if (!size)
- goto err;
- } else {
- entry = yt921x_acl_entries_new(entries, &size,
- YT921X_ACL_TYPE_ETHERTYPE);
- if (!entry)
- goto err;
-
- entry->key[0] |= ntohs(match.key->n_proto);
- entry->mask[0] |= ntohs(match.mask->n_proto);
- }
- }
-
- if (match.mask->ip_proto) {
- enum yt921x_l4_type l4type = YT921X_L4_TYPE_OTHER;
-
- if (match.mask->ip_proto == (u8)~0)
- switch (match.key->ip_proto) {
- case IPPROTO_TCP:
- l4type = YT921X_L4_TYPE_TCP;
- break;
- case IPPROTO_UDP:
- l4type = YT921X_L4_TYPE_UDP;
- break;
- case IPPROTO_UDPLITE:
- l4type = YT921X_L4_TYPE_UDPLITE;
- break;
- case IPPROTO_ICMP:
- l4type = YT921X_L4_TYPE_ICMP;
- break;
- case IPPROTO_IGMP:
- l4type = YT921X_L4_TYPE_IGMP;
- break;
- }
-
- if (l4type != YT921X_L4_TYPE_OTHER) {
- size = yt921x_acl_entries_set_l4_type(entries,
- size,
- l4type);
- if (!size)
- goto err;
- } else {
- entry = yt921x_acl_entries_find(entries, &size,
- YT921X_ACL_TYPE_MISC);
- if (!entry)
- goto err;
-
- entry->key[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.key->ip_proto);
- entry->mask[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.mask->ip_proto);
- }
- }
- }
-
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
- u32 supp_flags = FLOW_DIS_IS_FRAGMENT | FLOW_DIS_FIRST_FRAG;
- struct flow_match_control match;
-
- flow_rule_match_control(rule, &match);
- if (!flow_rule_is_supp_control_flags(supp_flags,
- match.mask->flags, extack))
- return -EOPNOTSUPP;
-
- if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {
- bool set = match.key->flags & FLOW_DIS_IS_FRAGMENT;
-
- size = yt921x_acl_entries_set_is_fragment(entries, size,
- set);
- if (!size)
- goto err;
- }
- if (match.mask->flags & FLOW_DIS_FIRST_FRAG) {
- bool set = match.key->flags & FLOW_DIS_FIRST_FRAG;
-
- size = yt921x_acl_entries_set_first_frag(entries, size,
- set);
- if (!size)
- goto err;
- }
- }
-
- /* Misc only */
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
- struct flow_match_ip match;
-
- flow_rule_match_ip(rule, &match);
- if (match.mask->ttl) {
- NL_SET_ERR_MSG_MOD(extack,
- "Matching on TTL not supported");
- return -EOPNOTSUPP;
- }
-
- if (match.mask->tos) {
- entry = yt921x_acl_entries_find(entries, &size,
- YT921X_ACL_TYPE_MISC);
- if (!entry)
- goto err;
-
- entry->key[0] |= YT921X_ACL_BINa_MISC_TOS(match.key->tos);
- entry->mask[0] |= YT921X_ACL_BINa_MISC_TOS(match.mask->tos);
- }
- }
-
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) {
- struct flow_match_tcp match;
-
- flow_rule_match_tcp(rule, &match);
- if (match.mask->flags & htons(~0xff)) {
- NL_SET_ERR_MSG_MOD(extack, "Unsupported TCP flags");
- return -EOPNOTSUPP;
- }
-
- if (match.mask->flags) {
- entry = yt921x_acl_entries_find(entries, &size,
- YT921X_ACL_TYPE_MISC);
- if (!entry)
- goto err;
-
- entry->key[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.key->flags));
- entry->mask[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.mask->flags));
- }
- }
-
- if (!size) {
- NL_SET_ERR_MSG_MOD(extack, "Empty rule generated, this should not happen");
- return -EOPNOTSUPP;
- }
-
- ruleext->r.mask = (1 << size) - 1;
- return 0;
-
-err:
- NL_SET_ERR_MSG_MOD(extack, "Rule too complex");
- return -EOPNOTSUPP;
-}
-
-static int
-yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext,
- const struct flow_cls_offload *cls,
- struct yt921x_priv *priv, int port)
-{
- const struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
- const struct flow_action *flow_action = &rule->action;
- struct netlink_ext_ack *extack = cls->common.extack;
- enum flow_action_id redir_act = NUM_FLOW_ACTIONS;
- const struct flow_action_entry *act;
- u32 *action = ruleext->r.action;
- bool seen_priority = false;
- const char *reason = NULL;
- bool seen_police = false;
- unsigned int i;
- int res;
-
- memset(action, 0, 3 * sizeof(*action));
- flow_action_for_each(i, act, flow_action)
- switch (act->id) {
- case FLOW_ACTION_ACCEPT:
- case FLOW_ACTION_DROP:
- case FLOW_ACTION_REDIRECT:
- if (redir_act != NUM_FLOW_ACTIONS &&
- redir_act != act->id) {
- reason = "Different redirect actions";
- goto fallback;
- }
- redir_act = act->id;
-
- switch (act->id) {
- case FLOW_ACTION_ACCEPT:
- action[2] |= YT921X_ACL_ACTc_FWD_EN |
- YT921X_ACL_ACTc_FWD_FWD;
- break;
- case FLOW_ACTION_DROP:
- action[2] |= YT921X_ACL_ACTc_FWD_EN |
- YT921X_ACL_ACTc_FWD_REDIR;
- break;
- case FLOW_ACTION_REDIRECT: {
- struct dsa_port *to_dp;
-
- to_dp = dsa_port_from_netdev(act->dev);
- if (IS_ERR(to_dp) || to_dp->ds != &priv->ds) {
- reason = "Redirect to non-local port";
- goto fallback;
- }
-
- action[2] |= YT921X_ACL_ACTc_FWD_EN |
- YT921X_ACL_ACTc_FWD_REDIR |
- YT921X_ACL_ACTc_FWD_REDIR_DPORTn(to_dp->index);
- break;
- }
- default:
- break;
- }
- break;
- case FLOW_ACTION_PRIORITY:
- if (seen_priority) {
- action[0] &= ~YT921X_ACL_ACTa_PRIO_EN;
- action[1] &= ~YT921X_ACL_ACTb_PRIO_M;
-
- reason = "Multiple priority actions";
- goto fallback;
- }
- seen_priority = true;
-
- if (act->priority >= YT921X_PRIO_NUM) {
- NL_SET_ERR_MSG_MOD(extack,
- "Priority value is too high");
- return -EOPNOTSUPP;
- }
- action[0] |= YT921X_ACL_ACTa_PRIO_EN;
- action[1] |= YT921X_ACL_ACTb_PRIO(act->priority);
- break;
- case FLOW_ACTION_POLICE: {
- const struct flow_action_police *police = &act->police;
-
- if (seen_police) {
- action[0] &= ~YT921X_ACL_ACTa_METER_EN;
-
- reason = "Multiple police actions";
- goto fallback;
- }
- seen_police = true;
-
- res = yt921x_police_validate(police, flow_action, act,
- extack);
- if (res)
- return res;
-
- res = yt921x_marker_tfm_police(&ruleext->marker, police,
- 0, priv, port, extack);
- if (res)
- return res;
+ res = __mdiobus_read(bus, addr, reg_data);
+ if (res < 0)
+ goto end;
+ val = (u16)res;
+ res = __mdiobus_read(bus, addr, reg_data);
+ if (res < 0)
+ goto end;
+ val = (val << 16) | (u16)res;
- action[0] |= YT921X_ACL_ACTa_METER_EN;
- break;
- }
- default:
-fallback:
- if (cls->common.skip_sw) {
- NL_SET_ERR_MSG_FMT_MOD(extack,
- "Action not supported when skip_sw: %s",
- reason);
- return -EOPNOTSUPP;
- }
- fallthrough;
- case FLOW_ACTION_TRAP:
- redir_act = FLOW_ACTION_TRAP;
-
- action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M &
- ~YT921X_ACL_ACTc_FWD_M;
- action[2] |= YT921X_ACL_ACTc_FWD_EN |
- YT921X_ACL_ACTc_FWD_TRAP;
- break;
- }
+ *valp = val;
+ res = 0;
- ruleext->r.sw_assisted = !cls->common.skip_sw;
- return 0;
+end:
+ mutex_unlock(&bus->mdio_lock);
+ return res;
}
-static int
-yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port,
- const struct flow_cls_offload *cls, bool ingress,
- struct yt921x_priv *priv)
+static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val)
{
- struct netlink_ext_ack *extack = cls->common.extack;
+ struct yt921x_reg_mdio *mdio = context;
+ struct mii_bus *bus = mdio->bus;
+ int addr = mdio->addr;
+ u32 reg_addr;
+ u32 reg_data;
int res;
- if (!ingress) {
- NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
- return -EOPNOTSUPP;
- }
-
- if (cls->common.chain_index) {
- NL_SET_ERR_MSG(extack, "Only chain 0 is supported");
- return -EOPNOTSUPP;
- }
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
- res = yt921x_acl_rule_ext_parse_flow_action(ruleext, cls, priv, port);
+ reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
+ YT921X_SMI_WRITE;
+ res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
if (res)
- return res;
- res = yt921x_acl_rule_ext_parse_flow_entries(ruleext, cls);
+ goto end;
+ res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
if (res)
- return res;
-
- yt921x_acl_rule_set_ports(&ruleext->r, 0, BIT(port));
- ruleext->r.tag = cls->cookie;
- ruleext->r.type = TC_SETUP_CLSFLOWER;
- return 0;
-}
-
-static unsigned int
-yt921x_acl_find(const struct yt921x_priv *priv, enum tc_setup_type type,
- unsigned long tag)
-{
- for (unsigned int blkid = 0; blkid < YT921X_ACL_BLK_NUM; blkid++) {
- const struct yt921x_acl_blk *aclblk = priv->acl_blks[blkid];
+ goto end;
- if (!aclblk)
- continue;
+ reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
+ YT921X_SMI_WRITE;
+ res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16));
+ if (res)
+ goto end;
+ res = __mdiobus_write(bus, addr, reg_data, (u16)val);
+ if (res)
+ goto end;
- for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++)
- if (aclblk->rules[i] && aclblk->rules[i]->tag == tag &&
- aclblk->rules[i]->type == type)
- return YT921X_ACL_ENT_PER_BLK * blkid + i;
- }
+ res = 0;
- return UINT_MAX;
+end:
+ mutex_unlock(&bus->mdio_lock);
+ return res;
}
-static unsigned int
-yt921x_acl_reserve(struct yt921x_priv *priv, unsigned int entscnt,
- struct netlink_ext_ack *extack)
-{
- int candidates[YT921X_ACL_ENT_PER_BLK + 1];
- unsigned int acl_used_cnt = 0;
-
- if (WARN_ON(entscnt > YT921X_ACL_ENT_PER_BLK))
- return UINT_MAX;
-
- for (unsigned int i = 0; i < ARRAY_SIZE(candidates); i++)
- candidates[i] = -1;
- for (unsigned int i = YT921X_ACL_BLK_NUM; i-- > 0;) {
- unsigned int blk_used_cnt = hweight8(priv->acl_masks[i]);
-
- candidates[blk_used_cnt] = i;
- acl_used_cnt += blk_used_cnt;
- }
+static const struct yt921x_reg_ops yt921x_reg_ops_mdio = {
+ .read = yt921x_reg_mdio_read,
+ .write = yt921x_reg_mdio_write,
+};
- if (acl_used_cnt >= YT921X_ACL_NUM) {
- NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached");
- return UINT_MAX;
- }
- if (acl_used_cnt + entscnt <= YT921X_ACL_NUM)
- for (unsigned int i = YT921X_ACL_ENT_PER_BLK - entscnt + 1;
- i-- > 0;)
- if (candidates[i] >= 0)
- return YT921X_ACL_ENT_PER_BLK * candidates[i] +
- ffz(priv->acl_masks[candidates[i]]);
-
- NL_SET_ERR_MSG_MOD(extack,
- "ACL entry allocation failed, simplify your rules or remove existing rules");
- return UINT_MAX;
-}
+/* TODO: SPI/I2C */
static int
-yt921x_acl_commit(struct yt921x_priv *priv, unsigned int entid, u8 entsmask)
+yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)
{
- const struct yt921x_acl_rule *aclrule;
- const struct yt921x_acl_blk *aclblk;
- unsigned int blkid;
- unsigned int binid;
- unsigned long mask;
- u32 zeros[3] = {};
- unsigned int i;
- unsigned int o;
- u32 ctrl;
- int res;
-
- blkid = entid / YT921X_ACL_ENT_PER_BLK;
- binid = entid % YT921X_ACL_ENT_PER_BLK;
- aclblk = priv->acl_blks[blkid];
- aclrule = aclblk->rules[binid];
+ /* Poor datasheet for EEE operations; don't ask if you are confused */
- /* Write actions */
- res = yt921x_reg96_write(priv, YT921X_ACLn_ACT(entid),
- aclrule ? aclrule->action : zeros);
- if (res)
- return res;
+ bool enable = e->eee_enabled;
+ u16 new_mask;
+ int res;
- /* Select the block */
- ctrl = YT921X_ACL_BLK_CMD_MODIFY | YT921X_ACL_BLK_CMD_BLKID(blkid);
- res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl);
- if (res)
- return res;
+ /* Enable / disable global EEE */
+ new_mask = priv->eee_ports_mask;
+ new_mask &= ~BIT(port);
+ new_mask |= !enable ? 0 : BIT(port);
- /* Write keys and masks */
- ctrl = 0;
- for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++)
- ctrl |= YT921X_ACL_BLK_KEEP_KEEPn(i);
-
- mask = entsmask;
- i = 0;
- for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) {
- res = yt921x_reg64_write(priv, YT921X_ACLn_KEYm(blkid, o),
- aclrule ? aclrule->entries[i].key :
- zeros);
+ if (!!new_mask != !!priv->eee_ports_mask) {
+ res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC,
+ YT921X_PON_STRAP_EEE, !!new_mask);
if (res)
return res;
-
- res = yt921x_reg64_write(priv, YT921X_ACLn_MASKm(blkid, o),
- aclrule ? aclrule->entries[i].mask :
- zeros);
+ res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL,
+ YT921X_PON_STRAP_EEE, !!new_mask);
if (res)
return res;
-
- ctrl &= ~YT921X_ACL_BLK_KEEP_KEEPn(o);
- i++;
}
- res = yt921x_reg_write(priv, YT921X_ACL_BLK_KEEP, ctrl);
- if (res)
- return res;
-
- ctrl = 0;
- for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) {
- const struct yt921x_acl_rule *other = aclblk->rules[i];
-
- if (!other)
- continue;
+ priv->eee_ports_mask = new_mask;
- mask = other->mask;
- for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK)
- ctrl |= YT921X_ACL_ENTRY_ENm(o) |
- YT921X_ACL_ENTRY_GRPIDm(o, i);
- }
- res = yt921x_reg_write(priv, YT921X_ACLn_ENTRY(blkid), ctrl);
+ /* Enable / disable port EEE */
+ res = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL,
+ YT921X_EEE_CTRL_ENn(port), enable);
if (res)
return res;
-
- /* Commit the block */
- ctrl = YT921X_ACL_BLK_CMD_BLKID(blkid);
- res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl);
+ res = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port),
+ YT921X_EEE_VAL_DATA, enable);
if (res)
return res;
@@ -1463,263 +223,47 @@ yt921x_acl_commit(struct yt921x_priv *priv, unsigned int entid, u8 entsmask)
}
static int
-yt921x_acl_del(struct yt921x_priv *priv, enum tc_setup_type type,
- unsigned long tag)
-{
- struct yt921x_acl_rule *aclrule;
- struct yt921x_acl_blk *aclblk;
- unsigned int binid;
- unsigned int blkid;
- unsigned int entid;
- int res;
-
- entid = yt921x_acl_find(priv, type, tag);
- if (entid == UINT_MAX)
- return -ENOENT;
-
- blkid = entid / YT921X_ACL_ENT_PER_BLK;
- binid = entid % YT921X_ACL_ENT_PER_BLK;
- aclblk = priv->acl_blks[blkid];
- aclrule = aclblk->rules[binid];
-
- aclblk->rules[binid] = NULL;
- res = yt921x_acl_commit(priv, entid, aclrule->mask);
- /* the kernel never rolls back on failure */
-
- if (aclrule->action[0] & YT921X_ACL_ACTa_METER_EN)
- clear_bit(FIELD_GET(YT921X_ACL_ACTa_METER_ID_M,
- aclrule->action[0]),
- priv->meters_map);
- priv->acl_masks[blkid] &= ~aclrule->mask;
- kvfree(aclrule);
- if (!priv->acl_masks[blkid]) {
- kvfree(aclblk);
- priv->acl_blks[blkid] = NULL;
- }
- return res;
-}
-
-static int
-yt921x_acl_add(struct yt921x_priv *priv,
- const struct yt921x_acl_rule_ext *ruleext,
- struct netlink_ext_ack *extack)
-{
- unsigned int entscnt = hweight8(ruleext->r.mask);
- struct yt921x_acl_rule *aclrule;
- struct yt921x_acl_blk *aclblk;
- bool use_trap = false;
- unsigned int meterid;
- unsigned long mask;
- unsigned int binid;
- unsigned int blkid;
- unsigned int entid;
- unsigned int o;
- int res;
-
- /* Allocate resources */
- entid = yt921x_acl_reserve(priv, entscnt, extack);
- if (entid == UINT_MAX)
- return -EOPNOTSUPP;
-
- if (!(ruleext->r.action[0] & YT921X_ACL_ACTa_METER_EN)) {
- meterid = YT921X_METER_NUM;
- } else {
- meterid = find_first_zero_bit(priv->meters_map,
- YT921X_METER_NUM);
- if (meterid < YT921X_METER_NUM) {
- res = yt921x_meter_config(priv, meterid,
- &ruleext->marker);
- if (res)
- return res;
- } else if (ruleext->r.sw_assisted) {
- use_trap = true;
- } else {
- NL_SET_ERR_MSG_MOD(extack,
- "No more meters available");
- return -EOPNOTSUPP;
- }
- }
-
- /* Prepare acl block ctrlblk */
- blkid = entid / YT921X_ACL_ENT_PER_BLK;
- binid = entid % YT921X_ACL_ENT_PER_BLK;
- aclblk = priv->acl_blks[blkid];
- if (!aclblk) {
- aclblk = kvzalloc_obj(*aclblk);
- if (!aclblk)
- return -ENOMEM;
- priv->acl_blks[blkid] = aclblk;
- }
-
- /* Prepare acl rule ctrlblk */
- aclrule = kvmemdup(&ruleext->r,
- offsetof(struct yt921x_acl_rule, entries[entscnt]),
- GFP_KERNEL);
- if (!aclrule) {
- res = -ENOMEM;
- goto err;
- }
-
- /* Replace the placeholder resource IDs */
- aclrule->mask = 0;
- mask = priv->acl_masks[blkid];
- for_each_clear_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) {
- aclrule->mask |= BIT(o);
- entscnt--;
- if (!entscnt)
- break;
- }
-
- if (use_trap) {
- aclrule->action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M &
- ~YT921X_ACL_ACTc_FWD_M;
- aclrule->action[2] |= YT921X_ACL_ACTc_FWD_EN |
- YT921X_ACL_ACTc_FWD_TRAP;
- }
- if (meterid < YT921X_METER_NUM)
- aclrule->action[0] |= YT921X_ACL_ACTa_METER_ID(meterid);
- else
- aclrule->action[0] &= ~YT921X_ACL_ACTa_METER_EN;
-
- /* Write rules */
- aclblk->rules[binid] = aclrule;
- res = yt921x_acl_commit(priv, entid, aclrule->mask);
- if (res) {
- aclblk->rules[binid] = NULL;
- kvfree(aclrule);
- goto err;
- }
-
- if (meterid < YT921X_METER_NUM)
- set_bit(meterid, priv->meters_map);
- priv->acl_masks[blkid] |= aclrule->mask;
- return 0;
-
-err:
- if (!priv->acl_masks[blkid]) {
- kvfree(aclblk);
- priv->acl_blks[blkid] = NULL;
- }
- return res;
-}
-
-static int
-yt921x_dsa_cls_flower_del(struct dsa_switch *ds, int port,
- struct flow_cls_offload *cls, bool ingress)
+yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e)
{
struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
int res;
mutex_lock(&priv->reg_lock);
- res = yt921x_acl_del(priv, TC_SETUP_CLSFLOWER, cls->cookie);
+ res = yt921x_set_eee(priv, port, e);
mutex_unlock(&priv->reg_lock);
return res;
}
static int
-yt921x_dsa_cls_flower_add(struct dsa_switch *ds, int port,
- struct flow_cls_offload *cls, bool ingress)
+yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
{
- struct netlink_ext_ack *extack = cls->common.extack;
+ /* Only serves as packet filter, since the frame size is always set to
+ * maximum after reset
+ */
+
struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
- struct yt921x_acl_rule_ext ruleext;
+ struct dsa_port *dp = dsa_to_port(ds, port);
+ int frame_size;
int res;
- res = yt921x_acl_rule_ext_parse_flow(&ruleext, port, cls, ingress,
- priv);
- if (res)
- return res;
+ frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN;
+ if (dsa_port_is_cpu(dp))
+ frame_size += YT921X_TAG_LEN;
mutex_lock(&priv->reg_lock);
- res = yt921x_acl_add(priv, &ruleext, extack);
+ res = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port),
+ YT921X_MAC_FRAME_SIZE_M,
+ YT921X_MAC_FRAME_SIZE(frame_size));
mutex_unlock(&priv->reg_lock);
return res;
}
-static int
-yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress)
-{
- u32 mask;
-
- if (ingress)
- mask = YT921X_MIRROR_IGR_PORTn(port);
- else
- mask = YT921X_MIRROR_EGR_PORTn(port);
- return yt921x_reg_clear_bits(priv, YT921X_MIRROR, mask);
-}
-
-static int
-yt921x_mirror_add(struct yt921x_priv *priv, int port, bool ingress,
- int to_local_port, struct netlink_ext_ack *extack)
-{
- u32 srcs;
- u32 ctrl;
- u32 val;
- u32 dst;
- int res;
-
- if (ingress)
- srcs = YT921X_MIRROR_IGR_PORTn(port);
- else
- srcs = YT921X_MIRROR_EGR_PORTn(port);
- dst = YT921X_MIRROR_PORT(to_local_port);
-
- res = yt921x_reg_read(priv, YT921X_MIRROR, &val);
- if (res)
- return res;
-
- /* other mirror tasks & different dst port -> conflict */
- if ((val & ~srcs & (YT921X_MIRROR_EGR_PORTS_M |
- YT921X_MIRROR_IGR_PORTS_M)) &&
- (val & YT921X_MIRROR_PORT_M) != dst) {
- NL_SET_ERR_MSG_MOD(extack,
- "Sniffer port is already configured, delete existing rules & retry");
- return -EBUSY;
- }
-
- ctrl = val & ~YT921X_MIRROR_PORT_M;
- ctrl |= srcs;
- ctrl |= dst;
-
- if (ctrl == val)
- return 0;
-
- return yt921x_reg_write(priv, YT921X_MIRROR, ctrl);
-}
-
-static void
-yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port,
- struct dsa_mall_mirror_tc_entry *mirror)
-{
- struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
- struct device *dev = yt921x_priv_to_device(priv);
- int res;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_mirror_del(priv, port, mirror->ingress);
- mutex_unlock(&priv->reg_lock);
-
- if (res)
- dev_err(dev, "Failed to %s port %d: %i\n", "unmirror",
- port, res);
-}
-
-static int
-yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port,
- struct dsa_mall_mirror_tc_entry *mirror,
- bool ingress, struct netlink_ext_ack *extack)
+static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port)
{
- struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
- int res;
-
- mutex_lock(&priv->reg_lock);
- res = yt921x_mirror_add(priv, port, ingress,
- mirror->to_local_port, extack);
- mutex_unlock(&priv->reg_lock);
-
- return res;
+ /* Only called for user ports, exclude tag len here */
+ return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN;
}
static int yt921x_lag_hash(struct yt921x_priv *priv, u32 ctrl, bool unique_lag,
@@ -3791,48 +2335,6 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
return 0;
}
-static int yt921x_chip_setup_tc(struct yt921x_priv *priv)
-{
- unsigned int op_ns;
- u32 ctrl;
- int res;
-
- op_ns = 8 * priv->cycle_ns;
-
- ctrl = max(priv->meter_slot_ns / op_ns, YT921X_METER_SLOT_MIN);
- res = yt921x_reg_write(priv, YT921X_METER_SLOT, ctrl);
- if (res)
- return res;
- priv->meter_slot_ns = ctrl * op_ns;
-
- ctrl = max(priv->port_shape_slot_ns / op_ns,
- YT921X_PORT_SHAPE_SLOT_MIN);
- res = yt921x_reg_write(priv, YT921X_PORT_SHAPE_SLOT, ctrl);
- if (res)
- return res;
- priv->port_shape_slot_ns = ctrl * op_ns;
-
- return 0;
-}
-
-static int yt921x_chip_setup_acl(struct yt921x_priv *priv)
-{
- u32 ctrl;
- int res;
-
- ctrl = YT921X_ACL_PERMIT_UNMATCH_PORTS_M;
- res = yt921x_reg_write(priv, YT921X_ACL_PERMIT_UNMATCH, ctrl);
- if (res)
- return res;
-
- ctrl = YT921X_ACL_PORT_PORTS_M;
- res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl);
- if (res)
- return res;
-
- return 0;
-}
-
static int __maybe_unused yt921x_chip_setup_qos(struct yt921x_priv *priv)
{
u32 ctrl;
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 25ff77054de3..012de4f444f5 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -371,249 +371,6 @@ enum yt921x_app_selector {
#define YT921X_VLAN_CTRLa_METER_EN BIT(5)
#define YT921X_VLAN_CTRLa_METER_ID_M GENMASK(4, 0)
-#define YT921X_ACLn_ACT(n) (0x1c0000 + 0x10 * (n))
-#define YT921X_ACL_ACTc_STAG_M GENMASK(26, 25)
-#define YT921X_ACL_ACTc_STAG(x) FIELD_PREP(YT921X_ACL_ACTc_STAG_M, (x))
-#define YT921X_ACL_ACTc_STAG_DONTCARE YT921X_ACL_ACTc_STAG(0)
-#define YT921X_ACL_ACTc_STAG_UNTAG YT921X_ACL_ACTc_STAG(1)
-#define YT921X_ACL_ACTc_STAG_TAG YT921X_ACL_ACTc_STAG(2)
-#define YT921X_ACL_ACTc_STAG_KEEP YT921X_ACL_ACTc_STAG(3)
-#define YT921X_ACL_ACTc_CTAG_M GENMASK(24, 23)
-#define YT921X_ACL_ACTc_CTAG(x) FIELD_PREP(YT921X_ACL_ACTc_CTAG_M, (x))
-#define YT921X_ACL_ACTc_CTAG_DONTCARE YT921X_ACL_ACTc_CTAG(0)
-#define YT921X_ACL_ACTc_CTAG_UNTAG YT921X_ACL_ACTc_CTAG(1)
-#define YT921X_ACL_ACTc_CTAG_TAG YT921X_ACL_ACTc_CTAG(2)
-#define YT921X_ACL_ACTc_CTAG_KEEP YT921X_ACL_ACTc_CTAG(3)
-#define YT921X_ACL_ACTc_FWD_M GENMASK(22, 21)
-#define YT921X_ACL_ACTc_FWD(x) FIELD_PREP(YT921X_ACL_ACTc_FWD_M, (x))
-#define YT921X_ACL_ACTc_FWD_FWD YT921X_ACL_ACTc_FWD(0)
-#define YT921X_ACL_ACTc_FWD_COPY YT921X_ACL_ACTc_FWD(1)
-#define YT921X_ACL_ACTc_FWD_REDIR YT921X_ACL_ACTc_FWD(2)
-#define YT921X_ACL_ACTc_FWD_TRAP YT921X_ACL_ACTc_FWD(3)
-#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M GENMASK(20, 10)
-#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS(x) FIELD_PREP(YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M, (x))
-#define YT921X_ACL_ACTc_FWD_REDIR_DPORTn(port) BIT((port) + 10)
-#define YT921X_ACL_ACTc_FWD_EN BIT(9)
-#define YT921X_ACL_ACTc_SDEI BIT(8)
-#define YT921X_ACL_ACTc_SDEI_REPLACE BIT(7)
-#define YT921X_ACL_ACTc_SPRI_M GENMASK(6, 4)
-#define YT921X_ACL_ACTc_SPRI(x) FIELD_PREP(YT921X_ACL_ACTc_SPRI_M, (x))
-#define YT921X_ACL_ACTc_SPRI_REPLACE BIT(3)
-#define YT921X_ACL_ACTbc_SVID_M GENMASK_ULL(34, 23)
-#define YT921X_ACL_ACTbc_SVID(x) FIELD_PREP(YT921X_ACL_ACTbc_SVID_M, (x))
-#define YT921X_ACL_ACTb_SVID_REPLACE BIT(22)
-#define YT921X_ACL_ACTb_CDEI BIT(21)
-#define YT921X_ACL_ACTb_CDEI_REPLACE BIT(20)
-#define YT921X_ACL_ACTb_CPRI_M GENMASK(19, 17)
-#define YT921X_ACL_ACTb_CPRI(x) FIELD_PREP(YT921X_ACL_ACTb_CPRI_M, (x))
-#define YT921X_ACL_ACTb_CPRI_REPLACE BIT(16)
-#define YT921X_ACL_ACTb_CVID_M GENMASK(15, 4)
-#define YT921X_ACL_ACTb_CVID(x) FIELD_PREP(YT921X_ACL_ACTb_CVID_M, (x))
-#define YT921X_ACL_ACTb_CVID_REPLACE BIT(3)
-#define YT921X_ACL_ACTb_PRIO_M GENMASK(2, 0)
-#define YT921X_ACL_ACTb_PRIO(x) FIELD_PREP(YT921X_ACL_ACTb_PRIO_M, (x))
-#define YT921X_ACL_ACTa_PRIO_EN BIT(31)
-#define YT921X_ACL_ACTa_COLOR_M GENMASK(30, 29)
-#define YT921X_ACL_ACTa_COLOR(x) FIELD_PREP(YT921X_ACL_ACTa_COLOR_M, (x))
-#define YT921X_ACL_ACTa_COLOR_GREEN YT921X_ACL_ACTa_COLOR(0)
-#define YT921X_ACL_ACTa_COLOR_YELLOW YT921X_ACL_ACTa_COLOR(1)
-#define YT921X_ACL_ACTa_COLOR_RED YT921X_ACL_ACTa_COLOR(2)
-#define YT921X_ACL_ACTa_COLOR_EN BIT(28)
-#define YT921X_ACL_ACTa_DSCP_M GENMASK(27, 22)
-#define YT921X_ACL_ACTa_DSCP(x) FIELD_PREP(YT921X_ACL_ACTa_DSCP_M, (x))
-#define YT921X_ACL_ACTa_DSCP_REPLACE BIT(21)
-#define YT921X_ACL_ACTa_METER_ID_M GENMASK(20, 15)
-#define YT921X_ACL_ACTa_METER_ID(x) FIELD_PREP(YT921X_ACL_ACTa_METER_ID_M, (x))
-#define YT921X_ACL_ACTa_METER_EN BIT(14)
-#define YT921X_ACL_ACTa_MIRROR_EN BIT(13)
-#define YT921X_ACL_ACTa_FLOWSTAT_EN BIT(12)
-#define YT921X_ACL_ACTa_FLOWSTAT_ID_M GENMASK(11, 6)
-#define YT921X_ACL_ACTa_FLOWSTAT_ID(x) FIELD_PREP(YT921X_ACL_ACTa_FLOWSTAT_ID_M, (x))
-#define YT921X_ACL_ACTa_GPIO_EN BIT(5)
-#define YT921X_ACL_ACTa_GPIO_PIN_M GENMASK(4, 1)
-#define YT921X_ACL_ACTa_GPIO_PIN(x) FIELD_PREP(YT921X_ACL_ACTa_GPIO_PIN_M, (x))
-#define YT921X_ACL_ACTa_INTR_EN BIT(0)
-#define YT921X_ACL_BLK_KEEP 0x201000
-#define YT921X_ACL_BLK_KEEP_GRPIDn_M(bin) (7 << (4 * (bin) + 1))
-#define YT921X_ACL_BLK_KEEP_GRPIDn(bin, x) ((x) << (4 * (bin) + 1))
-#define YT921X_ACL_BLK_KEEP_KEEPn(bin) BIT(4 * (bin))
-#define YT921X_ACL_PORT 0x202000
-#define YT921X_ACL_PORT_PORTS_M GENMASK(10, 0)
-#define YT921X_ACL_PORT_PORTS(x) FIELD_PREP(YT921X_ACL_PORT_PORTS_M, (x))
-#define YT921X_ACL_PORT_PORTn(port) BIT(port)
-#define YT921X_ACL_BLK_CMD 0x202004
-#define YT921X_ACL_BLK_CMD_BLKID_M GENMASK(6, 1)
-#define YT921X_ACL_BLK_CMD_BLKID(x) FIELD_PREP(YT921X_ACL_BLK_CMD_BLKID_M, (x))
-#define YT921X_ACL_BLK_CMD_MODIFY BIT(0)
-#define YT921X_ACLn_ENTRY(blk) (0x203000 + 4 * (blk))
-#define YT921X_ACL_ENTRY_GRPIDm_M(bin) (7 << (4 * (bin) + 1))
-#define YT921X_ACL_ENTRY_GRPIDm(bin, x) ((x) << (4 * (bin) + 1))
-#define YT921X_ACL_ENTRY_ENm(bin) BIT(4 * (bin))
-#define YT921X_ACLn_KEYm(blk, bin) (0x204000 + 0x200 * (bin) + 8 * (blk))
-#define YT921X_ACL_KEYb_ORD_M GENMASK(29, 21)
-#define YT921X_ACL_KEYb_ORD(x) FIELD_PREP(YT921X_ACL_KEYb_ORD_M, (x))
-#define YT921X_ACL_KEYb_SPORTS_M GENMASK(20, 10)
-#define YT921X_ACL_KEYb_SPORTS(x) FIELD_PREP(YT921X_ACL_KEYb_SPORTS_M, (x))
-#define YT921X_ACL_KEYb_SPORTn(port) BIT((port) + 10)
-#define YT921X_ACL_KEYb_REVERSE BIT(9) /* reverse match */
-#define YT921X_ACL_KEYb_TYPE_M GENMASK(8, 4)
-#define YT921X_ACL_KEYb_TYPE(x) FIELD_PREP(YT921X_ACL_KEYb_TYPE_M, (x))
-/* KEY_* fields need no masks */
-#define YT921X_ACLn_MASKm(blk, bin) (0x205000 + 0x200 * (bin) + 8 * (blk))
-
-enum yt921x_acl_type {
- YT921X_ACL_TYPE_NA,
- YT921X_ACL_TYPE_MAC_DA0,
- YT921X_ACL_TYPE_MAC_SA0,
- YT921X_ACL_TYPE_MAC_DA1_SA1,
- YT921X_ACL_TYPE_VLAN,
- YT921X_ACL_TYPE_VTAG,
- YT921X_ACL_TYPE_IPV4_DA,
- YT921X_ACL_TYPE_IPV4_SA,
- YT921X_ACL_TYPE_IPV6_DA0,
- YT921X_ACL_TYPE_IPV6_DA1,
- YT921X_ACL_TYPE_IPV6_DA2,
- YT921X_ACL_TYPE_IPV6_DA3,
- YT921X_ACL_TYPE_IPV6_SA0,
- YT921X_ACL_TYPE_IPV6_SA1,
- YT921X_ACL_TYPE_IPV6_SA2,
- YT921X_ACL_TYPE_IPV6_SA3,
- YT921X_ACL_TYPE_MISC,
- YT921X_ACL_TYPE_L4,
- YT921X_ACL_TYPE_UDF0,
- YT921X_ACL_TYPE_UDF1,
- YT921X_ACL_TYPE_UDF2,
- YT921X_ACL_TYPE_UDF3,
- YT921X_ACL_TYPE_UDF4,
- YT921X_ACL_TYPE_UDF5,
- YT921X_ACL_TYPE_UDF6,
- YT921X_ACL_TYPE_UDF7,
- YT921X_ACL_TYPE_ETHERTYPE,
- YT921X_ACL_TYPE_NUM
-};
-
-/* Range: turn KEY:MASK into MIN:MAX */
-
-#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M GENMASK(3, 0)
-#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M, (x))
-#define YT921X_ACL_BINa_MAC_xA0_MAC_xA0_M GENMASK(31, 0)
-
-#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M GENMASK(2, 0)
-#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M, (x))
-#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_DA1_M GENMASK(31, 16)
-#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_SA1_M GENMASK(15, 0)
-
-#define YT921X_ACL_KEYb_VLAN_SVID_RANGE_EN BIT(31)
-#define YT921X_ACL_KEYb_VLAN_CVID_RANGE_EN BIT(30)
-#define YT921X_ACL_BINb_VLAN_CDEI BIT(3)
-#define YT921X_ACL_BINb_VLAN_CPRI_M GENMASK(2, 0)
-#define YT921X_ACL_BINb_VLAN_CPRI(x) FIELD_PREP(YT921X_ACL_BINb_VLAN_CPRI_M, (x))
-#define YT921X_ACL_BINa_VLAN_CTAG_FMT_M GENMASK(31, 30)
-#define YT921X_ACL_BINa_VLAN_CTAG_FMT(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_CTAG_FMT_M, (x))
-#define YT921X_ACL_BINa_VLAN_SDEI BIT(29)
-#define YT921X_ACL_BINa_VLAN_SPRI_M GENMASK(28, 26)
-#define YT921X_ACL_BINa_VLAN_SPRI(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_SPRI_M, (x))
-#define YT921X_ACL_BINa_VLAN_STAG_FMT_M GENMASK(25, 24)
-#define YT921X_ACL_BINa_VLAN_STAG_FMT(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_STAG_FMT_M, (x))
-#define YT921X_ACL_BINa_VLAN_SVID_M GENMASK(23, 12)
-#define YT921X_ACL_BINa_VLAN_SVID(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_SVID_M, (x))
-#define YT921X_ACL_BINa_VLAN_CVID_M GENMASK(11, 0)
-#define YT921X_ACL_BINa_VLAN_CVID(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_CVID_M, (x))
-
-#define YT921X_ACL_KEYb_VTAG_SVID_RANGE_EN BIT(31)
-#define YT921X_ACL_KEYb_VTAG_CVID_RANGE_EN BIT(30)
-#define YT921X_ACL_BINa_VTAG_CDEI BIT(31)
-#define YT921X_ACL_BINa_VTAG_CPRI_M GENMASK(30, 28)
-#define YT921X_ACL_BINa_VTAG_CPRI(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_CPRI_M, (x))
-#define YT921X_ACL_BINa_VTAG_SDEI BIT(27)
-#define YT921X_ACL_BINa_VTAG_SPRI_M GENMASK(26, 24)
-#define YT921X_ACL_BINa_VTAG_SPRI(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_SPRI_M, (x))
-#define YT921X_ACL_BINa_VTAG_SVID_M GENMASK(23, 12)
-#define YT921X_ACL_BINa_VTAG_SVID(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_SVID_M, (x))
-#define YT921X_ACL_BINa_VTAG_CVID_M GENMASK(11, 0)
-#define YT921X_ACL_BINa_VTAG_CVID(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_CVID_M, (x))
-
-#define YT921X_ACL_KEYb_IPV4_ADDR_RANGE_EN BIT(30)
-#define YT921X_ACL_BINb_IPV4_FRAG BIT(3)
-#define YT921X_ACL_BINb_IPV4_L4_TYPE_M GENMASK(2, 0)
-#define YT921X_ACL_BINb_IPV4_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_IPV4_L4_TYPE_M, (x))
-#define YT921X_ACL_BINa_IPV4_ADDR_M GENMASK(31, 0)
-
-#define YT921X_ACL_BINb_IPV6_L4_TYPE_M GENMASK(2, 0)
-#define YT921X_ACL_BINb_IPV6_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_IPV6_L4_TYPE_M, (x))
-#define YT921X_ACL_BINa_IPV6_ADDRx_M GENMASK(31, 0)
-
-#define YT921X_ACL_BINb_IPV6_xA1_IP_OPTION BIT(3)
-
-#define YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG BIT(3)
-
-#define YT921X_ACL_KEYb_IPV6_xA3_ADDR_RANGE_EN BIT(30)
-#define YT921X_ACL_BINb_IPV6_xA3_FRAG BIT(3)
-
-#define YT921X_ACL_BINb_MISC_FRAG BIT(3)
-#define YT921X_ACL_BINb_MISC_L4_TYPE_M GENMASK(2, 0)
-#define YT921X_ACL_BINb_MISC_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MISC_L4_TYPE_M, (x))
-#define YT921X_ACL_BINa_MISC_PPPOE_FLAG BIT(30)
-#define YT921X_ACL_BINa_MISC_FIRST_FRAG BIT(29)
-#define YT921X_ACL_BINa_MISC_IP_OPTION BIT(28)
-#define YT921X_ACL_BINa_MISC_TCP_FLAGS_M GENMASK(27, 20)
-#define YT921X_ACL_BINa_MISC_TCP_FLAGS(x) FIELD_PREP(YT921X_ACL_BINa_MISC_TCP_FLAGS_M, (x))
-#define YT921X_ACL_BINa_MISC_IP_PROTO_M GENMASK(19, 12)
-#define YT921X_ACL_BINa_MISC_IP_PROTO(x) FIELD_PREP(YT921X_ACL_BINa_MISC_IP_PROTO_M, (x))
-#define YT921X_ACL_BINa_MISC_TOS_M GENMASK(11, 4)
-#define YT921X_ACL_BINa_MISC_TOS(x) FIELD_PREP(YT921X_ACL_BINa_MISC_TOS_M, (x))
-#define YT921X_ACL_BINa_MISC_L3_TYPE_M GENMASK(3, 0)
-#define YT921X_ACL_BINa_MISC_L3_TYPE(x) FIELD_PREP(YT921X_ACL_BINa_MISC_L3_TYPE_M, (x))
-
-#define YT921X_ACL_KEYb_L4_DPORT_RANGE_EN BIT(31)
-#define YT921X_ACL_KEYb_L4_SPORT_RANGE_EN BIT(30)
-#define YT921X_ACL_BINb_L4_FRAG BIT(3)
-#define YT921X_ACL_BINb_L4_TYPE_M GENMASK(2, 0)
-#define YT921X_ACL_BINb_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_L4_TYPE_M, (x))
-#define YT921X_ACL_BINa_L4_DPORT_M GENMASK(31, 16)
-#define YT921X_ACL_BINa_L4_SPORT_M GENMASK(15, 0)
-
-#define YT921X_ACL_BINb_UDF_IS_IGMP BIT(0)
-#define YT921X_ACL_BINa_UDF_UDF0_M GENMASK(31, 16)
-#define YT921X_ACL_BINa_UDF_UDF0(x) FIELD_PREP(YT921X_ACL_BINa_UDF_UDF0_M, (x))
-#define YT921X_ACL_BINa_UDF_UDF1_M GENMASK(15, 0)
-#define YT921X_ACL_BINa_UDF_UDF1(x) FIELD_PREP(YT921X_ACL_BINa_UDF_UDF1_M, (x))
-
-#define YT921X_ACL_KEYb_ETHERTYPE_ETHERTYPE_RANGE_EN BIT(30)
-#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M GENMASK(2, 0)
-#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M, (x))
-#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M GENMASK(15, 0)
-#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE(x) FIELD_PREP(YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M, (x))
-
-enum yt921x_l2_type {
- YT921X_L2_TYPE_ETH,
- YT921X_L2_TYPE_ETHV2,
- YT921X_L2_TYPE_ETHSAP,
- YT921X_L2_TYPE_ETHSNAP,
-};
-
-enum yt921x_l3_type {
- YT921X_L3_TYPE_OTHER,
- YT921X_L3_TYPE_IPV4,
- YT921X_L3_TYPE_IPV6,
- YT921X_L3_TYPE_ARP,
- YT921X_L3_TYPE_LLDP,
- YT921X_L3_TYPE_PAE,
- YT921X_L3_TYPE_ERP,
- YT921X_L3_TYPE_SLOW_PROTOCOL,
-};
-
-enum yt921x_l4_type {
- YT921X_L4_TYPE_OTHER,
- YT921X_L4_TYPE_TCP,
- YT921X_L4_TYPE_UDP,
- YT921X_L4_TYPE_UDPLITE,
- YT921X_L4_TYPE_ICMP,
- YT921X_L4_TYPE_IGMP,
- YT921X_L4_TYPE_MLD,
- YT921X_L4_TYPE_ND,
-};
-
#define YT921X_TPID_IGRn(x) (0x210000 + 4 * (x)) /* [0, 3] */
#define YT921X_TPID_IGR_TPID_M GENMASK(15, 0)
#define YT921X_PORTn_IGR_TPID(port) (0x210010 + 4 * (port))
@@ -630,47 +387,6 @@ enum yt921x_l4_type {
#define YT921X_LAG_HASH_MAC_SA BIT(2)
#define YT921X_LAG_HASH_MAC_DA BIT(1)
#define YT921X_LAG_HASH_SRC_PORT BIT(0)
-#define YT921X_UDFn_CTRL(x) (0x210094 + 4 * (x))
-#define YT921X_UDF_CTRL_UDF_TYPE_M GENMASK(8, 7)
-#define YT921X_UDF_CTRL_UDF_TYPE(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_TYPE_M, (x))
-#define YT921X_UDF_CTRL_UDF_TYPE_ETH YT921X_UDF_CTRL_UDF_TYPE(0)
-#define YT921X_UDF_CTRL_UDF_TYPE_L3 YT921X_UDF_CTRL_UDF_TYPE(1)
-#define YT921X_UDF_CTRL_UDF_TYPE_L4 YT921X_UDF_CTRL_UDF_TYPE(2)
-#define YT921X_UDF_CTRL_UDF_OFFSET_M GENMASK(6, 0)
-#define YT921X_UDF_CTRL_UDF_OFFSET(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_OFFSET_M, (x))
-
-#define YT921X_PORTn_RATE(port) (0x220000 + 4 * (port))
-#define YT921X_PORT_RATE_GAP_VALUE GENMASK(4, 0) /* default 20 */
-#define YT921X_METER_SLOT 0x220104
-#define YT921X_METER_SLOT_SLOT_M GENMASK(11, 0)
-#define YT921X_PORTn_METER(port) (0x220108 + 4 * (port))
-#define YT921X_PORT_METER_EN BIT(4)
-#define YT921X_PORT_METER_ID_M GENMASK(3, 0)
-#define YT921X_PORT_METER_ID(x) FIELD_PREP(YT921X_PORT_METER_ID_M, (x))
-#define YT921X_METERn_CTRL(x) (0x220800 + 0x10 * (x))
-#define YT921X_METER_CTRLc_METER_EN BIT(14)
-#define YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN BIT(13) /* RFC4115: yellow use unused green bw */
-#define YT921X_METER_CTRLc_DROP_M GENMASK(12, 11)
-#define YT921X_METER_CTRLc_DROP(x) FIELD_PREP(YT921X_METER_CTRLc_DROP_M, (x))
-#define YT921X_METER_CTRLc_DROP_GYR YT921X_METER_CTRLc_DROP(0)
-#define YT921X_METER_CTRLc_DROP_YR YT921X_METER_CTRLc_DROP(1)
-#define YT921X_METER_CTRLc_DROP_R YT921X_METER_CTRLc_DROP(2)
-#define YT921X_METER_CTRLc_DROP_NONE YT921X_METER_CTRLc_DROP(3)
-#define YT921X_METER_CTRLc_COLOR_BLIND BIT(10)
-#define YT921X_METER_CTRLc_UNIT_M GENMASK(9, 7)
-#define YT921X_METER_CTRLc_UNIT(x) FIELD_PREP(YT921X_METER_CTRLc_UNIT_M, (x))
-#define YT921X_METER_CTRLc_BYTE_MODE_INCLUDE_GAP BIT(6) /* +GAP_VALUE bytes each packet */
-#define YT921X_METER_CTRLc_PKT_MODE BIT(5) /* 0: byte rate mode */
-#define YT921X_METER_CTRLc_RFC2698 BIT(4) /* 0: RFC4115 */
-#define YT921X_METER_CTRLbc_CBS_M GENMASK_ULL(35, 20)
-#define YT921X_METER_CTRLbc_CBS(x) FIELD_PREP(YT921X_METER_CTRLbc_CBS_M, (x))
-#define YT921X_METER_CTRLb_CIR_M GENMASK(19, 2)
-#define YT921X_METER_CTRLb_CIR(x) FIELD_PREP(YT921X_METER_CTRLb_CIR_M, (x))
-#define YT921X_METER_CTRLab_EBS_M GENMASK_ULL(33, 18)
-#define YT921X_METER_CTRLab_EBS(x) FIELD_PREP(YT921X_METER_CTRLab_EBS_M, (x))
-#define YT921X_METER_CTRLa_EIR_M GENMASK(17, 0)
-#define YT921X_METER_CTRLa_EIR(x) FIELD_PREP(YT921X_METER_CTRLa_EIR_M, (x))
-#define YT921X_METERn_STAT(x) (0x221000 + 8 * (x))
#define YT921X_PORTn_VLAN_CTRL(port) (0x230010 + 4 * (port))
#define YT921X_PORT_VLAN_CTRL_SVLAN_PRIO_EN BIT(31)
@@ -689,29 +405,6 @@ enum yt921x_l4_type {
#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED BIT(1)
#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED BIT(0)
-#define YT921X_MIRROR 0x300300
-#define YT921X_MIRROR_IGR_PORTS_M GENMASK(26, 16)
-#define YT921X_MIRROR_IGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_IGR_PORTS_M, (x))
-#define YT921X_MIRROR_IGR_PORTn(port) BIT((port) + 16)
-#define YT921X_MIRROR_EGR_PORTS_M GENMASK(14, 4)
-#define YT921X_MIRROR_EGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_EGR_PORTS_M, (x))
-#define YT921X_MIRROR_EGR_PORTn(port) BIT((port) + 4)
-#define YT921X_MIRROR_PORT_M GENMASK(3, 0)
-#define YT921X_MIRROR_PORT(x) FIELD_PREP(YT921X_MIRROR_PORT_M, (x))
-
-#define YT921X_PORT_SHAPE_SLOT 0x34000c
-#define YT921X_PORT_SHAPE_SLOT_SLOT_M GENMASK(11, 0)
-#define YT921X_PORTn_SHAPE_CTRL(port) (0x354000 + 8 * (port))
-#define YT921X_PORT_SHAPE_CTRLb_EN BIT(4)
-#define YT921X_PORT_SHAPE_CTRLb_PKT_MODE BIT(3) /* 0: byte rate mode */
-#define YT921X_PORT_SHAPE_CTRLb_UNIT_M GENMASK(2, 0)
-#define YT921X_PORT_SHAPE_CTRLb_UNIT(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLb_UNIT_M, (x))
-#define YT921X_PORT_SHAPE_CTRLa_CBS_M GENMASK(31, 18)
-#define YT921X_PORT_SHAPE_CTRLa_CBS(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CBS_M, (x))
-#define YT921X_PORT_SHAPE_CTRLa_CIR_M GENMASK(17, 0)
-#define YT921X_PORT_SHAPE_CTRLa_CIR(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CIR_M, (x))
-#define YT921X_PORTn_SHAPE_STAT(port) (0x356000 + 4 * (port))
-
#define YT921X_EDATA_EXTMODE 0xfb
#define YT921X_EDATA_LEN 0x100
@@ -728,19 +421,8 @@ enum yt921x_fdb_entry_status {
#define YT921X_MSTI_NUM 16
-#define YT921X_TOKEN_BYTE_C 1 /* 1 token = 2^1 byte */
-#define YT921X_TOKEN_PKT_C -6 /* 1 token = 2^-6 packets */
-#define YT921X_TOKEN_RATE_C -15
/* Custom meters only, not including dedicated port meters (11) */
#define YT921X_METER_NUM 64
-#define YT921X_METER_SLOT_MIN 80
-#define YT921X_METER_UNIT_MAX ((1 << 3) - 1)
-#define YT921X_METER_CIR_MAX ((1 << 18) - 1)
-#define YT921X_METER_CBS_MAX ((1 << 16) - 1)
-#define YT921X_PORT_SHAPE_SLOT_MIN 80
-#define YT921X_SHAPE_UNIT_MAX ((1 << 3) - 1)
-#define YT921X_SHAPE_CIR_MAX ((1 << 18) - 1)
-#define YT921X_SHAPE_CBS_MAX ((1 << 14) - 1)
#define YT921X_LAG_NUM 2
#define YT921X_LAG_PORT_NUM 4
@@ -758,8 +440,6 @@ enum yt921x_fdb_entry_status {
#define YT921X_TAG_LEN 8
#define YT921X_ACL_BLK_NUM 48
-#define YT921X_ACL_ENT_PER_BLK 8
-#define YT921X_ACL_NUM (YT921X_ACL_BLK_NUM * YT921X_ACL_ENT_PER_BLK)
#define YT921X_UDF_NUM 8
#define YT921X_LED_GROUP_NUM 3
@@ -770,26 +450,6 @@ enum yt921x_fdb_entry_status {
#define YT921X_NAME "yt921x"
-struct yt921x_acl_entry {
- u32 key[2];
- u32 mask[2];
-};
-
-struct yt921x_acl_rule {
- unsigned long tag;
- enum tc_setup_type type;
-
- u32 action[3];
- bool sw_assisted;
-
- u8 mask;
- struct yt921x_acl_entry entries[YT921X_ACL_ENT_PER_BLK];
-};
-
-struct yt921x_acl_blk {
- struct yt921x_acl_rule *rules[YT921X_ACL_ENT_PER_BLK];
-};
-
struct yt921x_port {
unsigned char index;
@@ -893,4 +553,14 @@ static inline int ethtool_speed_to_yt921x(int speed)
}
}
+static inline u32 ethaddr_hi4_to_u32(const unsigned char *addr)
+{
+ return (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3];
+}
+
+static inline u32 ethaddr_lo2_to_u32(const unsigned char *addr)
+{
+ return (addr[4] << 8) | addr[5];
+}
+
#endif
diff --git a/drivers/net/dsa/motorcomm/tc.c b/drivers/net/dsa/motorcomm/tc.c
new file mode 100644
index 000000000000..433a428ed070
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/tc.c
@@ -0,0 +1,1496 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#include "chip.h"
+#include "smi.h"
+#include "tc.h"
+
+static int yt921x_mtu_fetch(struct yt921x_priv *priv, int port)
+{
+ struct dsa_port *dp = dsa_to_port(&priv->ds, port);
+
+ return dp->user ? READ_ONCE(dp->user->mtu) : ETH_DATA_LEN;
+}
+
+/* v * 2^e */
+static u64 ldexpu64(u64 v, int e)
+{
+ return e >= 0 ? v << e : v >> -e;
+}
+
+/* slot (ns) * rate (/s) / 10^9 (ns/s) = 2^C * token * 4^unit */
+static u32 rate2token(u64 rate, unsigned int slot_ns, int unit, int C)
+{
+ int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
+
+ return div_u64(ldexpu64(slot_ns * rate, -e), 1000000000);
+}
+
+static u64 token2rate(u32 token, unsigned int slot_ns, int unit, int C)
+{
+ int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
+
+ return div_u64(ldexpu64(mul_u32_u32(1000000000, token), e), slot_ns);
+}
+
+/* burst = 2^C * token * 4^unit */
+static u32 burst2token(u64 burst, int unit, int C)
+{
+ return ldexpu64(burst, -(2 * unit + C));
+}
+
+static u64 token2burst(u32 token, int unit, int C)
+{
+ return ldexpu64(token, 2 * unit + C);
+}
+
+struct yt921x_marker {
+ u32 cir;
+ u32 cbs;
+ u32 ebs;
+ int unit;
+ bool pkt_mode;
+};
+
+#define YT921X_MARKER_PKT_MODE BIT(0)
+#define YT921X_MARKER_SINGLE_BUCKET BIT(1)
+
+static int
+yt921x_marker_tfm(struct yt921x_marker *marker, u64 rate, u64 burst,
+ unsigned int flags, unsigned int slot_ns, u32 cir_max,
+ u32 cbs_max, int unit_max, struct yt921x_priv *priv, int port,
+ struct netlink_ext_ack *extack)
+{
+ const int C = flags & YT921X_MARKER_PKT_MODE ? YT921X_TOKEN_PKT_C :
+ YT921X_TOKEN_BYTE_C;
+ struct device *dev = yt921x_priv_to_device(priv);
+ struct yt921x_marker m;
+ u64 burst_est;
+ u64 burst_sug;
+ u64 burst_max;
+ u64 rate_max;
+
+ m.unit = unit_max;
+ rate_max = token2rate(cir_max, slot_ns, m.unit, C);
+ burst_max = token2burst(cbs_max, m.unit, C);
+
+ /* Check for unusual values */
+ if (rate > rate_max || burst > burst_max) {
+ NL_SET_ERR_MSG_MOD(extack, "Unexpected tremendous rate");
+ return -ERANGE;
+ }
+
+ /* Check for matching burst */
+ burst_est = div_u64(slot_ns * rate, 1000000000);
+ burst_sug = burst_est;
+ if (flags & YT921X_MARKER_PKT_MODE)
+ burst_sug++;
+ else
+ burst_sug += ETH_HLEN + yt921x_mtu_fetch(priv, port) +
+ ETH_FCS_LEN;
+ if (burst_sug > burst)
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "Consider match rate %llu with burst at least %llu",
+ rate, burst_sug);
+
+ /* Select unit */
+ for (; m.unit > 0; m.unit--) {
+ if (rate > (rate_max >> 2) || burst > (burst_max >> 2))
+ break;
+ rate_max >>= 2;
+ burst_max >>= 2;
+ }
+
+ /* Calculate information rate and bucket size */
+ m.cir = rate2token(rate, slot_ns, m.unit, C);
+ if (!m.cir)
+ m.cir = 1;
+ else if (WARN_ON(m.cir > cir_max))
+ m.cir = cir_max;
+ m.cbs = burst2token(burst, m.unit, C);
+ if (!m.cbs)
+ m.cbs = 1;
+ else if (WARN_ON(m.cbs > cbs_max))
+ m.cbs = cbs_max;
+
+ /* Cut EBS */
+ m.ebs = 0;
+ if (!(flags & YT921X_MARKER_SINGLE_BUCKET)) {
+ /* We don't have a chance to adjust rate when MTU is changed */
+ if (flags & YT921X_MARKER_PKT_MODE)
+ burst_est++;
+ else
+ burst_est += YT921X_FRAME_SIZE_MAX;
+
+ if (burst_est < burst) {
+ u32 pbs = m.cbs;
+
+ m.cbs = burst2token(burst_est, m.unit, C);
+ if (!m.cbs)
+ m.cbs = 1;
+ else if (WARN_ON(m.cbs > cbs_max))
+ m.cbs = cbs_max;
+
+ if (pbs > m.cbs)
+ m.ebs = pbs - m.cbs;
+ }
+ }
+
+ dev_dbg(dev,
+ "slot %u ns, rate %llu, burst %llu -> unit %d, cir %u, cbs %u, ebs %u\n",
+ slot_ns, rate, burst, m.unit, m.cir, m.cbs, m.ebs);
+
+ m.pkt_mode = flags & YT921X_MARKER_PKT_MODE;
+ *marker = m;
+ return 0;
+}
+
+static int
+yt921x_marker_tfm_police(struct yt921x_marker *marker,
+ const struct flow_action_police *police,
+ unsigned int flags, struct yt921x_priv *priv, int port,
+ struct netlink_ext_ack *extack)
+{
+ bool pkt_mode = !!police->rate_pkt_ps;
+ u64 burst;
+ u64 rate;
+
+ rate = pkt_mode ? police->rate_pkt_ps : police->rate_bytes_ps;
+ burst = pkt_mode ? police->burst_pkt : police->burst;
+ if (pkt_mode)
+ flags |= YT921X_MARKER_PKT_MODE;
+
+ return yt921x_marker_tfm(marker, rate, burst, flags,
+ priv->meter_slot_ns, YT921X_METER_CIR_MAX,
+ YT921X_METER_CBS_MAX, YT921X_METER_UNIT_MAX,
+ priv, port, extack);
+}
+
+static int
+yt921x_marker_tfm_shape(struct yt921x_marker *marker, u64 rate, u64 burst,
+ unsigned int flags, struct yt921x_priv *priv, int port,
+ struct netlink_ext_ack *extack)
+{
+ return yt921x_marker_tfm(marker, rate, burst, flags,
+ priv->port_shape_slot_ns, YT921X_SHAPE_CIR_MAX,
+ YT921X_SHAPE_CBS_MAX, YT921X_SHAPE_UNIT_MAX,
+ priv, port, extack);
+}
+
+static int
+yt921x_police_validate(const struct flow_action_police *police,
+ const struct flow_action *action,
+ const struct flow_action_entry *act,
+ struct netlink_ext_ack *extack)
+{
+ if (police->exceed.act_id != FLOW_ACTION_DROP) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Offload not supported when exceed action is not drop");
+ return -EOPNOTSUPP;
+ }
+
+ if (police->notexceed.act_id != FLOW_ACTION_PIPE &&
+ police->notexceed.act_id != FLOW_ACTION_ACCEPT) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Offload not supported when conform action is not pipe or ok");
+ return -EOPNOTSUPP;
+ }
+
+ if (police->notexceed.act_id == FLOW_ACTION_ACCEPT && action && act &&
+ !flow_action_is_last_entry(action, act)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Offload not supported when conform action is ok, but action is not last");
+ return -EOPNOTSUPP;
+ }
+
+ /* mtu defaults to unlimited but we got 2040 here, don't know why */
+ if (police->peakrate_bytes_ps || police->avrate || police->overhead) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Offload not supported when peakrate/avrate/overhead is configured");
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int
+yt921x_meter_config(struct yt921x_priv *priv, unsigned int id,
+ const struct yt921x_marker *marker)
+{
+ u32 ctrls[3];
+
+ ctrls[0] = 0;
+ ctrls[1] = YT921X_METER_CTRLb_CIR(marker->cir);
+ ctrls[2] = YT921X_METER_CTRLc_UNIT(marker->unit) |
+ YT921X_METER_CTRLc_DROP_R |
+ YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN |
+ YT921X_METER_CTRLc_METER_EN;
+ if (marker->pkt_mode)
+ ctrls[2] |= YT921X_METER_CTRLc_PKT_MODE;
+ update_ctrls_unaligned(&ctrls[0], &ctrls[1],
+ YT921X_METER_CTRLab_EBS_M,
+ YT921X_METER_CTRLab_EBS(marker->ebs));
+ update_ctrls_unaligned(&ctrls[1], &ctrls[2],
+ YT921X_METER_CTRLbc_CBS_M,
+ YT921X_METER_CTRLbc_CBS(marker->cbs));
+
+ return yt921x_reg96_write(priv, YT921X_METERn_CTRL(id), ctrls);
+}
+
+void yt921x_dsa_port_policer_del(struct dsa_switch *ds, int port)
+{
+ struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
+ struct device *dev = yt921x_priv_to_device(priv);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), 0);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dev, "Failed to %s port %d: %i\n", "delete policer on",
+ port, res);
+}
+
+int
+yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port,
+ const struct flow_action_police *police,
+ struct netlink_ext_ack *extack)
+{
+ struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
+ struct yt921x_marker marker;
+ u32 ctrl;
+ int res;
+
+ res = yt921x_police_validate(police, NULL, NULL, extack);
+ if (res)
+ return res;
+
+ res = yt921x_marker_tfm_police(&marker, police, 0, priv, port, extack);
+ if (res)
+ return res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_meter_config(priv, port + YT921X_METER_NUM, &marker);
+ if (res)
+ goto end;
+
+ ctrl = YT921X_PORT_METER_ID(port) | YT921X_PORT_METER_EN;
+ res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), ctrl);
+end:
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port,
+ const struct tc_tbf_qopt_offload *qopt)
+{
+ struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
+ struct netlink_ext_ack *extack = qopt->extack;
+ u32 ctrls[2];
+ int res;
+
+ if (qopt->parent != TC_H_ROOT)
+ return -EOPNOTSUPP;
+
+ switch (qopt->command) {
+ case TC_TBF_STATS:
+ /* Unfortunately the convention for TC_*_STATS is a mess,
+ * neither 0 nor -EOPNOTSUPP is perfect here.
+ */
+ return -EOPNOTSUPP;
+ case TC_TBF_DESTROY:
+ ctrls[0] = 0;
+ ctrls[1] = 0;
+ break;
+ case TC_TBF_REPLACE: {
+ const struct tc_tbf_qopt_offload_replace_params *p;
+ struct yt921x_marker marker;
+
+ p = &qopt->replace_params;
+
+ res = yt921x_marker_tfm_shape(&marker, p->rate.rate_bytes_ps,
+ p->max_size,
+ YT921X_MARKER_SINGLE_BUCKET,
+ priv, port, extack);
+ if (res)
+ return res;
+
+ ctrls[0] = YT921X_PORT_SHAPE_CTRLa_CIR(marker.cir) |
+ YT921X_PORT_SHAPE_CTRLa_CBS(marker.cbs);
+ ctrls[1] = YT921X_PORT_SHAPE_CTRLb_UNIT(marker.unit) |
+ YT921X_PORT_SHAPE_CTRLb_EN;
+ break;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_reg64_write(priv, YT921X_PORTn_SHAPE_CTRL(port), ctrls);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+int
+yt921x_dsa_port_setup_tc(struct dsa_switch *ds, int port,
+ enum tc_setup_type type, void *type_data)
+{
+ switch (type) {
+ case TC_SETUP_QDISC_TBF: {
+ const struct tc_tbf_qopt_offload *qopt = type_data;
+
+ return yt921x_dsa_port_setup_tc_tbf_port(ds, port, qopt);
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/* ACL: 48 blocks * 8 entries
+ *
+ * One rule can span multiple entries, but within a block.
+ */
+
+static void
+yt921x_acl_entry_set(struct yt921x_acl_entry *entry, unsigned int offset,
+ u32 flags, bool set)
+{
+ if (set)
+ entry->key[offset] |= flags;
+ entry->mask[offset] |= flags;
+}
+
+static unsigned int
+yt921x_acl_entries_set_is_fragment(struct yt921x_acl_entry *entries,
+ unsigned int size, bool set)
+{
+ for (unsigned int i = 0; i < size; i++)
+ switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
+ case YT921X_ACL_TYPE_IPV4_DA:
+ case YT921X_ACL_TYPE_IPV4_SA:
+ yt921x_acl_entry_set(&entries[i], 1,
+ YT921X_ACL_BINb_IPV4_FRAG, set);
+ return size;
+ case YT921X_ACL_TYPE_IPV6_DA3:
+ case YT921X_ACL_TYPE_IPV6_SA3:
+ yt921x_acl_entry_set(&entries[i], 1,
+ YT921X_ACL_BINb_IPV6_xA3_FRAG,
+ set);
+ return size;
+ case YT921X_ACL_TYPE_MISC:
+ yt921x_acl_entry_set(&entries[i], 1,
+ YT921X_ACL_BINb_MISC_FRAG, set);
+ return size;
+ case YT921X_ACL_TYPE_L4:
+ yt921x_acl_entry_set(&entries[i], 1,
+ YT921X_ACL_BINb_L4_FRAG, set);
+ return size;
+ }
+
+ if (size >= YT921X_ACL_ENT_PER_BLK)
+ return 0;
+
+ entries[size] = (typeof(*entries)){};
+ entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
+ yt921x_acl_entry_set(&entries[size], 1, YT921X_ACL_BINb_MISC_FRAG, set);
+
+ return size + 1;
+}
+
+static unsigned int
+yt921x_acl_entries_set_first_frag(struct yt921x_acl_entry *entries,
+ unsigned int size, bool set)
+{
+ for (unsigned int i = 0; i < size; i++)
+ switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
+ case YT921X_ACL_TYPE_IPV6_DA2:
+ case YT921X_ACL_TYPE_IPV6_SA2:
+ yt921x_acl_entry_set(&entries[i], 1,
+ YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG,
+ set);
+ return size;
+ case YT921X_ACL_TYPE_MISC:
+ yt921x_acl_entry_set(&entries[i], 0,
+ YT921X_ACL_BINa_MISC_FIRST_FRAG,
+ set);
+ return size;
+ }
+
+ if (size >= YT921X_ACL_ENT_PER_BLK)
+ return 0;
+
+ entries[size] = (typeof(*entries)){};
+ entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
+ yt921x_acl_entry_set(&entries[size], 0,
+ YT921X_ACL_BINa_MISC_FIRST_FRAG, set);
+
+ return size + 1;
+}
+
+static unsigned int
+yt921x_acl_entries_set_l3_type(struct yt921x_acl_entry *entries,
+ unsigned int size, enum yt921x_l3_type type)
+{
+ for (unsigned int i = 0; i < size; i++)
+ switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
+ case YT921X_ACL_TYPE_MAC_DA0:
+ case YT921X_ACL_TYPE_MAC_SA0:
+ entries[i].key[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE(type);
+ entries[i].mask[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M;
+ return size;
+ case YT921X_ACL_TYPE_MISC:
+ entries[i].key[0] |= YT921X_ACL_BINa_MISC_L3_TYPE(type);
+ entries[i].mask[0] |= YT921X_ACL_BINa_MISC_L3_TYPE_M;
+ return size;
+ }
+
+ if (size >= YT921X_ACL_ENT_PER_BLK)
+ return 0;
+
+ entries[size] = (typeof(*entries)){};
+ entries[size].key[0] = YT921X_ACL_BINa_MISC_L3_TYPE(type);
+ entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
+ entries[size].mask[0] = YT921X_ACL_BINa_MISC_L3_TYPE_M;
+
+ return size + 1;
+}
+
+static unsigned int
+yt921x_acl_entries_set_l4_type(struct yt921x_acl_entry *entries,
+ unsigned int size, enum yt921x_l4_type type)
+{
+ for (unsigned int i = 0; i < size; i++)
+ switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
+ case YT921X_ACL_TYPE_IPV4_DA:
+ case YT921X_ACL_TYPE_IPV4_SA:
+ entries[i].key[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE(type);
+ entries[i].mask[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE_M;
+ return size;
+ case YT921X_ACL_TYPE_IPV6_DA0:
+ case YT921X_ACL_TYPE_IPV6_DA1:
+ case YT921X_ACL_TYPE_IPV6_DA2:
+ case YT921X_ACL_TYPE_IPV6_DA3:
+ case YT921X_ACL_TYPE_IPV6_SA0:
+ case YT921X_ACL_TYPE_IPV6_SA1:
+ case YT921X_ACL_TYPE_IPV6_SA2:
+ case YT921X_ACL_TYPE_IPV6_SA3:
+ entries[i].key[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE(type);
+ entries[i].mask[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE_M;
+ return size;
+ case YT921X_ACL_TYPE_L4:
+ entries[i].key[1] |= YT921X_ACL_BINb_L4_TYPE(type);
+ entries[i].mask[1] |= YT921X_ACL_BINb_L4_TYPE_M;
+ return size;
+ case YT921X_ACL_TYPE_MISC:
+ entries[i].key[1] |= YT921X_ACL_BINb_MISC_L4_TYPE(type);
+ entries[i].mask[1] |= YT921X_ACL_BINb_MISC_L4_TYPE_M;
+ return size;
+ }
+
+ if (size >= YT921X_ACL_ENT_PER_BLK)
+ return 0;
+
+ entries[size] = (typeof(*entries)){};
+ entries[size].key[1] = YT921X_ACL_BINb_MISC_L4_TYPE(type) |
+ YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
+ entries[size].mask[1] = YT921X_ACL_BINb_MISC_L4_TYPE_M;
+
+ return size + 1;
+}
+
+static struct yt921x_acl_entry *
+yt921x_acl_entries_new(struct yt921x_acl_entry *entries, unsigned int *sizep,
+ u32 type)
+{
+ unsigned int size = *sizep;
+
+ if (size >= YT921X_ACL_ENT_PER_BLK)
+ return NULL;
+
+ entries[size] = (typeof(*entries)){};
+ entries[size].key[1] = YT921X_ACL_KEYb_TYPE(type);
+
+ (*sizep)++;
+ return &entries[size];
+}
+
+static struct yt921x_acl_entry *
+yt921x_acl_entries_find(struct yt921x_acl_entry *entries, unsigned int *sizep,
+ u32 type)
+{
+ for (unsigned int i = 0; i < *sizep; i++)
+ if (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1]) ==
+ type)
+ return &entries[i];
+ return yt921x_acl_entries_new(entries, sizep, type);
+}
+
+static void
+yt921x_acl_rule_set_ports(struct yt921x_acl_rule *aclrule, u16 ord,
+ u16 ports_mask)
+{
+ struct yt921x_acl_entry *entries = aclrule->entries;
+
+ for (unsigned int i = 0; i < hweight8(aclrule->mask); i++) {
+ entries[i].key[1] |= YT921X_ACL_KEYb_SPORTS(ports_mask) |
+ YT921X_ACL_KEYb_ORD(ord);
+ }
+}
+
+struct yt921x_acl_rule_ext {
+ struct yt921x_acl_rule r;
+
+ struct yt921x_marker marker;
+};
+
+static int
+yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
+ const struct flow_cls_offload *cls)
+{
+ const struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
+ struct yt921x_acl_entry *entries = ruleext->r.entries;
+ struct netlink_ext_ack *extack = cls->common.extack;
+ const struct flow_dissector *dissector;
+ struct yt921x_acl_entry *entry;
+ unsigned int size = 0;
+ bool use_dport;
+ bool use_sport;
+
+ /* Incomplete and probably won't, since it supports custom u32 filters.
+ * New adapters are welcome.
+ */
+ dissector = rule->match.dissector;
+ if (dissector->used_keys &
+ ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_PORTS_RANGE) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_IP) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_TCP))) {
+ NL_SET_ERR_MSG_MOD(extack, "Unsupported keys used");
+ return -EOPNOTSUPP;
+ }
+
+ /* Entries */
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
+ struct flow_match_ipv4_addrs match;
+
+ flow_rule_match_ipv4_addrs(rule, &match);
+
+ if (match.mask->dst) {
+ entry = yt921x_acl_entries_new(entries, &size,
+ YT921X_ACL_TYPE_IPV4_DA);
+ if (!entry)
+ goto err;
+
+ entry->key[0] |= ntohl(match.key->dst);
+ entry->mask[0] |= ntohl(match.mask->dst);
+ }
+
+ if (match.mask->src) {
+ entry = yt921x_acl_entries_new(entries, &size,
+ YT921X_ACL_TYPE_IPV4_SA);
+ if (!entry)
+ goto err;
+
+ entry->key[0] |= ntohl(match.key->src);
+ entry->mask[0] |= ntohl(match.mask->src);
+ }
+ }
+
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
+ struct flow_match_ipv6_addrs match;
+
+ flow_rule_match_ipv6_addrs(rule, &match);
+
+ for (unsigned int i = 0; i < 4; i++) {
+ if (!match.mask->dst.s6_addr32[i])
+ continue;
+
+ entry = yt921x_acl_entries_new(entries, &size,
+ YT921X_ACL_TYPE_IPV6_DA0 + i);
+ if (!entry)
+ goto err;
+
+ entry->key[0] |= ntohl(match.key->dst.s6_addr32[i]);
+ entry->mask[0] |= ntohl(match.mask->dst.s6_addr32[i]);
+ }
+
+ for (unsigned int i = 0; i < 4; i++) {
+ if (!match.mask->src.s6_addr32[i])
+ continue;
+
+ entry = yt921x_acl_entries_new(entries, &size,
+ YT921X_ACL_TYPE_IPV6_SA0 + i);
+ if (!entry)
+ goto err;
+
+ entry->key[0] |= ntohl(match.key->src.s6_addr32[i]);
+ entry->mask[0] |= ntohl(match.mask->src.s6_addr32[i]);
+ }
+ }
+
+ use_dport = false;
+ use_sport = false;
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+ struct flow_match_ports match;
+
+ entry = yt921x_acl_entries_new(entries, &size,
+ YT921X_ACL_TYPE_L4);
+ if (!entry)
+ goto err;
+
+ flow_rule_match_ports(rule, &match);
+
+ use_dport = !!match.mask->dst;
+ use_sport = !!match.mask->src;
+
+ entry->key[0] |= (ntohs(match.key->dst) << 16) |
+ ntohs(match.key->src);
+ entry->mask[0] |= (ntohs(match.mask->dst) << 16) |
+ ntohs(match.mask->src);
+ }
+
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) {
+ struct flow_match_ports_range match;
+
+ entry = yt921x_acl_entries_find(entries, &size,
+ YT921X_ACL_TYPE_L4);
+ if (!entry)
+ goto err;
+
+ flow_rule_match_ports_range(rule, &match);
+
+ if ((use_dport && match.mask->tp.dst) ||
+ (use_sport && match.mask->tp.src)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Port mask and range are mutually exclusive");
+ return -EINVAL;
+ }
+
+ if (match.mask->tp.dst) {
+ entry->key[0] |= ntohs(match.key->tp_min.dst) << 16;
+ entry->key[1] |= YT921X_ACL_KEYb_L4_DPORT_RANGE_EN;
+ entry->mask[0] |= ntohs(match.key->tp_max.dst) << 16;
+ }
+
+ if (match.mask->tp.src) {
+ entry->key[0] |= ntohs(match.key->tp_min.src);
+ entry->key[1] |= YT921X_ACL_KEYb_L4_SPORT_RANGE_EN;
+ entry->mask[0] |= ntohs(match.key->tp_max.src);
+ }
+ }
+
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+ struct flow_match_eth_addrs match;
+ u32 mask;
+
+ flow_rule_match_eth_addrs(rule, &match);
+
+ mask = ethaddr_hi4_to_u32(match.mask->dst);
+ if (mask) {
+ entry = yt921x_acl_entries_new(entries, &size,
+ YT921X_ACL_TYPE_MAC_DA0);
+ if (!entry)
+ goto err;
+
+ entry->key[0] |= ethaddr_hi4_to_u32(match.key->dst);
+ entry->mask[0] |= mask;
+ }
+
+ mask = ethaddr_hi4_to_u32(match.mask->src);
+ if (mask) {
+ entry = yt921x_acl_entries_new(entries, &size,
+ YT921X_ACL_TYPE_MAC_SA0);
+ if (!entry)
+ goto err;
+
+ entry->key[0] |= ethaddr_hi4_to_u32(match.key->src);
+ entry->mask[0] |= mask;
+ }
+
+ mask = (ethaddr_lo2_to_u32(match.mask->dst) << 16) |
+ ethaddr_lo2_to_u32(match.mask->src);
+ if (mask) {
+ entry = yt921x_acl_entries_new(entries, &size,
+ YT921X_ACL_TYPE_MAC_DA1_SA1);
+ if (!entry)
+ goto err;
+
+ entry->key[0] |= (ethaddr_lo2_to_u32(match.key->dst) << 16) |
+ ethaddr_lo2_to_u32(match.key->src);
+ entry->mask[0] |= mask;
+ }
+ }
+
+ /* Entries + Misc */
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+ struct flow_match_basic match;
+
+ flow_rule_match_basic(rule, &match);
+
+ if (match.mask->n_proto) {
+ enum yt921x_l3_type l3type = YT921X_L3_TYPE_OTHER;
+
+ if (match.mask->n_proto == htons(~0))
+ switch (match.key->n_proto) {
+ case htons(ETH_P_IP):
+ l3type = YT921X_L3_TYPE_IPV4;
+ break;
+ case htons(ETH_P_IPV6):
+ l3type = YT921X_L3_TYPE_IPV6;
+ break;
+ case htons(ETH_P_ARP):
+ l3type = YT921X_L3_TYPE_ARP;
+ break;
+ case htons(ETH_P_LLDP):
+ l3type = YT921X_L3_TYPE_LLDP;
+ break;
+ case htons(ETH_P_PAE):
+ l3type = YT921X_L3_TYPE_PAE;
+ break;
+ case htons(ETH_P_CFM):
+ l3type = YT921X_L3_TYPE_ERP;
+ break;
+ }
+
+ if (l3type != YT921X_L3_TYPE_OTHER) {
+ size = yt921x_acl_entries_set_l3_type(entries,
+ size,
+ l3type);
+ if (!size)
+ goto err;
+ } else {
+ entry = yt921x_acl_entries_new(entries, &size,
+ YT921X_ACL_TYPE_ETHERTYPE);
+ if (!entry)
+ goto err;
+
+ entry->key[0] |= ntohs(match.key->n_proto);
+ entry->mask[0] |= ntohs(match.mask->n_proto);
+ }
+ }
+
+ if (match.mask->ip_proto) {
+ enum yt921x_l4_type l4type = YT921X_L4_TYPE_OTHER;
+
+ if (match.mask->ip_proto == (u8)~0)
+ switch (match.key->ip_proto) {
+ case IPPROTO_TCP:
+ l4type = YT921X_L4_TYPE_TCP;
+ break;
+ case IPPROTO_UDP:
+ l4type = YT921X_L4_TYPE_UDP;
+ break;
+ case IPPROTO_UDPLITE:
+ l4type = YT921X_L4_TYPE_UDPLITE;
+ break;
+ case IPPROTO_ICMP:
+ l4type = YT921X_L4_TYPE_ICMP;
+ break;
+ case IPPROTO_IGMP:
+ l4type = YT921X_L4_TYPE_IGMP;
+ break;
+ }
+
+ if (l4type != YT921X_L4_TYPE_OTHER) {
+ size = yt921x_acl_entries_set_l4_type(entries,
+ size,
+ l4type);
+ if (!size)
+ goto err;
+ } else {
+ entry = yt921x_acl_entries_find(entries, &size,
+ YT921X_ACL_TYPE_MISC);
+ if (!entry)
+ goto err;
+
+ entry->key[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.key->ip_proto);
+ entry->mask[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.mask->ip_proto);
+ }
+ }
+ }
+
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+ u32 supp_flags = FLOW_DIS_IS_FRAGMENT | FLOW_DIS_FIRST_FRAG;
+ struct flow_match_control match;
+
+ flow_rule_match_control(rule, &match);
+ if (!flow_rule_is_supp_control_flags(supp_flags,
+ match.mask->flags, extack))
+ return -EOPNOTSUPP;
+
+ if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {
+ bool set = match.key->flags & FLOW_DIS_IS_FRAGMENT;
+
+ size = yt921x_acl_entries_set_is_fragment(entries, size,
+ set);
+ if (!size)
+ goto err;
+ }
+ if (match.mask->flags & FLOW_DIS_FIRST_FRAG) {
+ bool set = match.key->flags & FLOW_DIS_FIRST_FRAG;
+
+ size = yt921x_acl_entries_set_first_frag(entries, size,
+ set);
+ if (!size)
+ goto err;
+ }
+ }
+
+ /* Misc only */
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
+ struct flow_match_ip match;
+
+ flow_rule_match_ip(rule, &match);
+ if (match.mask->ttl) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Matching on TTL not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (match.mask->tos) {
+ entry = yt921x_acl_entries_find(entries, &size,
+ YT921X_ACL_TYPE_MISC);
+ if (!entry)
+ goto err;
+
+ entry->key[0] |= YT921X_ACL_BINa_MISC_TOS(match.key->tos);
+ entry->mask[0] |= YT921X_ACL_BINa_MISC_TOS(match.mask->tos);
+ }
+ }
+
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) {
+ struct flow_match_tcp match;
+
+ flow_rule_match_tcp(rule, &match);
+ if (match.mask->flags & htons(~0xff)) {
+ NL_SET_ERR_MSG_MOD(extack, "Unsupported TCP flags");
+ return -EOPNOTSUPP;
+ }
+
+ if (match.mask->flags) {
+ entry = yt921x_acl_entries_find(entries, &size,
+ YT921X_ACL_TYPE_MISC);
+ if (!entry)
+ goto err;
+
+ entry->key[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.key->flags));
+ entry->mask[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.mask->flags));
+ }
+ }
+
+ if (!size) {
+ NL_SET_ERR_MSG_MOD(extack, "Empty rule generated, this should not happen");
+ return -EOPNOTSUPP;
+ }
+
+ ruleext->r.mask = (1 << size) - 1;
+ return 0;
+
+err:
+ NL_SET_ERR_MSG_MOD(extack, "Rule too complex");
+ return -EOPNOTSUPP;
+}
+
+static int
+yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext,
+ const struct flow_cls_offload *cls,
+ struct yt921x_priv *priv, int port)
+{
+ const struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
+ const struct flow_action *flow_action = &rule->action;
+ struct netlink_ext_ack *extack = cls->common.extack;
+ enum flow_action_id redir_act = NUM_FLOW_ACTIONS;
+ const struct flow_action_entry *act;
+ u32 *action = ruleext->r.action;
+ bool seen_priority = false;
+ const char *reason = NULL;
+ bool seen_police = false;
+ unsigned int i;
+ int res;
+
+ memset(action, 0, 3 * sizeof(*action));
+ flow_action_for_each(i, act, flow_action)
+ switch (act->id) {
+ case FLOW_ACTION_ACCEPT:
+ case FLOW_ACTION_DROP:
+ case FLOW_ACTION_REDIRECT:
+ if (redir_act != NUM_FLOW_ACTIONS &&
+ redir_act != act->id) {
+ reason = "Different redirect actions";
+ goto fallback;
+ }
+ redir_act = act->id;
+
+ switch (act->id) {
+ case FLOW_ACTION_ACCEPT:
+ action[2] |= YT921X_ACL_ACTc_FWD_EN |
+ YT921X_ACL_ACTc_FWD_FWD;
+ break;
+ case FLOW_ACTION_DROP:
+ action[2] |= YT921X_ACL_ACTc_FWD_EN |
+ YT921X_ACL_ACTc_FWD_REDIR;
+ break;
+ case FLOW_ACTION_REDIRECT: {
+ struct dsa_port *to_dp;
+
+ to_dp = dsa_port_from_netdev(act->dev);
+ if (IS_ERR(to_dp) || to_dp->ds != &priv->ds) {
+ reason = "Redirect to non-local port";
+ goto fallback;
+ }
+
+ action[2] |= YT921X_ACL_ACTc_FWD_EN |
+ YT921X_ACL_ACTc_FWD_REDIR |
+ YT921X_ACL_ACTc_FWD_REDIR_DPORTn(to_dp->index);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ case FLOW_ACTION_PRIORITY:
+ if (seen_priority) {
+ action[0] &= ~YT921X_ACL_ACTa_PRIO_EN;
+ action[1] &= ~YT921X_ACL_ACTb_PRIO_M;
+
+ reason = "Multiple priority actions";
+ goto fallback;
+ }
+ seen_priority = true;
+
+ if (act->priority >= YT921X_PRIO_NUM) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Priority value is too high");
+ return -EOPNOTSUPP;
+ }
+ action[0] |= YT921X_ACL_ACTa_PRIO_EN;
+ action[1] |= YT921X_ACL_ACTb_PRIO(act->priority);
+ break;
+ case FLOW_ACTION_POLICE: {
+ const struct flow_action_police *police = &act->police;
+
+ if (seen_police) {
+ action[0] &= ~YT921X_ACL_ACTa_METER_EN;
+
+ reason = "Multiple police actions";
+ goto fallback;
+ }
+ seen_police = true;
+
+ res = yt921x_police_validate(police, flow_action, act,
+ extack);
+ if (res)
+ return res;
+
+ res = yt921x_marker_tfm_police(&ruleext->marker, police,
+ 0, priv, port, extack);
+ if (res)
+ return res;
+
+ action[0] |= YT921X_ACL_ACTa_METER_EN;
+ break;
+ }
+ default:
+fallback:
+ if (cls->common.skip_sw) {
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "Action not supported when skip_sw: %s",
+ reason);
+ return -EOPNOTSUPP;
+ }
+ fallthrough;
+ case FLOW_ACTION_TRAP:
+ redir_act = FLOW_ACTION_TRAP;
+
+ action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M &
+ ~YT921X_ACL_ACTc_FWD_M;
+ action[2] |= YT921X_ACL_ACTc_FWD_EN |
+ YT921X_ACL_ACTc_FWD_TRAP;
+ break;
+ }
+
+ ruleext->r.sw_assisted = !cls->common.skip_sw;
+ return 0;
+}
+
+static int
+yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port,
+ const struct flow_cls_offload *cls, bool ingress,
+ struct yt921x_priv *priv)
+{
+ struct netlink_ext_ack *extack = cls->common.extack;
+ int res;
+
+ if (!ingress) {
+ NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (cls->common.chain_index) {
+ NL_SET_ERR_MSG(extack, "Only chain 0 is supported");
+ return -EOPNOTSUPP;
+ }
+
+ res = yt921x_acl_rule_ext_parse_flow_action(ruleext, cls, priv, port);
+ if (res)
+ return res;
+ res = yt921x_acl_rule_ext_parse_flow_entries(ruleext, cls);
+ if (res)
+ return res;
+
+ yt921x_acl_rule_set_ports(&ruleext->r, 0, BIT(port));
+ ruleext->r.tag = cls->cookie;
+ ruleext->r.type = TC_SETUP_CLSFLOWER;
+ return 0;
+}
+
+static unsigned int
+yt921x_acl_find(const struct yt921x_priv *priv, enum tc_setup_type type,
+ unsigned long tag)
+{
+ for (unsigned int blkid = 0; blkid < YT921X_ACL_BLK_NUM; blkid++) {
+ const struct yt921x_acl_blk *aclblk = priv->acl_blks[blkid];
+
+ if (!aclblk)
+ continue;
+
+ for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++)
+ if (aclblk->rules[i] && aclblk->rules[i]->tag == tag &&
+ aclblk->rules[i]->type == type)
+ return YT921X_ACL_ENT_PER_BLK * blkid + i;
+ }
+
+ return UINT_MAX;
+}
+
+static unsigned int
+yt921x_acl_reserve(struct yt921x_priv *priv, unsigned int entscnt,
+ struct netlink_ext_ack *extack)
+{
+ int candidates[YT921X_ACL_ENT_PER_BLK + 1];
+ unsigned int acl_used_cnt = 0;
+
+ if (WARN_ON(entscnt > YT921X_ACL_ENT_PER_BLK))
+ return UINT_MAX;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(candidates); i++)
+ candidates[i] = -1;
+ for (unsigned int i = YT921X_ACL_BLK_NUM; i-- > 0;) {
+ unsigned int blk_used_cnt = hweight8(priv->acl_masks[i]);
+
+ candidates[blk_used_cnt] = i;
+ acl_used_cnt += blk_used_cnt;
+ }
+
+ if (acl_used_cnt >= YT921X_ACL_NUM) {
+ NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached");
+ return UINT_MAX;
+ }
+ if (acl_used_cnt + entscnt <= YT921X_ACL_NUM)
+ for (unsigned int i = YT921X_ACL_ENT_PER_BLK - entscnt + 1;
+ i-- > 0;)
+ if (candidates[i] >= 0)
+ return YT921X_ACL_ENT_PER_BLK * candidates[i] +
+ ffz(priv->acl_masks[candidates[i]]);
+
+ NL_SET_ERR_MSG_MOD(extack,
+ "ACL entry allocation failed, simplify your rules or remove existing rules");
+ return UINT_MAX;
+}
+
+static int
+yt921x_acl_commit(struct yt921x_priv *priv, unsigned int entid, u8 entsmask)
+{
+ const struct yt921x_acl_rule *aclrule;
+ const struct yt921x_acl_blk *aclblk;
+ unsigned int blkid;
+ unsigned int binid;
+ unsigned long mask;
+ u32 zeros[3] = {};
+ unsigned int i;
+ unsigned int o;
+ u32 ctrl;
+ int res;
+
+ blkid = entid / YT921X_ACL_ENT_PER_BLK;
+ binid = entid % YT921X_ACL_ENT_PER_BLK;
+ aclblk = priv->acl_blks[blkid];
+ aclrule = aclblk->rules[binid];
+
+ /* Write actions */
+ res = yt921x_reg96_write(priv, YT921X_ACLn_ACT(entid),
+ aclrule ? aclrule->action : zeros);
+ if (res)
+ return res;
+
+ /* Select the block */
+ ctrl = YT921X_ACL_BLK_CMD_MODIFY | YT921X_ACL_BLK_CMD_BLKID(blkid);
+ res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl);
+ if (res)
+ return res;
+
+ /* Write keys and masks */
+ ctrl = 0;
+ for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++)
+ ctrl |= YT921X_ACL_BLK_KEEP_KEEPn(i);
+
+ mask = entsmask;
+ i = 0;
+ for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) {
+ res = yt921x_reg64_write(priv, YT921X_ACLn_KEYm(blkid, o),
+ aclrule ? aclrule->entries[i].key :
+ zeros);
+ if (res)
+ return res;
+
+ res = yt921x_reg64_write(priv, YT921X_ACLn_MASKm(blkid, o),
+ aclrule ? aclrule->entries[i].mask :
+ zeros);
+ if (res)
+ return res;
+
+ ctrl &= ~YT921X_ACL_BLK_KEEP_KEEPn(o);
+ i++;
+ }
+
+ res = yt921x_reg_write(priv, YT921X_ACL_BLK_KEEP, ctrl);
+ if (res)
+ return res;
+
+ ctrl = 0;
+ for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) {
+ const struct yt921x_acl_rule *other = aclblk->rules[i];
+
+ if (!other)
+ continue;
+
+ mask = other->mask;
+ for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK)
+ ctrl |= YT921X_ACL_ENTRY_ENm(o) |
+ YT921X_ACL_ENTRY_GRPIDm(o, i);
+ }
+ res = yt921x_reg_write(priv, YT921X_ACLn_ENTRY(blkid), ctrl);
+ if (res)
+ return res;
+
+ /* Commit the block */
+ ctrl = YT921X_ACL_BLK_CMD_BLKID(blkid);
+ res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int
+yt921x_acl_del(struct yt921x_priv *priv, enum tc_setup_type type,
+ unsigned long tag)
+{
+ struct yt921x_acl_rule *aclrule;
+ struct yt921x_acl_blk *aclblk;
+ unsigned int binid;
+ unsigned int blkid;
+ unsigned int entid;
+ int res;
+
+ entid = yt921x_acl_find(priv, type, tag);
+ if (entid == UINT_MAX)
+ return -ENOENT;
+
+ blkid = entid / YT921X_ACL_ENT_PER_BLK;
+ binid = entid % YT921X_ACL_ENT_PER_BLK;
+ aclblk = priv->acl_blks[blkid];
+ aclrule = aclblk->rules[binid];
+
+ aclblk->rules[binid] = NULL;
+ res = yt921x_acl_commit(priv, entid, aclrule->mask);
+ /* the kernel never rolls back on failure */
+
+ if (aclrule->action[0] & YT921X_ACL_ACTa_METER_EN)
+ clear_bit(FIELD_GET(YT921X_ACL_ACTa_METER_ID_M,
+ aclrule->action[0]),
+ priv->meters_map);
+ priv->acl_masks[blkid] &= ~aclrule->mask;
+ kvfree(aclrule);
+ if (!priv->acl_masks[blkid]) {
+ kvfree(aclblk);
+ priv->acl_blks[blkid] = NULL;
+ }
+ return res;
+}
+
+static int
+yt921x_acl_add(struct yt921x_priv *priv,
+ const struct yt921x_acl_rule_ext *ruleext,
+ struct netlink_ext_ack *extack)
+{
+ unsigned int entscnt = hweight8(ruleext->r.mask);
+ struct yt921x_acl_rule *aclrule;
+ struct yt921x_acl_blk *aclblk;
+ bool use_trap = false;
+ unsigned int meterid;
+ unsigned long mask;
+ unsigned int binid;
+ unsigned int blkid;
+ unsigned int entid;
+ unsigned int o;
+ int res;
+
+ /* Allocate resources */
+ entid = yt921x_acl_reserve(priv, entscnt, extack);
+ if (entid == UINT_MAX)
+ return -EOPNOTSUPP;
+
+ if (!(ruleext->r.action[0] & YT921X_ACL_ACTa_METER_EN)) {
+ meterid = YT921X_METER_NUM;
+ } else {
+ meterid = find_first_zero_bit(priv->meters_map,
+ YT921X_METER_NUM);
+ if (meterid < YT921X_METER_NUM) {
+ res = yt921x_meter_config(priv, meterid,
+ &ruleext->marker);
+ if (res)
+ return res;
+ } else if (ruleext->r.sw_assisted) {
+ use_trap = true;
+ } else {
+ NL_SET_ERR_MSG_MOD(extack,
+ "No more meters available");
+ return -EOPNOTSUPP;
+ }
+ }
+
+ /* Prepare acl block ctrlblk */
+ blkid = entid / YT921X_ACL_ENT_PER_BLK;
+ binid = entid % YT921X_ACL_ENT_PER_BLK;
+ aclblk = priv->acl_blks[blkid];
+ if (!aclblk) {
+ aclblk = kvzalloc_obj(*aclblk);
+ if (!aclblk)
+ return -ENOMEM;
+ priv->acl_blks[blkid] = aclblk;
+ }
+
+ /* Prepare acl rule ctrlblk */
+ aclrule = kvmemdup(&ruleext->r,
+ offsetof(struct yt921x_acl_rule, entries[entscnt]),
+ GFP_KERNEL);
+ if (!aclrule) {
+ res = -ENOMEM;
+ goto err;
+ }
+
+ /* Replace the placeholder resource IDs */
+ aclrule->mask = 0;
+ mask = priv->acl_masks[blkid];
+ for_each_clear_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) {
+ aclrule->mask |= BIT(o);
+ entscnt--;
+ if (!entscnt)
+ break;
+ }
+
+ if (use_trap) {
+ aclrule->action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M &
+ ~YT921X_ACL_ACTc_FWD_M;
+ aclrule->action[2] |= YT921X_ACL_ACTc_FWD_EN |
+ YT921X_ACL_ACTc_FWD_TRAP;
+ }
+ if (meterid < YT921X_METER_NUM)
+ aclrule->action[0] |= YT921X_ACL_ACTa_METER_ID(meterid);
+ else
+ aclrule->action[0] &= ~YT921X_ACL_ACTa_METER_EN;
+
+ /* Write rules */
+ aclblk->rules[binid] = aclrule;
+ res = yt921x_acl_commit(priv, entid, aclrule->mask);
+ if (res) {
+ aclblk->rules[binid] = NULL;
+ kvfree(aclrule);
+ goto err;
+ }
+
+ if (meterid < YT921X_METER_NUM)
+ set_bit(meterid, priv->meters_map);
+ priv->acl_masks[blkid] |= aclrule->mask;
+ return 0;
+
+err:
+ if (!priv->acl_masks[blkid]) {
+ kvfree(aclblk);
+ priv->acl_blks[blkid] = NULL;
+ }
+ return res;
+}
+
+int
+yt921x_dsa_cls_flower_del(struct dsa_switch *ds, int port,
+ struct flow_cls_offload *cls, bool ingress)
+{
+ struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_acl_del(priv, TC_SETUP_CLSFLOWER, cls->cookie);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+int
+yt921x_dsa_cls_flower_add(struct dsa_switch *ds, int port,
+ struct flow_cls_offload *cls, bool ingress)
+{
+ struct netlink_ext_ack *extack = cls->common.extack;
+ struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
+ struct yt921x_acl_rule_ext ruleext;
+ int res;
+
+ res = yt921x_acl_rule_ext_parse_flow(&ruleext, port, cls, ingress,
+ priv);
+ if (res)
+ return res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_acl_add(priv, &ruleext, extack);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress)
+{
+ u32 mask;
+
+ if (ingress)
+ mask = YT921X_MIRROR_IGR_PORTn(port);
+ else
+ mask = YT921X_MIRROR_EGR_PORTn(port);
+ return yt921x_reg_clear_bits(priv, YT921X_MIRROR, mask);
+}
+
+static int
+yt921x_mirror_add(struct yt921x_priv *priv, int port, bool ingress,
+ int to_local_port, struct netlink_ext_ack *extack)
+{
+ u32 srcs;
+ u32 ctrl;
+ u32 val;
+ u32 dst;
+ int res;
+
+ if (ingress)
+ srcs = YT921X_MIRROR_IGR_PORTn(port);
+ else
+ srcs = YT921X_MIRROR_EGR_PORTn(port);
+ dst = YT921X_MIRROR_PORT(to_local_port);
+
+ res = yt921x_reg_read(priv, YT921X_MIRROR, &val);
+ if (res)
+ return res;
+
+ /* other mirror tasks & different dst port -> conflict */
+ if ((val & ~srcs & (YT921X_MIRROR_EGR_PORTS_M |
+ YT921X_MIRROR_IGR_PORTS_M)) &&
+ (val & YT921X_MIRROR_PORT_M) != dst) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Sniffer port is already configured, delete existing rules & retry");
+ return -EBUSY;
+ }
+
+ ctrl = val & ~YT921X_MIRROR_PORT_M;
+ ctrl |= srcs;
+ ctrl |= dst;
+
+ if (ctrl == val)
+ return 0;
+
+ return yt921x_reg_write(priv, YT921X_MIRROR, ctrl);
+}
+
+void
+yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror)
+{
+ struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
+ struct device *dev = yt921x_priv_to_device(priv);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_mirror_del(priv, port, mirror->ingress);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dev, "Failed to %s port %d: %i\n", "unmirror",
+ port, res);
+}
+
+int
+yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror,
+ bool ingress, struct netlink_ext_ack *extack)
+{
+ struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_mirror_add(priv, port, ingress,
+ mirror->to_local_port, extack);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+int yt921x_chip_setup_tc(struct yt921x_priv *priv)
+{
+ unsigned int op_ns;
+ u32 ctrl;
+ int res;
+
+ op_ns = 8 * priv->cycle_ns;
+
+ ctrl = max(priv->meter_slot_ns / op_ns, YT921X_METER_SLOT_MIN);
+ res = yt921x_reg_write(priv, YT921X_METER_SLOT, ctrl);
+ if (res)
+ return res;
+ priv->meter_slot_ns = ctrl * op_ns;
+
+ ctrl = max(priv->port_shape_slot_ns / op_ns,
+ YT921X_PORT_SHAPE_SLOT_MIN);
+ res = yt921x_reg_write(priv, YT921X_PORT_SHAPE_SLOT, ctrl);
+ if (res)
+ return res;
+ priv->port_shape_slot_ns = ctrl * op_ns;
+
+ return 0;
+}
+
+int yt921x_chip_setup_acl(struct yt921x_priv *priv)
+{
+ u32 ctrl;
+ int res;
+
+ ctrl = YT921X_ACL_PERMIT_UNMATCH_PORTS_M;
+ res = yt921x_reg_write(priv, YT921X_ACL_PERMIT_UNMATCH, ctrl);
+ if (res)
+ return res;
+
+ ctrl = YT921X_ACL_PORT_PORTS_M;
+ res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl);
+ if (res)
+ return res;
+
+ return 0;
+}
diff --git a/drivers/net/dsa/motorcomm/tc.h b/drivers/net/dsa/motorcomm/tc.h
new file mode 100644
index 000000000000..32b53f1f3825
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/tc.h
@@ -0,0 +1,380 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 David Yang
+ */
+
+#ifndef _YT_TC_H
+#define _YT_TC_H
+
+#include <net/dsa.h>
+#include <net/pkt_cls.h>
+
+#define YT921X_ACLn_ACT(n) (0x1c0000 + 0x10 * (n))
+#define YT921X_ACL_ACTc_STAG_M GENMASK(26, 25)
+#define YT921X_ACL_ACTc_STAG(x) FIELD_PREP(YT921X_ACL_ACTc_STAG_M, (x))
+#define YT921X_ACL_ACTc_STAG_DONTCARE YT921X_ACL_ACTc_STAG(0)
+#define YT921X_ACL_ACTc_STAG_UNTAG YT921X_ACL_ACTc_STAG(1)
+#define YT921X_ACL_ACTc_STAG_TAG YT921X_ACL_ACTc_STAG(2)
+#define YT921X_ACL_ACTc_STAG_KEEP YT921X_ACL_ACTc_STAG(3)
+#define YT921X_ACL_ACTc_CTAG_M GENMASK(24, 23)
+#define YT921X_ACL_ACTc_CTAG(x) FIELD_PREP(YT921X_ACL_ACTc_CTAG_M, (x))
+#define YT921X_ACL_ACTc_CTAG_DONTCARE YT921X_ACL_ACTc_CTAG(0)
+#define YT921X_ACL_ACTc_CTAG_UNTAG YT921X_ACL_ACTc_CTAG(1)
+#define YT921X_ACL_ACTc_CTAG_TAG YT921X_ACL_ACTc_CTAG(2)
+#define YT921X_ACL_ACTc_CTAG_KEEP YT921X_ACL_ACTc_CTAG(3)
+#define YT921X_ACL_ACTc_FWD_M GENMASK(22, 21)
+#define YT921X_ACL_ACTc_FWD(x) FIELD_PREP(YT921X_ACL_ACTc_FWD_M, (x))
+#define YT921X_ACL_ACTc_FWD_FWD YT921X_ACL_ACTc_FWD(0)
+#define YT921X_ACL_ACTc_FWD_COPY YT921X_ACL_ACTc_FWD(1)
+#define YT921X_ACL_ACTc_FWD_REDIR YT921X_ACL_ACTc_FWD(2)
+#define YT921X_ACL_ACTc_FWD_TRAP YT921X_ACL_ACTc_FWD(3)
+#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M GENMASK(20, 10)
+#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS(x) FIELD_PREP(YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M, (x))
+#define YT921X_ACL_ACTc_FWD_REDIR_DPORTn(port) BIT((port) + 10)
+#define YT921X_ACL_ACTc_FWD_EN BIT(9)
+#define YT921X_ACL_ACTc_SDEI BIT(8)
+#define YT921X_ACL_ACTc_SDEI_REPLACE BIT(7)
+#define YT921X_ACL_ACTc_SPRI_M GENMASK(6, 4)
+#define YT921X_ACL_ACTc_SPRI(x) FIELD_PREP(YT921X_ACL_ACTc_SPRI_M, (x))
+#define YT921X_ACL_ACTc_SPRI_REPLACE BIT(3)
+#define YT921X_ACL_ACTbc_SVID_M GENMASK_ULL(34, 23)
+#define YT921X_ACL_ACTbc_SVID(x) FIELD_PREP(YT921X_ACL_ACTbc_SVID_M, (x))
+#define YT921X_ACL_ACTb_SVID_REPLACE BIT(22)
+#define YT921X_ACL_ACTb_CDEI BIT(21)
+#define YT921X_ACL_ACTb_CDEI_REPLACE BIT(20)
+#define YT921X_ACL_ACTb_CPRI_M GENMASK(19, 17)
+#define YT921X_ACL_ACTb_CPRI(x) FIELD_PREP(YT921X_ACL_ACTb_CPRI_M, (x))
+#define YT921X_ACL_ACTb_CPRI_REPLACE BIT(16)
+#define YT921X_ACL_ACTb_CVID_M GENMASK(15, 4)
+#define YT921X_ACL_ACTb_CVID(x) FIELD_PREP(YT921X_ACL_ACTb_CVID_M, (x))
+#define YT921X_ACL_ACTb_CVID_REPLACE BIT(3)
+#define YT921X_ACL_ACTb_PRIO_M GENMASK(2, 0)
+#define YT921X_ACL_ACTb_PRIO(x) FIELD_PREP(YT921X_ACL_ACTb_PRIO_M, (x))
+#define YT921X_ACL_ACTa_PRIO_EN BIT(31)
+#define YT921X_ACL_ACTa_COLOR_M GENMASK(30, 29)
+#define YT921X_ACL_ACTa_COLOR(x) FIELD_PREP(YT921X_ACL_ACTa_COLOR_M, (x))
+#define YT921X_ACL_ACTa_COLOR_GREEN YT921X_ACL_ACTa_COLOR(0)
+#define YT921X_ACL_ACTa_COLOR_YELLOW YT921X_ACL_ACTa_COLOR(1)
+#define YT921X_ACL_ACTa_COLOR_RED YT921X_ACL_ACTa_COLOR(2)
+#define YT921X_ACL_ACTa_COLOR_EN BIT(28)
+#define YT921X_ACL_ACTa_DSCP_M GENMASK(27, 22)
+#define YT921X_ACL_ACTa_DSCP(x) FIELD_PREP(YT921X_ACL_ACTa_DSCP_M, (x))
+#define YT921X_ACL_ACTa_DSCP_REPLACE BIT(21)
+#define YT921X_ACL_ACTa_METER_ID_M GENMASK(20, 15)
+#define YT921X_ACL_ACTa_METER_ID(x) FIELD_PREP(YT921X_ACL_ACTa_METER_ID_M, (x))
+#define YT921X_ACL_ACTa_METER_EN BIT(14)
+#define YT921X_ACL_ACTa_MIRROR_EN BIT(13)
+#define YT921X_ACL_ACTa_FLOWSTAT_EN BIT(12)
+#define YT921X_ACL_ACTa_FLOWSTAT_ID_M GENMASK(11, 6)
+#define YT921X_ACL_ACTa_FLOWSTAT_ID(x) FIELD_PREP(YT921X_ACL_ACTa_FLOWSTAT_ID_M, (x))
+#define YT921X_ACL_ACTa_GPIO_EN BIT(5)
+#define YT921X_ACL_ACTa_GPIO_PIN_M GENMASK(4, 1)
+#define YT921X_ACL_ACTa_GPIO_PIN(x) FIELD_PREP(YT921X_ACL_ACTa_GPIO_PIN_M, (x))
+#define YT921X_ACL_ACTa_INTR_EN BIT(0)
+#define YT921X_ACL_BLK_KEEP 0x201000
+#define YT921X_ACL_BLK_KEEP_GRPIDn_M(bin) (7 << (4 * (bin) + 1))
+#define YT921X_ACL_BLK_KEEP_GRPIDn(bin, x) ((x) << (4 * (bin) + 1))
+#define YT921X_ACL_BLK_KEEP_KEEPn(bin) BIT(4 * (bin))
+#define YT921X_ACL_PORT 0x202000
+#define YT921X_ACL_PORT_PORTS_M GENMASK(10, 0)
+#define YT921X_ACL_PORT_PORTS(x) FIELD_PREP(YT921X_ACL_PORT_PORTS_M, (x))
+#define YT921X_ACL_PORT_PORTn(port) BIT(port)
+#define YT921X_ACL_BLK_CMD 0x202004
+#define YT921X_ACL_BLK_CMD_BLKID_M GENMASK(6, 1)
+#define YT921X_ACL_BLK_CMD_BLKID(x) FIELD_PREP(YT921X_ACL_BLK_CMD_BLKID_M, (x))
+#define YT921X_ACL_BLK_CMD_MODIFY BIT(0)
+#define YT921X_ACLn_ENTRY(blk) (0x203000 + 4 * (blk))
+#define YT921X_ACL_ENTRY_GRPIDm_M(bin) (7 << (4 * (bin) + 1))
+#define YT921X_ACL_ENTRY_GRPIDm(bin, x) ((x) << (4 * (bin) + 1))
+#define YT921X_ACL_ENTRY_ENm(bin) BIT(4 * (bin))
+#define YT921X_ACLn_KEYm(blk, bin) (0x204000 + 0x200 * (bin) + 8 * (blk))
+#define YT921X_ACL_KEYb_ORD_M GENMASK(29, 21)
+#define YT921X_ACL_KEYb_ORD(x) FIELD_PREP(YT921X_ACL_KEYb_ORD_M, (x))
+#define YT921X_ACL_KEYb_SPORTS_M GENMASK(20, 10)
+#define YT921X_ACL_KEYb_SPORTS(x) FIELD_PREP(YT921X_ACL_KEYb_SPORTS_M, (x))
+#define YT921X_ACL_KEYb_SPORTn(port) BIT((port) + 10)
+#define YT921X_ACL_KEYb_REVERSE BIT(9) /* reverse match */
+#define YT921X_ACL_KEYb_TYPE_M GENMASK(8, 4)
+#define YT921X_ACL_KEYb_TYPE(x) FIELD_PREP(YT921X_ACL_KEYb_TYPE_M, (x))
+/* KEY_* fields need no masks */
+#define YT921X_ACLn_MASKm(blk, bin) (0x205000 + 0x200 * (bin) + 8 * (blk))
+
+enum yt921x_acl_type {
+ YT921X_ACL_TYPE_NA,
+ YT921X_ACL_TYPE_MAC_DA0,
+ YT921X_ACL_TYPE_MAC_SA0,
+ YT921X_ACL_TYPE_MAC_DA1_SA1,
+ YT921X_ACL_TYPE_VLAN,
+ YT921X_ACL_TYPE_VTAG,
+ YT921X_ACL_TYPE_IPV4_DA,
+ YT921X_ACL_TYPE_IPV4_SA,
+ YT921X_ACL_TYPE_IPV6_DA0,
+ YT921X_ACL_TYPE_IPV6_DA1,
+ YT921X_ACL_TYPE_IPV6_DA2,
+ YT921X_ACL_TYPE_IPV6_DA3,
+ YT921X_ACL_TYPE_IPV6_SA0,
+ YT921X_ACL_TYPE_IPV6_SA1,
+ YT921X_ACL_TYPE_IPV6_SA2,
+ YT921X_ACL_TYPE_IPV6_SA3,
+ YT921X_ACL_TYPE_MISC,
+ YT921X_ACL_TYPE_L4,
+ YT921X_ACL_TYPE_UDF0,
+ YT921X_ACL_TYPE_UDF1,
+ YT921X_ACL_TYPE_UDF2,
+ YT921X_ACL_TYPE_UDF3,
+ YT921X_ACL_TYPE_UDF4,
+ YT921X_ACL_TYPE_UDF5,
+ YT921X_ACL_TYPE_UDF6,
+ YT921X_ACL_TYPE_UDF7,
+ YT921X_ACL_TYPE_ETHERTYPE,
+ YT921X_ACL_TYPE_NUM
+};
+
+/* Range: turn KEY:MASK into MIN:MAX */
+
+#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M GENMASK(3, 0)
+#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M, (x))
+#define YT921X_ACL_BINa_MAC_xA0_MAC_xA0_M GENMASK(31, 0)
+
+#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M GENMASK(2, 0)
+#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M, (x))
+#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_DA1_M GENMASK(31, 16)
+#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_SA1_M GENMASK(15, 0)
+
+#define YT921X_ACL_KEYb_VLAN_SVID_RANGE_EN BIT(31)
+#define YT921X_ACL_KEYb_VLAN_CVID_RANGE_EN BIT(30)
+#define YT921X_ACL_BINb_VLAN_CDEI BIT(3)
+#define YT921X_ACL_BINb_VLAN_CPRI_M GENMASK(2, 0)
+#define YT921X_ACL_BINb_VLAN_CPRI(x) FIELD_PREP(YT921X_ACL_BINb_VLAN_CPRI_M, (x))
+#define YT921X_ACL_BINa_VLAN_CTAG_FMT_M GENMASK(31, 30)
+#define YT921X_ACL_BINa_VLAN_CTAG_FMT(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_CTAG_FMT_M, (x))
+#define YT921X_ACL_BINa_VLAN_SDEI BIT(29)
+#define YT921X_ACL_BINa_VLAN_SPRI_M GENMASK(28, 26)
+#define YT921X_ACL_BINa_VLAN_SPRI(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_SPRI_M, (x))
+#define YT921X_ACL_BINa_VLAN_STAG_FMT_M GENMASK(25, 24)
+#define YT921X_ACL_BINa_VLAN_STAG_FMT(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_STAG_FMT_M, (x))
+#define YT921X_ACL_BINa_VLAN_SVID_M GENMASK(23, 12)
+#define YT921X_ACL_BINa_VLAN_SVID(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_SVID_M, (x))
+#define YT921X_ACL_BINa_VLAN_CVID_M GENMASK(11, 0)
+#define YT921X_ACL_BINa_VLAN_CVID(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_CVID_M, (x))
+
+#define YT921X_ACL_KEYb_VTAG_SVID_RANGE_EN BIT(31)
+#define YT921X_ACL_KEYb_VTAG_CVID_RANGE_EN BIT(30)
+#define YT921X_ACL_BINa_VTAG_CDEI BIT(31)
+#define YT921X_ACL_BINa_VTAG_CPRI_M GENMASK(30, 28)
+#define YT921X_ACL_BINa_VTAG_CPRI(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_CPRI_M, (x))
+#define YT921X_ACL_BINa_VTAG_SDEI BIT(27)
+#define YT921X_ACL_BINa_VTAG_SPRI_M GENMASK(26, 24)
+#define YT921X_ACL_BINa_VTAG_SPRI(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_SPRI_M, (x))
+#define YT921X_ACL_BINa_VTAG_SVID_M GENMASK(23, 12)
+#define YT921X_ACL_BINa_VTAG_SVID(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_SVID_M, (x))
+#define YT921X_ACL_BINa_VTAG_CVID_M GENMASK(11, 0)
+#define YT921X_ACL_BINa_VTAG_CVID(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_CVID_M, (x))
+
+#define YT921X_ACL_KEYb_IPV4_ADDR_RANGE_EN BIT(30)
+#define YT921X_ACL_BINb_IPV4_FRAG BIT(3)
+#define YT921X_ACL_BINb_IPV4_L4_TYPE_M GENMASK(2, 0)
+#define YT921X_ACL_BINb_IPV4_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_IPV4_L4_TYPE_M, (x))
+#define YT921X_ACL_BINa_IPV4_ADDR_M GENMASK(31, 0)
+
+#define YT921X_ACL_BINb_IPV6_L4_TYPE_M GENMASK(2, 0)
+#define YT921X_ACL_BINb_IPV6_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_IPV6_L4_TYPE_M, (x))
+#define YT921X_ACL_BINa_IPV6_ADDRx_M GENMASK(31, 0)
+
+#define YT921X_ACL_BINb_IPV6_xA1_IP_OPTION BIT(3)
+
+#define YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG BIT(3)
+
+#define YT921X_ACL_KEYb_IPV6_xA3_ADDR_RANGE_EN BIT(30)
+#define YT921X_ACL_BINb_IPV6_xA3_FRAG BIT(3)
+
+#define YT921X_ACL_BINb_MISC_FRAG BIT(3)
+#define YT921X_ACL_BINb_MISC_L4_TYPE_M GENMASK(2, 0)
+#define YT921X_ACL_BINb_MISC_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MISC_L4_TYPE_M, (x))
+#define YT921X_ACL_BINa_MISC_PPPOE_FLAG BIT(30)
+#define YT921X_ACL_BINa_MISC_FIRST_FRAG BIT(29)
+#define YT921X_ACL_BINa_MISC_IP_OPTION BIT(28)
+#define YT921X_ACL_BINa_MISC_TCP_FLAGS_M GENMASK(27, 20)
+#define YT921X_ACL_BINa_MISC_TCP_FLAGS(x) FIELD_PREP(YT921X_ACL_BINa_MISC_TCP_FLAGS_M, (x))
+#define YT921X_ACL_BINa_MISC_IP_PROTO_M GENMASK(19, 12)
+#define YT921X_ACL_BINa_MISC_IP_PROTO(x) FIELD_PREP(YT921X_ACL_BINa_MISC_IP_PROTO_M, (x))
+#define YT921X_ACL_BINa_MISC_TOS_M GENMASK(11, 4)
+#define YT921X_ACL_BINa_MISC_TOS(x) FIELD_PREP(YT921X_ACL_BINa_MISC_TOS_M, (x))
+#define YT921X_ACL_BINa_MISC_L3_TYPE_M GENMASK(3, 0)
+#define YT921X_ACL_BINa_MISC_L3_TYPE(x) FIELD_PREP(YT921X_ACL_BINa_MISC_L3_TYPE_M, (x))
+
+#define YT921X_ACL_KEYb_L4_DPORT_RANGE_EN BIT(31)
+#define YT921X_ACL_KEYb_L4_SPORT_RANGE_EN BIT(30)
+#define YT921X_ACL_BINb_L4_FRAG BIT(3)
+#define YT921X_ACL_BINb_L4_TYPE_M GENMASK(2, 0)
+#define YT921X_ACL_BINb_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_L4_TYPE_M, (x))
+#define YT921X_ACL_BINa_L4_DPORT_M GENMASK(31, 16)
+#define YT921X_ACL_BINa_L4_SPORT_M GENMASK(15, 0)
+
+#define YT921X_ACL_BINb_UDF_IS_IGMP BIT(0)
+#define YT921X_ACL_BINa_UDF_UDF0_M GENMASK(31, 16)
+#define YT921X_ACL_BINa_UDF_UDF0(x) FIELD_PREP(YT921X_ACL_BINa_UDF_UDF0_M, (x))
+#define YT921X_ACL_BINa_UDF_UDF1_M GENMASK(15, 0)
+#define YT921X_ACL_BINa_UDF_UDF1(x) FIELD_PREP(YT921X_ACL_BINa_UDF_UDF1_M, (x))
+
+#define YT921X_ACL_KEYb_ETHERTYPE_ETHERTYPE_RANGE_EN BIT(30)
+#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M GENMASK(2, 0)
+#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M, (x))
+#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M GENMASK(15, 0)
+#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE(x) FIELD_PREP(YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M, (x))
+
+enum yt921x_l2_type {
+ YT921X_L2_TYPE_ETH,
+ YT921X_L2_TYPE_ETHV2,
+ YT921X_L2_TYPE_ETHSAP,
+ YT921X_L2_TYPE_ETHSNAP,
+};
+
+enum yt921x_l3_type {
+ YT921X_L3_TYPE_OTHER,
+ YT921X_L3_TYPE_IPV4,
+ YT921X_L3_TYPE_IPV6,
+ YT921X_L3_TYPE_ARP,
+ YT921X_L3_TYPE_LLDP,
+ YT921X_L3_TYPE_PAE,
+ YT921X_L3_TYPE_ERP,
+ YT921X_L3_TYPE_SLOW_PROTOCOL,
+};
+
+enum yt921x_l4_type {
+ YT921X_L4_TYPE_OTHER,
+ YT921X_L4_TYPE_TCP,
+ YT921X_L4_TYPE_UDP,
+ YT921X_L4_TYPE_UDPLITE,
+ YT921X_L4_TYPE_ICMP,
+ YT921X_L4_TYPE_IGMP,
+ YT921X_L4_TYPE_MLD,
+ YT921X_L4_TYPE_ND,
+};
+
+#define YT921X_UDFn_CTRL(x) (0x210094 + 4 * (x))
+#define YT921X_UDF_CTRL_UDF_TYPE_M GENMASK(8, 7)
+#define YT921X_UDF_CTRL_UDF_TYPE(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_TYPE_M, (x))
+#define YT921X_UDF_CTRL_UDF_TYPE_ETH YT921X_UDF_CTRL_UDF_TYPE(0)
+#define YT921X_UDF_CTRL_UDF_TYPE_L3 YT921X_UDF_CTRL_UDF_TYPE(1)
+#define YT921X_UDF_CTRL_UDF_TYPE_L4 YT921X_UDF_CTRL_UDF_TYPE(2)
+#define YT921X_UDF_CTRL_UDF_OFFSET_M GENMASK(6, 0)
+#define YT921X_UDF_CTRL_UDF_OFFSET(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_OFFSET_M, (x))
+
+#define YT921X_PORTn_RATE(port) (0x220000 + 4 * (port))
+#define YT921X_PORT_RATE_GAP_VALUE GENMASK(4, 0) /* default 20 */
+#define YT921X_METER_SLOT 0x220104
+#define YT921X_METER_SLOT_SLOT_M GENMASK(11, 0)
+#define YT921X_PORTn_METER(port) (0x220108 + 4 * (port))
+#define YT921X_PORT_METER_EN BIT(4)
+#define YT921X_PORT_METER_ID_M GENMASK(3, 0)
+#define YT921X_PORT_METER_ID(x) FIELD_PREP(YT921X_PORT_METER_ID_M, (x))
+#define YT921X_METERn_CTRL(x) (0x220800 + 0x10 * (x))
+#define YT921X_METER_CTRLc_METER_EN BIT(14)
+#define YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN BIT(13) /* RFC4115: yellow use unused green bw */
+#define YT921X_METER_CTRLc_DROP_M GENMASK(12, 11)
+#define YT921X_METER_CTRLc_DROP(x) FIELD_PREP(YT921X_METER_CTRLc_DROP_M, (x))
+#define YT921X_METER_CTRLc_DROP_GYR YT921X_METER_CTRLc_DROP(0)
+#define YT921X_METER_CTRLc_DROP_YR YT921X_METER_CTRLc_DROP(1)
+#define YT921X_METER_CTRLc_DROP_R YT921X_METER_CTRLc_DROP(2)
+#define YT921X_METER_CTRLc_DROP_NONE YT921X_METER_CTRLc_DROP(3)
+#define YT921X_METER_CTRLc_COLOR_BLIND BIT(10)
+#define YT921X_METER_CTRLc_UNIT_M GENMASK(9, 7)
+#define YT921X_METER_CTRLc_UNIT(x) FIELD_PREP(YT921X_METER_CTRLc_UNIT_M, (x))
+#define YT921X_METER_CTRLc_BYTE_MODE_INCLUDE_GAP BIT(6) /* +GAP_VALUE bytes each packet */
+#define YT921X_METER_CTRLc_PKT_MODE BIT(5) /* 0: byte rate mode */
+#define YT921X_METER_CTRLc_RFC2698 BIT(4) /* 0: RFC4115 */
+#define YT921X_METER_CTRLbc_CBS_M GENMASK_ULL(35, 20)
+#define YT921X_METER_CTRLbc_CBS(x) FIELD_PREP(YT921X_METER_CTRLbc_CBS_M, (x))
+#define YT921X_METER_CTRLb_CIR_M GENMASK(19, 2)
+#define YT921X_METER_CTRLb_CIR(x) FIELD_PREP(YT921X_METER_CTRLb_CIR_M, (x))
+#define YT921X_METER_CTRLab_EBS_M GENMASK_ULL(33, 18)
+#define YT921X_METER_CTRLab_EBS(x) FIELD_PREP(YT921X_METER_CTRLab_EBS_M, (x))
+#define YT921X_METER_CTRLa_EIR_M GENMASK(17, 0)
+#define YT921X_METER_CTRLa_EIR(x) FIELD_PREP(YT921X_METER_CTRLa_EIR_M, (x))
+#define YT921X_METERn_STAT(x) (0x221000 + 8 * (x))
+
+#define YT921X_MIRROR 0x300300
+#define YT921X_MIRROR_IGR_PORTS_M GENMASK(26, 16)
+#define YT921X_MIRROR_IGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_IGR_PORTS_M, (x))
+#define YT921X_MIRROR_IGR_PORTn(port) BIT((port) + 16)
+#define YT921X_MIRROR_EGR_PORTS_M GENMASK(14, 4)
+#define YT921X_MIRROR_EGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_EGR_PORTS_M, (x))
+#define YT921X_MIRROR_EGR_PORTn(port) BIT((port) + 4)
+#define YT921X_MIRROR_PORT_M GENMASK(3, 0)
+#define YT921X_MIRROR_PORT(x) FIELD_PREP(YT921X_MIRROR_PORT_M, (x))
+
+#define YT921X_PORT_SHAPE_SLOT 0x34000c
+#define YT921X_PORT_SHAPE_SLOT_SLOT_M GENMASK(11, 0)
+#define YT921X_PORTn_SHAPE_CTRL(port) (0x354000 + 8 * (port))
+#define YT921X_PORT_SHAPE_CTRLb_EN BIT(4)
+#define YT921X_PORT_SHAPE_CTRLb_PKT_MODE BIT(3) /* 0: byte rate mode */
+#define YT921X_PORT_SHAPE_CTRLb_UNIT_M GENMASK(2, 0)
+#define YT921X_PORT_SHAPE_CTRLb_UNIT(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLb_UNIT_M, (x))
+#define YT921X_PORT_SHAPE_CTRLa_CBS_M GENMASK(31, 18)
+#define YT921X_PORT_SHAPE_CTRLa_CBS(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CBS_M, (x))
+#define YT921X_PORT_SHAPE_CTRLa_CIR_M GENMASK(17, 0)
+#define YT921X_PORT_SHAPE_CTRLa_CIR(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CIR_M, (x))
+#define YT921X_PORTn_SHAPE_STAT(port) (0x356000 + 4 * (port))
+
+#define YT921X_TOKEN_BYTE_C 1 /* 1 token = 2^1 byte */
+#define YT921X_TOKEN_PKT_C -6 /* 1 token = 2^-6 packets */
+#define YT921X_TOKEN_RATE_C -15
+#define YT921X_METER_SLOT_MIN 80
+#define YT921X_METER_UNIT_MAX ((1 << 3) - 1)
+#define YT921X_METER_CIR_MAX ((1 << 18) - 1)
+#define YT921X_METER_CBS_MAX ((1 << 16) - 1)
+#define YT921X_PORT_SHAPE_SLOT_MIN 80
+#define YT921X_SHAPE_UNIT_MAX ((1 << 3) - 1)
+#define YT921X_SHAPE_CIR_MAX ((1 << 18) - 1)
+#define YT921X_SHAPE_CBS_MAX ((1 << 14) - 1)
+
+#define YT921X_ACL_ENT_PER_BLK 8
+#define YT921X_ACL_NUM 384
+
+struct yt921x_acl_entry {
+ u32 key[2];
+ u32 mask[2];
+};
+
+struct yt921x_acl_rule {
+ unsigned long tag;
+ enum tc_setup_type type;
+
+ u32 action[3];
+ bool sw_assisted;
+
+ u8 mask;
+ struct yt921x_acl_entry entries[YT921X_ACL_ENT_PER_BLK];
+};
+
+struct yt921x_acl_blk {
+ struct yt921x_acl_rule *rules[YT921X_ACL_ENT_PER_BLK];
+};
+
+void yt921x_dsa_port_policer_del(struct dsa_switch *ds, int port);
+int
+yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port,
+ const struct flow_action_police *police,
+ struct netlink_ext_ack *extack);
+int
+yt921x_dsa_port_setup_tc(struct dsa_switch *ds, int port,
+ enum tc_setup_type type, void *type_data);
+int
+yt921x_dsa_cls_flower_del(struct dsa_switch *ds, int port,
+ struct flow_cls_offload *cls, bool ingress);
+int
+yt921x_dsa_cls_flower_add(struct dsa_switch *ds, int port,
+ struct flow_cls_offload *cls, bool ingress);
+void
+yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror);
+int
+yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror,
+ bool ingress, struct netlink_ext_ack *extack);
+
+int yt921x_chip_setup_tc(struct yt921x_priv *priv);
+int yt921x_chip_setup_acl(struct yt921x_priv *priv);
+
+#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH net-next v3 2/4] net: dsa: motorcomm: Use NSEC_PER_SEC
2026-10-09 16:58 [PATCH net-next v3 0/4] net: dsa: motorcomm: TC offload follow-ups David Yang
2026-10-09 16:58 ` [PATCH net-next v3 1/4] net: dsa: motorcomm: Split TC module David Yang
@ 2026-10-09 16:58 ` David Yang
2026-10-09 16:58 ` [PATCH net-next v3 3/4] net: dsa: motorcomm: Enable ACL on demand David Yang
2026-10-09 16:58 ` [PATCH net-next v3 4/4] net: dsa: motorcomm: Add TC prio support David Yang
3 siblings, 0 replies; 8+ messages in thread
From: David Yang @ 2026-10-09 16:58 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
Get rid of magic numbers.
Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/motorcomm/tc.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/tc.c b/drivers/net/dsa/motorcomm/tc.c
index 433a428ed070..be3321df8868 100644
--- a/drivers/net/dsa/motorcomm/tc.c
+++ b/drivers/net/dsa/motorcomm/tc.c
@@ -25,14 +25,14 @@ static u32 rate2token(u64 rate, unsigned int slot_ns, int unit, int C)
{
int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
- return div_u64(ldexpu64(slot_ns * rate, -e), 1000000000);
+ return div_u64(ldexpu64(slot_ns * rate, -e), NSEC_PER_SEC);
}
static u64 token2rate(u32 token, unsigned int slot_ns, int unit, int C)
{
int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
- return div_u64(ldexpu64(mul_u32_u32(1000000000, token), e), slot_ns);
+ return div_u64(ldexpu64(mul_u32_u32(NSEC_PER_SEC, token), e), slot_ns);
}
/* burst = 2^C * token * 4^unit */
@@ -83,7 +83,7 @@ yt921x_marker_tfm(struct yt921x_marker *marker, u64 rate, u64 burst,
}
/* Check for matching burst */
- burst_est = div_u64(slot_ns * rate, 1000000000);
+ burst_est = div_u64(slot_ns * rate, NSEC_PER_SEC);
burst_sug = burst_est;
if (flags & YT921X_MARKER_PKT_MODE)
burst_sug++;
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH net-next v3 3/4] net: dsa: motorcomm: Enable ACL on demand
2026-10-09 16:58 [PATCH net-next v3 0/4] net: dsa: motorcomm: TC offload follow-ups David Yang
2026-10-09 16:58 ` [PATCH net-next v3 1/4] net: dsa: motorcomm: Split TC module David Yang
2026-10-09 16:58 ` [PATCH net-next v3 2/4] net: dsa: motorcomm: Use NSEC_PER_SEC David Yang
@ 2026-10-09 16:58 ` David Yang
2026-10-10 17:14 ` netdev-bot+sashiko
2026-10-09 16:58 ` [PATCH net-next v3 4/4] net: dsa: motorcomm: Add TC prio support David Yang
3 siblings, 1 reply; 8+ messages in thread
From: David Yang @ 2026-10-09 16:58 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
Track the usage of ACL rules and enable it per port only when it is
actually used. This is aligned with ODM recommendations and helps reduce
latency.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.h | 2 +
drivers/net/dsa/motorcomm/tc.c | 70 ++++++++++++++++++++++++++++----
2 files changed, 64 insertions(+), 8 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 012de4f444f5..7047ad98f431 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -462,6 +462,8 @@ struct yt921x_port {
/* BR_ISOLATED */
bool isolated:1;
+ unsigned short acl_cnt;
+
#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
unsigned char led_duty;
unsigned short led_cycle;
diff --git a/drivers/net/dsa/motorcomm/tc.c b/drivers/net/dsa/motorcomm/tc.c
index be3321df8868..a302016912b5 100644
--- a/drivers/net/dsa/motorcomm/tc.c
+++ b/drivers/net/dsa/motorcomm/tc.c
@@ -1055,6 +1055,19 @@ yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port,
return 0;
}
+/* Ports that _should_ have ACL activated on. ACL_PORT \succeq activated_ports()
+ */
+static u16 yt921x_acl_activated_ports(const struct yt921x_priv *priv)
+{
+ u16 mask = 0;
+
+ for (int port = 0; port < YT921X_PORT_NUM; port++)
+ if (priv->ports[port].acl_cnt)
+ mask |= BIT(port);
+
+ return mask;
+}
+
static unsigned int
yt921x_acl_find(const struct yt921x_priv *priv, enum tc_setup_type type,
unsigned long tag)
@@ -1199,10 +1212,15 @@ yt921x_acl_del(struct yt921x_priv *priv, enum tc_setup_type type,
{
struct yt921x_acl_rule *aclrule;
struct yt921x_acl_blk *aclblk;
+ bool refresh_en = false;
unsigned int binid;
unsigned int blkid;
unsigned int entid;
+ unsigned long m;
+ unsigned int o;
+ u32 ctrl;
int res;
+ int ret;
entid = yt921x_acl_find(priv, type, tag);
if (entid == UINT_MAX)
@@ -1214,9 +1232,27 @@ yt921x_acl_del(struct yt921x_priv *priv, enum tc_setup_type type,
aclrule = aclblk->rules[binid];
aclblk->rules[binid] = NULL;
- res = yt921x_acl_commit(priv, entid, aclrule->mask);
+ ret = yt921x_acl_commit(priv, entid, aclrule->mask);
/* the kernel never rolls back on failure */
+ m = FIELD_GET(YT921X_ACL_KEYb_SPORTS_M, aclrule->entries[0].key[1]);
+ for_each_set_bit(o, &m, YT921X_PORT_NUM) {
+ struct yt921x_port *pp = &priv->ports[o];
+
+ if (!WARN_ON(!pp->acl_cnt)) {
+ pp->acl_cnt--;
+ if (pp->acl_cnt)
+ continue;
+ }
+ refresh_en = true;
+ }
+ if (refresh_en) {
+ ctrl = yt921x_acl_activated_ports(priv);
+ res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl);
+ if (res)
+ ret = res;
+ }
+
if (aclrule->action[0] & YT921X_ACL_ACTa_METER_EN)
clear_bit(FIELD_GET(YT921X_ACL_ACTa_METER_ID_M,
aclrule->action[0]),
@@ -1227,7 +1263,7 @@ yt921x_acl_del(struct yt921x_priv *priv, enum tc_setup_type type,
kvfree(aclblk);
priv->acl_blks[blkid] = NULL;
}
- return res;
+ return ret;
}
static int
@@ -1240,11 +1276,12 @@ yt921x_acl_add(struct yt921x_priv *priv,
struct yt921x_acl_blk *aclblk;
bool use_trap = false;
unsigned int meterid;
- unsigned long mask;
unsigned int binid;
unsigned int blkid;
unsigned int entid;
+ unsigned long m;
unsigned int o;
+ u32 ctrl;
int res;
/* Allocate resources */
@@ -1293,8 +1330,8 @@ yt921x_acl_add(struct yt921x_priv *priv,
/* Replace the placeholder resource IDs */
aclrule->mask = 0;
- mask = priv->acl_masks[blkid];
- for_each_clear_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) {
+ m = priv->acl_masks[blkid];
+ for_each_clear_bit(o, &m, YT921X_ACL_ENT_PER_BLK) {
aclrule->mask |= BIT(o);
entscnt--;
if (!entscnt)
@@ -1312,21 +1349,39 @@ yt921x_acl_add(struct yt921x_priv *priv,
else
aclrule->action[0] &= ~YT921X_ACL_ACTa_METER_EN;
+ /* Activate ACL if needed */
+ ctrl = 0;
+ m = FIELD_GET(YT921X_ACL_KEYb_SPORTS_M, ruleext->r.entries[0].key[1]);
+ for_each_set_bit(o, &m, YT921X_PORT_NUM)
+ if (!priv->ports[o].acl_cnt)
+ ctrl |= YT921X_ACL_PORT_PORTn(o);
+ if (ctrl) {
+ ctrl |= yt921x_acl_activated_ports(priv);
+ res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl);
+ if (res)
+ goto err;
+ }
+
/* Write rules */
aclblk->rules[binid] = aclrule;
res = yt921x_acl_commit(priv, entid, aclrule->mask);
if (res) {
aclblk->rules[binid] = NULL;
- kvfree(aclrule);
goto err;
}
+ /* Bookkeeping */
+ m = FIELD_GET(YT921X_ACL_KEYb_SPORTS_M, ruleext->r.entries[0].key[1]);
+ for_each_set_bit(o, &m, YT921X_PORT_NUM)
+ priv->ports[o].acl_cnt++;
+
if (meterid < YT921X_METER_NUM)
set_bit(meterid, priv->meters_map);
priv->acl_masks[blkid] |= aclrule->mask;
return 0;
err:
+ kvfree(aclrule);
if (!priv->acl_masks[blkid]) {
kvfree(aclblk);
priv->acl_blks[blkid] = NULL;
@@ -1487,8 +1542,7 @@ int yt921x_chip_setup_acl(struct yt921x_priv *priv)
if (res)
return res;
- ctrl = YT921X_ACL_PORT_PORTS_M;
- res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl);
+ res = yt921x_reg_write(priv, YT921X_ACL_PORT, 0);
if (res)
return res;
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH net-next v3 4/4] net: dsa: motorcomm: Add TC prio support
2026-10-09 16:58 [PATCH net-next v3 0/4] net: dsa: motorcomm: TC offload follow-ups David Yang
` (2 preceding siblings ...)
2026-10-09 16:58 ` [PATCH net-next v3 3/4] net: dsa: motorcomm: Enable ACL on demand David Yang
@ 2026-10-09 16:58 ` David Yang
2026-10-10 17:14 ` netdev-bot+sashiko
3 siblings, 1 reply; 8+ messages in thread
From: David Yang @ 2026-10-09 16:58 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
The HW orders ACL rules by the 9-bit ORD field (512 values, larger
matching first), while TC priorities span 1..U16_MAX with the smallest
meaning the highest. Map them as evenly as possible, and reject others
which do not map onto a hardware order instead of silently colliding
with a neighbouring one.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/tc.c | 35 +++++++++++++++++++++++++++++++++-
drivers/net/dsa/motorcomm/tc.h | 1 +
2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/motorcomm/tc.c b/drivers/net/dsa/motorcomm/tc.c
index a302016912b5..3e0f80500f41 100644
--- a/drivers/net/dsa/motorcomm/tc.c
+++ b/drivers/net/dsa/motorcomm/tc.c
@@ -1029,7 +1029,10 @@ yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port,
const struct flow_cls_offload *cls, bool ingress,
struct yt921x_priv *priv)
{
+ const unsigned int scale = (U16_MAX + 1) / YT921X_ACL_ORD_NUM;
struct netlink_ext_ack *extack = cls->common.extack;
+ u32 prio = cls->common.prio;
+ u32 ord;
int res;
if (!ingress) {
@@ -1042,6 +1045,36 @@ yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port,
return -EOPNOTSUPP;
}
+ /* Lower TC priorities take precedence, while larger orders win in
+ * hardware. Spread the 512 hardware orders over the TC priority range
+ * as evenly as possible, retaining the relative order:
+ *
+ * prio 65535 -> ORD 0 catchall filter
+ * prio 65408 -> ORD 1 highest mappable TC priority
+ * ...
+ * prio 49152 -> ORD 128 default value for the first tc flower rule
+ * ...
+ * prio 128 -> ORD 511
+ * prio 0 (invalid TC priority)
+ *
+ * The catchall filter always comes with priority 65535; pin it to ORD
+ * 0, the lowest hardware order, instead of rejecting it.
+ *
+ * Prefer explicit rejects over implicit behaviour changes: reject
+ * the priorities which do not map onto a hardware order instead of
+ * silently colliding with a neighbouring one.
+ */
+ if (prio == U16_MAX) {
+ ord = 0;
+ } else if (!(prio % scale)) {
+ ord = YT921X_ACL_ORD_NUM - prio / scale;
+ } else {
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "Invalid priority %u, must be a multiple of %u or 65535",
+ prio, scale);
+ return -EOPNOTSUPP;
+ }
+
res = yt921x_acl_rule_ext_parse_flow_action(ruleext, cls, priv, port);
if (res)
return res;
@@ -1049,7 +1082,7 @@ yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port,
if (res)
return res;
- yt921x_acl_rule_set_ports(&ruleext->r, 0, BIT(port));
+ yt921x_acl_rule_set_ports(&ruleext->r, ord, BIT(port));
ruleext->r.tag = cls->cookie;
ruleext->r.type = TC_SETUP_CLSFLOWER;
return 0;
diff --git a/drivers/net/dsa/motorcomm/tc.h b/drivers/net/dsa/motorcomm/tc.h
index 32b53f1f3825..f11136a1c5e2 100644
--- a/drivers/net/dsa/motorcomm/tc.h
+++ b/drivers/net/dsa/motorcomm/tc.h
@@ -331,6 +331,7 @@ enum yt921x_l4_type {
#define YT921X_ACL_ENT_PER_BLK 8
#define YT921X_ACL_NUM 384
+#define YT921X_ACL_ORD_NUM 512
struct yt921x_acl_entry {
u32 key[2];
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH net-next v3 3/4] net: dsa: motorcomm: Enable ACL on demand
2026-10-09 16:58 ` [PATCH net-next v3 3/4] net: dsa: motorcomm: Enable ACL on demand David Yang
@ 2026-10-10 17:14 ` netdev-bot+sashiko
0 siblings, 0 replies; 8+ messages in thread
From: netdev-bot+sashiko @ 2026-10-10 17:14 UTC (permalink / raw)
To: mmyangfl
Cc: netdev, andrew, olteanv, davem, edumazet, kuba, pabeni,
linux-kernel
Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.
Low priority / nit picks for your consideration:
- [Low] yt921x_acl_add() in drivers/net/dsa/motorcomm/tc.c writes
YT921X_ACL_PORT before the rule is programmed.
--
net: dsa: motorcomm: Enable ACL on demand
This commit adds a per-port acl_cnt to struct yt921x_port. It now writes
YT921X_ACL_PORT only for ports that have ACL rules, instead of enabling
ACL on all ports at setup time. The register is updated from
yt921x_acl_add() and yt921x_acl_del() when a port's rule count goes from
zero to non-zero or back.
> diff --git a/drivers/net/dsa/motorcomm/tc.c b/drivers/net/dsa/motorcomm/tc.c
> index be3321df88683..a302016912b5f 100644
> --- a/drivers/net/dsa/motorcomm/tc.c
> +++ b/drivers/net/dsa/motorcomm/tc.c
[ ... ]
> @@ -1312,21 +1349,39 @@ yt921x_acl_add(struct yt921x_priv *priv,
> else
> aclrule->action[0] &= ~YT921X_ACL_ACTa_METER_EN;
>
> + /* Activate ACL if needed */
> + ctrl = 0;
> + m = FIELD_GET(YT921X_ACL_KEYb_SPORTS_M, ruleext->r.entries[0].key[1]);
> + for_each_set_bit(o, &m, YT921X_PORT_NUM)
> + if (!priv->ports[o].acl_cnt)
> + ctrl |= YT921X_ACL_PORT_PORTn(o);
> + if (ctrl) {
> + ctrl |= yt921x_acl_activated_ports(priv);
> + res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl);
> + if (res)
> + goto err;
> + }
> +
> /* Write rules */
> aclblk->rules[binid] = aclrule;
> res = yt921x_acl_commit(priv, entid, aclrule->mask);
> if (res) {
> aclblk->rules[binid] = NULL;
> - kvfree(aclrule);
> goto err;
> }
>
> + /* Bookkeeping */
> + m = FIELD_GET(YT921X_ACL_KEYb_SPORTS_M, ruleext->r.entries[0].key[1]);
> + for_each_set_bit(o, &m, YT921X_PORT_NUM)
> + priv->ports[o].acl_cnt++;
> +
> if (meterid < YT921X_METER_NUM)
> set_bit(meterid, priv->meters_map);
> priv->acl_masks[blkid] |= aclrule->mask;
> return 0;
>
> err:
> + kvfree(aclrule);
> if (!priv->acl_masks[blkid]) {
> kvfree(aclblk);
> priv->acl_blks[blkid] = NULL;
[Severity: Low]
If yt921x_acl_commit() fails here, should YT921X_ACL_PORT be restored
before returning?
The enable bits for the new rule's source ports have already been written
by then. The err path frees aclrule (and aclblk if it is now empty), but
it doesn't write YT921X_ACL_PORT back to yt921x_acl_activated_ports(priv).
acl_cnt++ only runs on success, so the driver counts zero rules on those
ports while the hardware still has ACL lookup enabled on them.
yt921x_acl_find() can no longer find the rejected rule, so it can't be
deleted to clean this up.
yt921x_acl_commit() writes several registers in sequence (ACT, BLK_CMD
modify, KEY/MASK, BLK_KEEP, ACLn_ENTRY, BLK_CMD commit), and any of those
writes can fail.
yt921x_acl_del() can end up in the same state:
if (refresh_en) {
ctrl = yt921x_acl_activated_ports(priv);
res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl);
if (res)
ret = res;
}
If this write fails after the last rule on a port took acl_cnt to 0, the
rule is still freed. The enable bit stays set, and a second delete
returns -ENOENT.
After either failure, YT921X_ACL_PORT stays wider than
yt921x_acl_activated_ports() until some later add or delete happens to
rewrite it. If that never happens, those ports keep the extra ACL lookup
latency that this patch is meant to remove.
These paths are also unchanged later in the series. "net: dsa:
motorcomm: Add TC prio support" doesn't touch them.
Would it make sense to rewrite YT921X_ACL_PORT from
yt921x_acl_activated_ports() on the add error path?
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261009165825.359259-1-mmyangfl%40gmail.com
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net-next v3 4/4] net: dsa: motorcomm: Add TC prio support
2026-10-09 16:58 ` [PATCH net-next v3 4/4] net: dsa: motorcomm: Add TC prio support David Yang
@ 2026-10-10 17:14 ` netdev-bot+sashiko
2026-10-10 17:59 ` David Yang
0 siblings, 1 reply; 8+ messages in thread
From: netdev-bot+sashiko @ 2026-10-10 17:14 UTC (permalink / raw)
To: mmyangfl
Cc: netdev, andrew, olteanv, davem, edumazet, kuba, pabeni,
linux-kernel
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 1 · Low: 0
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] net: dsa: motorcomm: strict TC priority modulo check breaks
auto-allocation
--
> diff --git a/drivers/net/dsa/motorcomm/tc.c b/drivers/net/dsa/motorcomm/tc.c
> --- a/drivers/net/dsa/motorcomm/tc.c
> +++ b/drivers/net/dsa/motorcomm/tc.c
> @@ -1042,6 +1045,36 @@ yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port,
> return -EOPNOTSUPP;
> }
>
> + /* Lower TC priorities take precedence, while larger orders win in
> + * hardware. Spread the 512 hardware orders over the TC priority range
> + * as evenly as possible, retaining the relative order:
> + *
> + * prio 65535 -> ORD 0 catchall filter
> + * prio 65408 -> ORD 1 highest mappable TC priority
> + * ...
> + * prio 49152 -> ORD 128 default value for the first tc flower rule
> + * ...
> + * prio 128 -> ORD 511
> + * prio 0 (invalid TC priority)
> + *
> + * The catchall filter always comes with priority 65535; pin it to ORD
> + * 0, the lowest hardware order, instead of rejecting it.
> + *
> + * Prefer explicit rejects over implicit behaviour changes: reject
> + * the priorities which do not map onto a hardware order instead of
> + * silently colliding with a neighbouring one.
> + */
> + if (prio == U16_MAX) {
> + ord = 0;
> + } else if (!(prio % scale)) {
> + ord = YT921X_ACL_ORD_NUM - prio / scale;
> + } else {
> + NL_SET_ERR_MSG_FMT_MOD(extack,
> + "Invalid priority %u, must be a multiple of %u or 65535",
> + prio, scale);
> + return -EOPNOTSUPP;
> + }
> +
> res = yt921x_acl_rule_ext_parse_flow_action(ruleext, cls, priv, port);
> if (res)
> return res;
[Severity: Medium]
This rejects every priority that is not a multiple of 128, but the
kernel's own auto-allocation does not produce such values.
When a filter is added without an explicit pref, tcf_auto_prio() in
net/sched/cls_api.c gives the first filter 49152. Each later filter
gets the previous priority minus one, so the second gets 49151, the
third 49150, and so on.
With this patch, the first plain "tc filter add ... flower" on a port
is offloaded at ORD 128. Every following one added without pref then
fails here with -EOPNOTSUPP:
- With skip_sw this is a hard failure for a very common usage
pattern.
- Without skip_sw the rule quietly stays software-only.
Before this patch the same sequence was offloaded, with every rule at
ORD 0. That makes this a behaviour regression for anyone who does not
hand-pick prefs in steps of 128.
Is a fixed linear prio-to-ORD mapping really what we want here? For
example, could yt921x_acl_rule_ext_parse_flow() assign hardware
orders from the relative priority of the rules currently installed?
Or could it at least handle the auto-allocated range below 49152
sensibly? I'd like to understand how you expect users to drive this
in practice.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261009165825.359259-1-mmyangfl%40gmail.com
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net-next v3 4/4] net: dsa: motorcomm: Add TC prio support
2026-10-10 17:14 ` netdev-bot+sashiko
@ 2026-10-10 17:59 ` David Yang
0 siblings, 0 replies; 8+ messages in thread
From: David Yang @ 2026-10-10 17:59 UTC (permalink / raw)
To: netdev-bot+sashiko
Cc: netdev, andrew, olteanv, davem, edumazet, kuba, pabeni,
linux-kernel
On Sun, Oct 11, 2026 at 1:14 AM <netdev-bot+sashiko@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue(s) to consider.
>
> Critical: 0 · High: 0 · Medium: 1 · Low: 0
>
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
>
> - [Medium] net: dsa: motorcomm: strict TC priority modulo check breaks
> auto-allocation
Already explained in the comment. Also we already reject some rules
with -EOPNOTSUPP, if returning -EOPNOTSUPP when skip_sw is not set is
a problem, that should be fixed by another series.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-10-10 18:00 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-09 16:58 [PATCH net-next v3 0/4] net: dsa: motorcomm: TC offload follow-ups David Yang
2026-10-09 16:58 ` [PATCH net-next v3 1/4] net: dsa: motorcomm: Split TC module David Yang
2026-10-09 16:58 ` [PATCH net-next v3 2/4] net: dsa: motorcomm: Use NSEC_PER_SEC David Yang
2026-10-09 16:58 ` [PATCH net-next v3 3/4] net: dsa: motorcomm: Enable ACL on demand David Yang
2026-10-10 17:14 ` netdev-bot+sashiko
2026-10-09 16:58 ` [PATCH net-next v3 4/4] net: dsa: motorcomm: Add TC prio support David Yang
2026-10-10 17:14 ` netdev-bot+sashiko
2026-10-10 17:59 ` David Yang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox