Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next 0/2] Add DSA tag handling for KS8995
@ 2026-01-07 12:57 Linus Walleij
  2026-01-07 12:57 ` [PATCH net-next 1/2] net: dsa: tag_ks8995: Add the KS8995 tag handling Linus Walleij
  2026-01-07 12:57 ` [PATCH net-next 2/2] net: dsa: ks8995: Add DSA tagging to KS8995 Linus Walleij
  0 siblings, 2 replies; 6+ messages in thread
From: Linus Walleij @ 2026-01-07 12:57 UTC (permalink / raw)
  To: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman
  Cc: netdev, Linus Walleij

The KS8995 switch supports DSA tagging.

The sibling devices KSZ8864 and KSZ8795 does not support
special DSA tags, so the patch series makes sure we only
enable these tags specifically on the KS8995.

Tested on the Actiontec MI424WR rev D router.

Signed-off-by: Linus Walleij <linusw@kernel.org>
---
Linus Walleij (2):
      net: dsa: tag_ks8995: Add the KS8995 tag handling
      net: dsa: ks8995: Add DSA tagging to KS8995

 MAINTAINERS              |   8 ++++
 drivers/net/dsa/Kconfig  |   1 +
 drivers/net/dsa/ks8995.c |  87 +++++++++++++++++++++++++++++++++++-
 include/net/dsa.h        |   2 +
 net/dsa/Kconfig          |   6 +++
 net/dsa/Makefile         |   1 +
 net/dsa/tag_ks8995.c     | 114 +++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 217 insertions(+), 2 deletions(-)
---
base-commit: d48eae8baa8db1cff42c69aaedd87af43f4ca36b
change-id: 20251215-ks8995-dsa-tagging-e1898c3cc4ff

Best regards,
-- 
Linus Walleij <linusw@kernel.org>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net-next 1/2] net: dsa: tag_ks8995: Add the KS8995 tag handling
  2026-01-07 12:57 [PATCH net-next 0/2] Add DSA tag handling for KS8995 Linus Walleij
@ 2026-01-07 12:57 ` Linus Walleij
  2026-01-07 16:01   ` Andrew Lunn
  2026-01-12 21:07   ` Vladimir Oltean
  2026-01-07 12:57 ` [PATCH net-next 2/2] net: dsa: ks8995: Add DSA tagging to KS8995 Linus Walleij
  1 sibling, 2 replies; 6+ messages in thread
From: Linus Walleij @ 2026-01-07 12:57 UTC (permalink / raw)
  To: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman
  Cc: netdev, Linus Walleij

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 <linusw@kernel.org>
---
 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 <linusw@kernel.org>
+ */
+#include <linux/etherdevice.h>
+#include <linux/log2.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#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


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH net-next 2/2] net: dsa: ks8995: Add DSA tagging to KS8995
  2026-01-07 12:57 [PATCH net-next 0/2] Add DSA tag handling for KS8995 Linus Walleij
  2026-01-07 12:57 ` [PATCH net-next 1/2] net: dsa: tag_ks8995: Add the KS8995 tag handling Linus Walleij
@ 2026-01-07 12:57 ` Linus Walleij
  2026-01-12 21:31   ` Vladimir Oltean
  1 sibling, 1 reply; 6+ messages in thread
From: Linus Walleij @ 2026-01-07 12:57 UTC (permalink / raw)
  To: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman
  Cc: netdev, Linus Walleij

This makes the KS8995 DSA switch use the special tags to direct
traffic to a specific port and identify traffic coming in on a
specific port.

These tags are not available on the sibling devices KSZ8895
or KSZ8795.

To do this the switch require us to enable "special tags" in a
register, then enable tag insertion on the CPU port, meaning the
CPU port will deliver packets with a special tag indicating which
port the traffic is coming from, and then we need to enable
tag removal on all outgoing (LAN) ports, this means that the
special egress tag is stripped off by the switch before exiting
the PHY-backed ports.

Add a MAINTAINERS entry while we're at it.

Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 MAINTAINERS              |  8 +++++
 drivers/net/dsa/Kconfig  |  1 +
 drivers/net/dsa/ks8995.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 94 insertions(+), 2 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 5b11839cba9d..310accf05153 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16942,6 +16942,14 @@ F:	drivers/bus/mhi/
 F:	drivers/pci/endpoint/functions/pci-epf-mhi.c
 F:	include/linux/mhi.h
 
+MICREL KS8995 DSA SWITCH
+M:	Linus Walleij <linusw@kernel.org>
+S:	Supported
+S:	Maintained
+F:	Documentation/devicetree/bindings/net/dsa/micrel,ks8995.yaml
+F:	drivers/net/dsa/ks8995.c
+F:	net/dsa/tag_ks8995.c
+
 MICROBLAZE ARCHITECTURE
 M:	Michal Simek <monstr@monstr.eu>
 S:	Supported
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 7eb301fd987d..8925308cc7d7 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -97,6 +97,7 @@ config NET_DSA_KS8995
 	tristate "Micrel KS8995 family 5-ports 10/100 Ethernet switches"
 	depends on SPI
 	select NET_DSA_TAG_NONE
+	select NET_DSA_TAG_KS8995
 	help
 	  This driver supports the Micrel KS8995 family of 10/100 Mbit ethernet
 	  switches, managed over SPI.
diff --git a/drivers/net/dsa/ks8995.c b/drivers/net/dsa/ks8995.c
index 77d8b842693c..00c8c7853c61 100644
--- a/drivers/net/dsa/ks8995.c
+++ b/drivers/net/dsa/ks8995.c
@@ -3,7 +3,7 @@
  * SPI driver for Micrel/Kendin KS8995M and KSZ8864RMN ethernet switches
  *
  * Copyright (C) 2008 Gabor Juhos <juhosg at openwrt.org>
- * Copyright (C) 2025 Linus Walleij <linus.walleij@linaro.org>
+ * Copyright (C) 2025-2026 Linus Walleij <linusw@kernel.org>
  *
  * This file was based on: drivers/spi/at25.c
  *     Copyright (C) 2006 David Brownell
@@ -338,6 +338,12 @@ static int ks8995_reset(struct ks8995_switch *ks)
 	return ks8995_start(ks);
 }
 
+static bool ks8995_is_ks8995(struct ks8995_switch *ks)
+{
+	return ((ks->chip->family_id == FAMILY_KS8995) &&
+		(ks->chip->chip_id == KS8995_CHIP_ID));
+}
+
 /* ks8995_get_revision - get chip revision
  * @ks: pointer to switch instance
  *
@@ -532,12 +538,89 @@ dsa_tag_protocol ks8995_get_tag_protocol(struct dsa_switch *ds,
 					 int port,
 					 enum dsa_tag_protocol mp)
 {
-	/* This switch actually uses the 6 byte KS8995 protocol */
+	struct ks8995_switch *ks = ds->priv;
+
+	if (ks8995_is_ks8995(ks))
+		/* This switch uses the KS8995 protocol */
+		return DSA_TAG_PROTO_KS8995;
+
 	return DSA_TAG_PROTO_NONE;
 }
 
+/* Only the KS8995 supports special (DSA) tagging with special bits
+ * set for the ingress and egress ports. The "special tag" register bit
+ * in the other versions is used for clock edge setting so make sure
+ * to only enable this on the KS8995.
+ */
+static int ks8995_special_tags_setup(struct ks8995_switch *ks)
+{
+	int ret;
+	u8 val;
+	int i;
+
+	ret = ks8995_read_reg(ks, KS8995_REG_GC9, &val);
+	if (ret) {
+		dev_err(ks->dev, "failed to read KS8995_REG_GC9\n");
+		return ret;
+	}
+
+	/* Enable the "special tag" (the DSA port tagging) */
+	val |= KS8995_GC9_SPECIAL;
+
+	ret = ks8995_write_reg(ks, KS8995_REG_GC9, val);
+	if (ret)
+		dev_err(ks->dev, "failed to set KS8995_REG_GC11\n");
+
+	ret = ks8995_read_reg(ks, KS8995_REG_PC(KS8995_CPU_PORT, KS8995_REG_PC0), &val);
+	if (ret) {
+		dev_err(ks->dev, "failed to read KS8995_REG_PC0 on CPU port\n");
+		return ret;
+	}
+
+	/* Enable tag INSERTION on the CPU port, this will add the special KS8995 DSA tag
+	 * to packets entering from the chip, indicating the source port.
+	 */
+	val &= ~KS8995_PC0_TAG_REM;
+	val |= KS8995_PC0_TAG_INS;
+
+	ret = ks8995_write_reg(ks, KS8995_REG_PC(KS8995_CPU_PORT, KS8995_REG_PC0), val);
+	if (ret) {
+		dev_err(ks->dev, "failed to write KS8995_REG_PC0 on CPU port\n");
+		return ret;
+	}
+
+	/* Enable tag REMOVAL on all the LAN-facing ports: this will strip the special
+	 * DSA tag that we add during transmission of the egress packets before they exit
+	 * the router chip.
+	 */
+	for (i = 0; i < KS8995_CPU_PORT; i++) {
+		ret = ks8995_read_reg(ks, KS8995_REG_PC(i, KS8995_REG_PC0), &val);
+		if (ret) {
+			dev_err(ks->dev, "failed to read KS8995_REG_PC0 on port %d\n", i);
+			return ret;
+		}
+
+		val |= KS8995_PC0_TAG_REM;
+		val &= ~KS8995_PC0_TAG_INS;
+
+		ret = ks8995_write_reg(ks, KS8995_REG_PC(i, KS8995_REG_PC0), val);
+		if (ret) {
+			dev_err(ks->dev, "failed to write KS8995_REG_PC0 on port %d\n", i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 static int ks8995_setup(struct dsa_switch *ds)
 {
+	struct ks8995_switch *ks = ds->priv;
+
+	if (ks8995_is_ks8995(ks))
+		/* This switch uses the KS8995 protocol */
+		return ks8995_special_tags_setup(ks);
+
 	return 0;
 }
 

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next 1/2] net: dsa: tag_ks8995: Add the KS8995 tag handling
  2026-01-07 12:57 ` [PATCH net-next 1/2] net: dsa: tag_ks8995: Add the KS8995 tag handling Linus Walleij
@ 2026-01-07 16:01   ` Andrew Lunn
  2026-01-12 21:07   ` Vladimir Oltean
  1 sibling, 0 replies; 6+ messages in thread
From: Andrew Lunn @ 2026-01-07 16:01 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, netdev

> +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;

Should that actually happen? Normally all frames use the tag, and
anything without a tag we drop because we don't expect it.

> +	}
> +
> +	if ((etype & 0xFFF0U) != ETH_P_8021Q) {
> +		/* Not custom, just pass through */
> +		netdev_dbg(dev, "non-KS8995 ethertype 0x%04x\n", etype);
> +		return skb;
> +	}

Same here.

     Andrew

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next 1/2] net: dsa: tag_ks8995: Add the KS8995 tag handling
  2026-01-07 12:57 ` [PATCH net-next 1/2] net: dsa: tag_ks8995: Add the KS8995 tag handling Linus Walleij
  2026-01-07 16:01   ` Andrew Lunn
@ 2026-01-12 21:07   ` Vladimir Oltean
  1 sibling, 0 replies; 6+ messages in thread
From: Vladimir Oltean @ 2026-01-12 21:07 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, netdev

Hi Linus,

It's nice to see old hardware being consolidated into the DSA framework.
Thanks for that effort.

On Wed, Jan 07, 2026 at 01:57:14PM +0100, Linus Walleij wrote:
> 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 <linusw@kernel.org>
> ---
>  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 <linusw@kernel.org>

You can update to 2026.

> + */
> +#include <linux/etherdevice.h>
> +#include <linux/log2.h>
> +#include <linux/list.h>
> +#include <linux/slab.h>
> +
> +#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;

I think this is incorrect on multiple counts.

The first red flag is that the port mask is packed twice into the tag,
once into the TPID and second into the TCI. I opened the reference
manual and my reading is that the TCI portion is unnecessary/incorrect;
it remains processed by the switch as a TCI (aka VLAN ID + PCP), with no
port semantics overlaid on top.

Regarding the sentence "The manual says to set this to the CPU port if
no port is indicated" - I did not find that. I just found these
sentences instead:
- No change to TCI if not null VID
- Replace VID with ingress (port 5) port VID if null VID

which say a different story.

If VID==0, the packet will be processed in the CPU port's PVID,
otherwise the VID is preserved during the forwarding process. In both
cases, the VID may be stripped on egress, depending on VLAN table
settings, or not.

Practically, this means you have a combined DSA+VLAN tag.
On xmit, you'd need logic like this (not compiled, sorry):

#define KS8995M_STPID_STD	GENMASK(15, 4)
#define KS8995M_STPID_PORTMASK	GENMASK(3, 0)
#define KS8995M_STPID(portmask)	htons(ETH_P_8021Q | FIELD_PREP(KS8995M_STPID_PORTMASK, portmask))

	struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
	bool have_hwaccel_tag = false;
	int tci = 0, portmask;

	portmask = dsa_xmit_port_mask(skb, dev);

	if (skb_vlan_tag_present(skb) && skb->vlan_proto == htons(ETH_P_8021Q)) {
		tci = skb_vlan_tag_get(skb);
		__vlan_hwaccel_clear_tag(skb);
		have_hwaccel_tag = true;
	}

	if (have_hwaccel_tag || hdr->h_vlan_proto != htons(ETH_P_8021Q)) {
		skb = vlan_insert_tag(skb, KS8995M_STPID(portmask), tci);
		if (!skb)
			return NULL;
	} else {
		/* VLAN tag already exists in skb head, modify it in place */
		hdr->h_vlan_proto = KS8995M_STPID(portmask);
		hdr->h_vlan_TCI = htons(tci);
	}

and on rcv, something like this:

	/* We are expecting all received packets to have a mangled VLAN
	 * TPID, so drop anything else. Because of the non-standard TPID,
	 * don't even bother looking for a tag in the hwaccel area
	 */
	if (FIELD_GET(KS8995M_STPID_STD, ntohs(skb->protocol)) != ETH_P_8021Q)
		return NULL;

	/* Move the custom DSA+VLAN tag into the hwaccel area and strip
	 * it from the skb head
	 */
	skb = skb_vlan_untag(skb);
	if (!skb)
		return NULL;

	portmask = FIELD_GET(KS8995M_STPID_PORTMASK, ntohs(skb->vlan_proto));
	skb->dev = dsa_conduit_find_user(dev, 0, ilog2(portmask));
	if (!skb->dev)
		return NULL;

	/* Preserve the VLAN tag if it contains a non-zero VID or PCP,
	 * and restore its TPID to the standard value
	 */
	skb->vlan_proto = htons(ETH_P_8021Q);
	if (!skb->vlan_tci)
		__vlan_hwaccel_clear_tag(skb);

	dsa_default_offload_fwd_mark(skb);

Lastly, dsa_xmit_port_mask() never returns 0, so the extra code is dead.

The reason why you didn't notice anything out of place when transmitting
packets with VID=BIT(port) is, I believe, because you're configuring the
user ports as egress-untagged for all VLANs. Contrary to more advanced
switches where the untagged port mask is per VLAN, here it is per port
(the same "Tag insertion" and "Tag removal" bits from the Port Control 0
registers that you're also enabling on the CPU port). So any blunder in
the tagging protocol is being wiped by the switch.

> +
> +	/* 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 */

I hope you don't have a use case for such packets passing 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,

VLAN_HLEN

> +};
> +
> +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
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next 2/2] net: dsa: ks8995: Add DSA tagging to KS8995
  2026-01-07 12:57 ` [PATCH net-next 2/2] net: dsa: ks8995: Add DSA tagging to KS8995 Linus Walleij
@ 2026-01-12 21:31   ` Vladimir Oltean
  0 siblings, 0 replies; 6+ messages in thread
From: Vladimir Oltean @ 2026-01-12 21:31 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, netdev

On Wed, Jan 07, 2026 at 01:57:15PM +0100, Linus Walleij wrote:
> This makes the KS8995 DSA switch use the special tags to direct
> traffic to a specific port and identify traffic coming in on a
> specific port.
> 
> These tags are not available on the sibling devices KSZ8895
> or KSZ8795.
> 
> To do this the switch require us to enable "special tags" in a
> register, then enable tag insertion on the CPU port, meaning the
> CPU port will deliver packets with a special tag indicating which
> port the traffic is coming from, and then we need to enable
> tag removal on all outgoing (LAN) ports, this means that the
> special egress tag is stripped off by the switch before exiting
> the PHY-backed ports.
> 
> Add a MAINTAINERS entry while we're at it.
> 
> Signed-off-by: Linus Walleij <linusw@kernel.org>
> ---
>  MAINTAINERS              |  8 +++++
>  drivers/net/dsa/Kconfig  |  1 +
>  drivers/net/dsa/ks8995.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++--
>  3 files changed, 94 insertions(+), 2 deletions(-)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5b11839cba9d..310accf05153 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -16942,6 +16942,14 @@ F:	drivers/bus/mhi/
>  F:	drivers/pci/endpoint/functions/pci-epf-mhi.c
>  F:	include/linux/mhi.h
>  
> +MICREL KS8995 DSA SWITCH
> +M:	Linus Walleij <linusw@kernel.org>
> +S:	Supported
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/net/dsa/micrel,ks8995.yaml
> +F:	drivers/net/dsa/ks8995.c
> +F:	net/dsa/tag_ks8995.c
> +
>  MICROBLAZE ARCHITECTURE
>  M:	Michal Simek <monstr@monstr.eu>
>  S:	Supported
> diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
> index 7eb301fd987d..8925308cc7d7 100644
> --- a/drivers/net/dsa/Kconfig
> +++ b/drivers/net/dsa/Kconfig
> @@ -97,6 +97,7 @@ config NET_DSA_KS8995
>  	tristate "Micrel KS8995 family 5-ports 10/100 Ethernet switches"
>  	depends on SPI
>  	select NET_DSA_TAG_NONE
> +	select NET_DSA_TAG_KS8995
>  	help
>  	  This driver supports the Micrel KS8995 family of 10/100 Mbit ethernet
>  	  switches, managed over SPI.
> diff --git a/drivers/net/dsa/ks8995.c b/drivers/net/dsa/ks8995.c
> index 77d8b842693c..00c8c7853c61 100644
> --- a/drivers/net/dsa/ks8995.c
> +++ b/drivers/net/dsa/ks8995.c
> @@ -3,7 +3,7 @@
>   * SPI driver for Micrel/Kendin KS8995M and KSZ8864RMN ethernet switches
>   *
>   * Copyright (C) 2008 Gabor Juhos <juhosg at openwrt.org>
> - * Copyright (C) 2025 Linus Walleij <linus.walleij@linaro.org>
> + * Copyright (C) 2025-2026 Linus Walleij <linusw@kernel.org>
>   *
>   * This file was based on: drivers/spi/at25.c
>   *     Copyright (C) 2006 David Brownell
> @@ -338,6 +338,12 @@ static int ks8995_reset(struct ks8995_switch *ks)
>  	return ks8995_start(ks);
>  }
>  
> +static bool ks8995_is_ks8995(struct ks8995_switch *ks)
> +{
> +	return ((ks->chip->family_id == FAMILY_KS8995) &&
> +		(ks->chip->chip_id == KS8995_CHIP_ID));
> +}
> +
>  /* ks8995_get_revision - get chip revision
>   * @ks: pointer to switch instance
>   *
> @@ -532,12 +538,89 @@ dsa_tag_protocol ks8995_get_tag_protocol(struct dsa_switch *ds,
>  					 int port,
>  					 enum dsa_tag_protocol mp)
>  {
> -	/* This switch actually uses the 6 byte KS8995 protocol */
> +	struct ks8995_switch *ks = ds->priv;
> +
> +	if (ks8995_is_ks8995(ks))
> +		/* This switch uses the KS8995 protocol */
> +		return DSA_TAG_PROTO_KS8995;
> +
>  	return DSA_TAG_PROTO_NONE;
>  }
>  
> +/* Only the KS8995 supports special (DSA) tagging with special bits
> + * set for the ingress and egress ports. The "special tag" register bit
> + * in the other versions is used for clock edge setting so make sure
> + * to only enable this on the KS8995.

Can you enumerate the part numbers for which this special tag doesn't
exist? I see there is some overlap between switches supported by the
ks8995 driver and the ksz_common driver, correct? At least KSZ8864 and
KSZ8795 can be seen in the ksz_common driver as well, and there they use
the "ksz8795" tail tagging protocol. Correct? Are these the same part
numbers?

> + */
> +static int ks8995_special_tags_setup(struct ks8995_switch *ks)
> +{
> +	int ret;
> +	u8 val;
> +	int i;
> +
> +	ret = ks8995_read_reg(ks, KS8995_REG_GC9, &val);
> +	if (ret) {
> +		dev_err(ks->dev, "failed to read KS8995_REG_GC9\n");
> +		return ret;
> +	}
> +
> +	/* Enable the "special tag" (the DSA port tagging) */
> +	val |= KS8995_GC9_SPECIAL;
> +
> +	ret = ks8995_write_reg(ks, KS8995_REG_GC9, val);
> +	if (ret)
> +		dev_err(ks->dev, "failed to set KS8995_REG_GC11\n");

Does it make sense to introduce ks8995_rmw_reg()?
Also, I believe that this register write error should be fatal.

> +
> +	ret = ks8995_read_reg(ks, KS8995_REG_PC(KS8995_CPU_PORT, KS8995_REG_PC0), &val);
> +	if (ret) {
> +		dev_err(ks->dev, "failed to read KS8995_REG_PC0 on CPU port\n");
> +		return ret;
> +	}
> +
> +	/* Enable tag INSERTION on the CPU port, this will add the special KS8995 DSA tag
> +	 * to packets entering from the chip, indicating the source port.
> +	 */
> +	val &= ~KS8995_PC0_TAG_REM;
> +	val |= KS8995_PC0_TAG_INS;
> +
> +	ret = ks8995_write_reg(ks, KS8995_REG_PC(KS8995_CPU_PORT, KS8995_REG_PC0), val);
> +	if (ret) {
> +		dev_err(ks->dev, "failed to write KS8995_REG_PC0 on CPU port\n");
> +		return ret;
> +	}
> +
> +	/* Enable tag REMOVAL on all the LAN-facing ports: this will strip the special
> +	 * DSA tag that we add during transmission of the egress packets before they exit
> +	 * the router chip.
> +	 */

Ok, but I disagree with the explanation. The special tag is a VLAN tag,
and is treated as such. These settings actually make the user ports
egress-untagged for all VLANs, and the CPU port egress-tagged for all
VLANs. Right?

This is fine for now, because there is no bridge offloading, but the code
positioning will have to be revisited by then (the driver will have to
count its egress-tagged and egress-untagged VLANs on each port, and
reject combinations of tagged+untagged, similar to ocelot_vlan_prepare()).

> +	for (i = 0; i < KS8995_CPU_PORT; i++) {
> +		ret = ks8995_read_reg(ks, KS8995_REG_PC(i, KS8995_REG_PC0), &val);
> +		if (ret) {
> +			dev_err(ks->dev, "failed to read KS8995_REG_PC0 on port %d\n", i);
> +			return ret;
> +		}
> +
> +		val |= KS8995_PC0_TAG_REM;
> +		val &= ~KS8995_PC0_TAG_INS;
> +
> +		ret = ks8995_write_reg(ks, KS8995_REG_PC(i, KS8995_REG_PC0), val);
> +		if (ret) {
> +			dev_err(ks->dev, "failed to write KS8995_REG_PC0 on port %d\n", i);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  static int ks8995_setup(struct dsa_switch *ds)
>  {
> +	struct ks8995_switch *ks = ds->priv;
> +
> +	if (ks8995_is_ks8995(ks))
> +		/* This switch uses the KS8995 protocol */
> +		return ks8995_special_tags_setup(ks);
> +
>  	return 0;
>  }
>  
> 
> -- 
> 2.52.0
> 


Some other things I didn't get the chance to comment on, when this
driver was first moved to DSA in August (sorry):
- The ks8995_driver needs a shutdown method that calls
  dsa_switch_shutdown(). This is non-optional, see how other drivers
  call things.
- I have no idea why the driver implements
  ks8995_port_pre_bridge_flags() and ks8995_port_bridge_flags() when it
  doesn't implement port_bridge_join(). This is dead code.
- I see there is no effort being made to implement user port isolation
  via Port Control 1 (KS8995_REG_PC1) bits 4-0 (Port VLAN membership).
  When the tagging protocol was DSA_TAG_PROTO_NONE, let's say this was
  more or less tolerable as a wacky swconfig-style setup, but with a
  tagging protocol implemented, the user ports absolutely have to be
  isolated between each other, and if you want forwarding, you use the
  Linux bridge for that.
- Unbridged user ports are supposed to have address learning disabled.
  This is a bridge-specific function. Having it in ks8995_port_bridge_flags()
  is fine, but you also have to disable it at probe time.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-01-12 21:31 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-07 12:57 [PATCH net-next 0/2] Add DSA tag handling for KS8995 Linus Walleij
2026-01-07 12:57 ` [PATCH net-next 1/2] net: dsa: tag_ks8995: Add the KS8995 tag handling Linus Walleij
2026-01-07 16:01   ` Andrew Lunn
2026-01-12 21:07   ` Vladimir Oltean
2026-01-07 12:57 ` [PATCH net-next 2/2] net: dsa: ks8995: Add DSA tagging to KS8995 Linus Walleij
2026-01-12 21:31   ` Vladimir Oltean

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox