The KS8995 100Mbit switch can do proper DSA per-port tagging with the proper set-up. This adds the code to handle ingress and egress KS8995 tags. The tag is a modified 0x8100 ethertype tag where a bit in the last byte is set for each target port. Signed-off-by: Linus Walleij --- include/net/dsa.h | 2 + net/dsa/Kconfig | 6 +++ net/dsa/Makefile | 1 + net/dsa/tag_ks8995.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) diff --git a/include/net/dsa.h b/include/net/dsa.h index cced1a866757..b4c1ac14d051 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -57,6 +57,7 @@ struct tc_action; #define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29 #define DSA_TAG_PROTO_YT921X_VALUE 30 #define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31 +#define DSA_TAG_PROTO_KS8995_VALUE 32 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, @@ -91,6 +92,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_VSC73XX_8021Q = DSA_TAG_PROTO_VSC73XX_8021Q_VALUE, DSA_TAG_PROTO_YT921X = DSA_TAG_PROTO_YT921X_VALUE, DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE, + DSA_TAG_PROTO_KS8995 = DSA_TAG_PROTO_KS8995_VALUE, }; struct dsa_switch; diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index f86b30742122..c5272dc7af88 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -112,6 +112,12 @@ config NET_DSA_TAG_MXL_GSW1XX Say Y or M if you want to enable support for tagging frames for MaxLinear GSW1xx switches. +config NET_DSA_TAG_KS8995 + tristate "Tag driver for Micrel KS8995 switch" + help + Say Y if you want to enable support for tagging frames for the + Micrel KS8995 switch. + config NET_DSA_TAG_KSZ tristate "Tag driver for Microchip 8795/937x/9477/9893 families of switches" help diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 42d173f5a701..03eed7653a34 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o obj-$(CONFIG_NET_DSA_TAG_DSA_COMMON) += tag_dsa.o obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += tag_hellcreek.o +obj-$(CONFIG_NET_DSA_TAG_KS8995) += tag_ks8995.o obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o diff --git a/net/dsa/tag_ks8995.c b/net/dsa/tag_ks8995.c new file mode 100644 index 000000000000..a5adda4767a3 --- /dev/null +++ b/net/dsa/tag_ks8995.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Linus Walleij + */ +#include +#include +#include +#include + +#include "tag.h" + +/* The KS8995 Special Tag Packet ID (STPID) + * pushes its tag in a way similar to a VLAN tag + * ----------------------------------------------------------- + * | MAC DA | MAC SA | 2 bytes tag | 2 bytes TCI | EtherType | + * ----------------------------------------------------------- + * The tag is: 0x8100 |= BIT(port), ports 0,1,2,3 + */ + +#define KS8995_NAME "ks8995" + +#define KS8995_TAG_LEN 4 + +static struct sk_buff *ks8995_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct dsa_port *dp = dsa_user_to_port(dev); + u16 ks8995_tag; + __be16 *p; + u16 port; + u16 tci; + + /* Prepare the special KS8995 tags */ + port = dsa_xmit_port_mask(skb, dev); + /* The manual says to set this to the CPU port if no port is indicated */ + if (!port) + port = BIT(5); + + ks8995_tag = ETH_P_8021Q | port; + tci = port & VLAN_VID_MASK; + + /* Push in a tag between MAC and ethertype */ + netdev_dbg(dev, "egress packet tag: add tag %04x %04x to port %d\n", + ks8995_tag, tci, dp->index); + + skb_push(skb, KS8995_TAG_LEN); + dsa_alloc_etype_header(skb, KS8995_TAG_LEN); + + p = dsa_etype_header_pos_tx(skb); + p[0] = htons(ks8995_tag); + p[1] = htons(tci); + + return skb; +} + +static struct sk_buff *ks8995_rcv(struct sk_buff *skb, struct net_device *dev) +{ + unsigned int port; + __be16 *p; + u16 etype; + u16 tci; + + if (unlikely(!pskb_may_pull(skb, KS8995_TAG_LEN))) { + netdev_err(dev, "dropping packet, cannot pull\n"); + return NULL; + } + + p = dsa_etype_header_pos_rx(skb); + etype = ntohs(p[0]); + + if (etype == ETH_P_8021Q) { + /* That's just an ordinary VLAN tag, pass through */ + return skb; + } + + if ((etype & 0xFFF0U) != ETH_P_8021Q) { + /* Not custom, just pass through */ + netdev_dbg(dev, "non-KS8995 ethertype 0x%04x\n", etype); + return skb; + } + + port = ilog2(etype & 0xF); + tci = ntohs(p[1]); + netdev_dbg(dev, "ingress packet tag: %04x %04x, port %d\n", + etype, tci, port); + + skb->dev = dsa_conduit_find_user(dev, 0, port); + if (!skb->dev) { + netdev_err(dev, "could not find user for port %d\n", port); + return NULL; + } + + /* Remove KS8995 tag and recalculate checksum */ + skb_pull_rcsum(skb, KS8995_TAG_LEN); + + dsa_strip_etype_header(skb, KS8995_TAG_LEN); + + dsa_default_offload_fwd_mark(skb); + + return skb; +} + +static const struct dsa_device_ops ks8995_netdev_ops = { + .name = KS8995_NAME, + .proto = DSA_TAG_PROTO_KS8995, + .xmit = ks8995_xmit, + .rcv = ks8995_rcv, + .needed_headroom = KS8995_TAG_LEN, +}; + +MODULE_DESCRIPTION("DSA tag driver for Micrel KS8995 family of switches"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KS8995, KS8995_NAME); + +module_dsa_tag_driver(ks8995_netdev_ops); -- 2.52.0