devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next v6 0/7] Hirschmann Hellcreek DSA driver
@ 2020-10-04 11:29 Kurt Kanzenbach
  2020-10-04 11:29 ` [PATCH net-next v6 1/7] net: dsa: Add tag handling for Hirschmann Hellcreek switches Kurt Kanzenbach
                   ` (6 more replies)
  0 siblings, 7 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-04 11:29 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Kurt Kanzenbach

Hi,

this series adds a DSA driver for the Hirschmann Hellcreek TSN switch
IP. Characteristics of that IP:

 * Full duplex Ethernet interface at 100/1000 Mbps on three ports
 * IEEE 802.1Q-compliant Ethernet Switch
 * IEEE 802.1Qbv Time-Aware scheduling support
 * IEEE 1588 and IEEE 802.1AS support

That IP is used e.g. in

 https://www.arrow.com/en/campaigns/arrow-kairos

Due to the hardware setup the switch driver is implemented using DSA. A special
tagging protocol is leveraged. Furthermore, this driver supports PTP and
hardware timestamping.

This version implements the DSA configure_vlan_while_not_filtering behavior by
caching any VLAN requests when vlan_filtering is not set and restoring it as
soon as it is. Due to the lack of a port forwarding matrix in hardware, VLANs
are used to reflect the initial DSA/switchdev port separation. Patch two is the
only one which changed and has to be re-reviewed. Thanks!

This work is part of the AccessTSN project: https://www.accesstsn.com/

The previous versions can be found here:

 * https://lkml.kernel.org/netdev/20200618064029.32168-1-kurt@linutronix.de/
 * https://lkml.kernel.org/netdev/20200710113611.3398-1-kurt@linutronix.de/
 * https://lkml.kernel.org/netdev/20200723081714.16005-1-kurt@linutronix.de/
 * https://lkml.kernel.org/netdev/20200820081118.10105-1-kurt@linutronix.de/
 * https://lkml.kernel.org/netdev/20200901125014.17801-1-kurt@linutronix.de/
 * https://lkml.kernel.org/netdev/20200904062739.3540-1-kurt@linutronix.de/

Changes since v5:

 * Implement configure_vlan_while_not_filtering behavior (Vladimir Oltean)
 * Minor cleanups

Changes since v4:

 * Fix W=1 compiler warnings (kernel test robot)
 * Add tags

Changes since v3:

 * Drop TAPRIO support (David Miller)
   => Switch to mutexes due to the lack of hrtimers
 * Use more specific compatible strings and add platform data (Andrew Lunn)
 * Fix Kconfig ordering (Andrew Lunn)

Changes since v2:

 * Make it compile by getting all requirements merged first (Jakub Kicinski, David Miller)
 * Use "tsn" for TSN register set (Rob Herring)
 * Fix DT binding issues (Rob Herring)

Changes since v1:

 * Code simplifications (Florian Fainelli, Vladimir Oltean)
 * Fix issues with hellcreek.yaml bindings (Florian Fainelli)
 * Clear reserved field in ptp v2 event messages (Richard Cochran)
 * Make use of generic ptp parsing function (Richard Cochran, Vladimir Oltean)
 * Fix Kconfig (Florian Fainelli)
 * Add tags (Florian Fainelli, Rob Herring, Richard Cochran) 

Changes since RFC ordered by reviewers:

 * Andrew Lunn
   * Use dev_dbg for debug messages
   * Get rid of __ function names where possible
   * Use reverse xmas tree variable ordering
   * Remove redundant/useless checks
   * Improve comments e.g. for PTP
   * Fix Kconfig ordering
   * Make LED handling more generic and provide info via DT
   * Setup advertisement of PHYs according to hardware
   * Drop debugfs patch
 * Jakub Kicinski
   * Fix compiler warnings
 * Florian Fainelli
   * Switch to YAML DT bindings
 * Richard Cochran
   * Fix typo
   * Add missing NULL checks

Kamil Alkhouri (2):
  net: dsa: hellcreek: Add PTP clock support
  net: dsa: hellcreek: Add support for hardware timestamping

Kurt Kanzenbach (5):
  net: dsa: Add tag handling for Hirschmann Hellcreek switches
  net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  net: dsa: hellcreek: Add PTP status LEDs
  dt-bindings: Add vendor prefix for Hirschmann
  dt-bindings: net: dsa: Add documentation for Hellcreek switches

 .../bindings/net/dsa/hellcreek.yaml           |  127 ++
 .../devicetree/bindings/vendor-prefixes.yaml  |    2 +
 drivers/net/dsa/Kconfig                       |    2 +
 drivers/net/dsa/Makefile                      |    1 +
 drivers/net/dsa/hirschmann/Kconfig            |    9 +
 drivers/net/dsa/hirschmann/Makefile           |    5 +
 drivers/net/dsa/hirschmann/hellcreek.c        | 1371 +++++++++++++++++
 drivers/net/dsa/hirschmann/hellcreek.h        |  292 ++++
 .../net/dsa/hirschmann/hellcreek_hwtstamp.c   |  479 ++++++
 .../net/dsa/hirschmann/hellcreek_hwtstamp.h   |   58 +
 drivers/net/dsa/hirschmann/hellcreek_ptp.c    |  452 ++++++
 drivers/net/dsa/hirschmann/hellcreek_ptp.h    |   76 +
 .../platform_data/hirschmann-hellcreek.h      |   23 +
 include/net/dsa.h                             |    2 +
 net/dsa/Kconfig                               |    6 +
 net/dsa/Makefile                              |    1 +
 net/dsa/tag_hellcreek.c                       |  101 ++
 17 files changed, 3007 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/dsa/hellcreek.yaml
 create mode 100644 drivers/net/dsa/hirschmann/Kconfig
 create mode 100644 drivers/net/dsa/hirschmann/Makefile
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek.c
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek.h
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek_ptp.c
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek_ptp.h
 create mode 100644 include/linux/platform_data/hirschmann-hellcreek.h
 create mode 100644 net/dsa/tag_hellcreek.c

-- 
2.20.1


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

* [PATCH net-next v6 1/7] net: dsa: Add tag handling for Hirschmann Hellcreek switches
  2020-10-04 11:29 [PATCH net-next v6 0/7] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
@ 2020-10-04 11:29 ` Kurt Kanzenbach
  2020-10-04 11:56   ` Vladimir Oltean
  2020-10-04 11:29 ` [PATCH net-next v6 2/7] net: dsa: Add DSA driver " Kurt Kanzenbach
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-04 11:29 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Kurt Kanzenbach

The Hirschmann Hellcreek TSN switches have a special tagging protocol for frames
exchanged between the CPU port and the master interface. The format is a one
byte trailer indicating the destination or origin port.

It's quite similar to the Micrel KSZ tagging. That's why the implementation is
based on that code.

Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
 include/net/dsa.h       |   2 +
 net/dsa/Kconfig         |   6 +++
 net/dsa/Makefile        |   1 +
 net/dsa/tag_hellcreek.c | 101 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 110 insertions(+)
 create mode 100644 net/dsa/tag_hellcreek.c

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 8b0696e08cac..ee24476a1a4c 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -45,6 +45,7 @@ struct phylink_link_state;
 #define DSA_TAG_PROTO_OCELOT_VALUE		15
 #define DSA_TAG_PROTO_AR9331_VALUE		16
 #define DSA_TAG_PROTO_RTL4_A_VALUE		17
+#define DSA_TAG_PROTO_HELLCREEK_VALUE		18
 
 enum dsa_tag_protocol {
 	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
@@ -65,6 +66,7 @@ enum dsa_tag_protocol {
 	DSA_TAG_PROTO_OCELOT		= DSA_TAG_PROTO_OCELOT_VALUE,
 	DSA_TAG_PROTO_AR9331		= DSA_TAG_PROTO_AR9331_VALUE,
 	DSA_TAG_PROTO_RTL4_A		= DSA_TAG_PROTO_RTL4_A_VALUE,
+	DSA_TAG_PROTO_HELLCREEK		= DSA_TAG_PROTO_HELLCREEK_VALUE,
 };
 
 struct packet_type;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 1f9b9b11008c..d975614f7dd6 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -56,6 +56,12 @@ config NET_DSA_TAG_BRCM_PREPEND
 	  Broadcom switches which places the tag before the Ethernet header
 	  (prepended).
 
+config NET_DSA_TAG_HELLCREEK
+	tristate "Tag driver for Hirschmann Hellcreek TSN switches"
+	help
+	  Say Y or M if you want to enable support for tagging frames
+	  for the Hirschmann Hellcreek TSN switches.
+
 config NET_DSA_TAG_GSWIP
 	tristate "Tag driver for Lantiq / Intel GSWIP switches"
 	help
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index 4f47b2025ff5..e25d5457964a 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o
 obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o
 obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
 obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o
+obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += tag_hellcreek.o
 obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
 obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o
 obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
diff --git a/net/dsa/tag_hellcreek.c b/net/dsa/tag_hellcreek.c
new file mode 100644
index 000000000000..0895eda94bb5
--- /dev/null
+++ b/net/dsa/tag_hellcreek.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * net/dsa/tag_hellcreek.c - Hirschmann Hellcreek switch tag format handling
+ *
+ * Copyright (C) 2019,2020 Linutronix GmbH
+ * Author Kurt Kanzenbach <kurt@linutronix.de>
+ *
+ * Based on tag_ksz.c.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <net/dsa.h>
+
+#include "dsa_priv.h"
+
+#define HELLCREEK_TAG_LEN	1
+
+static struct sk_buff *hellcreek_xmit(struct sk_buff *skb,
+				      struct net_device *dev)
+{
+	struct dsa_port *dp = dsa_slave_to_port(dev);
+	struct sk_buff *nskb;
+	int padlen;
+	u8 *tag;
+
+	padlen = (skb->len >= ETH_ZLEN) ? 0 : ETH_ZLEN - skb->len;
+
+	if (skb_tailroom(skb) >= padlen + HELLCREEK_TAG_LEN) {
+		/* Let dsa_slave_xmit() free skb */
+		if (__skb_put_padto(skb, skb->len + padlen, false))
+			return NULL;
+
+		nskb = skb;
+	} else {
+		nskb = alloc_skb(NET_IP_ALIGN + skb->len +
+				 padlen + HELLCREEK_TAG_LEN, GFP_ATOMIC);
+		if (!nskb)
+			return NULL;
+		skb_reserve(nskb, NET_IP_ALIGN);
+
+		skb_reset_mac_header(nskb);
+		skb_set_network_header(nskb,
+				       skb_network_header(skb) - skb->head);
+		skb_set_transport_header(nskb,
+					 skb_transport_header(skb) - skb->head);
+		skb_copy_and_csum_dev(skb, skb_put(nskb, skb->len));
+
+		/* Let skb_put_padto() free nskb, and let dsa_slave_xmit() free
+		 * skb
+		 */
+		if (skb_put_padto(nskb, nskb->len + padlen))
+			return NULL;
+
+		consume_skb(skb);
+	}
+
+	if (!nskb)
+		return NULL;
+
+	/* Tag encoding */
+	tag  = skb_put(nskb, HELLCREEK_TAG_LEN);
+	*tag = BIT(dp->index);
+
+	return nskb;
+}
+
+static struct sk_buff *hellcreek_rcv(struct sk_buff *skb,
+				     struct net_device *dev,
+				     struct packet_type *pt)
+{
+	/* Tag decoding */
+	u8 *tag = skb_tail_pointer(skb) - HELLCREEK_TAG_LEN;
+	unsigned int port = tag[0] & 0x03;
+
+	skb->dev = dsa_master_find_slave(dev, 0, port);
+	if (!skb->dev) {
+		netdev_warn(dev, "Failed to get source port: %d\n", port);
+		return NULL;
+	}
+
+	pskb_trim_rcsum(skb, skb->len - HELLCREEK_TAG_LEN);
+
+	skb->offload_fwd_mark = true;
+
+	return skb;
+}
+
+static const struct dsa_device_ops hellcreek_netdev_ops = {
+	.name	  = "hellcreek",
+	.proto	  = DSA_TAG_PROTO_HELLCREEK,
+	.xmit	  = hellcreek_xmit,
+	.rcv	  = hellcreek_rcv,
+	.overhead = HELLCREEK_TAG_LEN,
+};
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_HELLCREEK);
+
+module_dsa_tag_driver(hellcreek_netdev_ops);
-- 
2.20.1


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

* [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-04 11:29 [PATCH net-next v6 0/7] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
  2020-10-04 11:29 ` [PATCH net-next v6 1/7] net: dsa: Add tag handling for Hirschmann Hellcreek switches Kurt Kanzenbach
@ 2020-10-04 11:29 ` Kurt Kanzenbach
  2020-10-04 12:56   ` Vladimir Oltean
  2020-10-04 11:29 ` [PATCH net-next v6 3/7] net: dsa: hellcreek: Add PTP clock support Kurt Kanzenbach
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-04 11:29 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Kurt Kanzenbach

Add a basic DSA driver for Hirschmann Hellcreek switches. Those switches are
implementing features needed for Time Sensitive Networking (TSN) such as support
for the Time Precision Protocol and various shapers like the Time Aware Shaper.

This driver includes basic support for networking:

 * VLAN handling
 * FDB handling
 * Port statistics
 * STP
 * Phylink

Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
---
 drivers/net/dsa/Kconfig                       |    2 +
 drivers/net/dsa/Makefile                      |    1 +
 drivers/net/dsa/hirschmann/Kconfig            |    8 +
 drivers/net/dsa/hirschmann/Makefile           |    2 +
 drivers/net/dsa/hirschmann/hellcreek.c        | 1285 +++++++++++++++++
 drivers/net/dsa/hirschmann/hellcreek.h        |  255 ++++
 .../platform_data/hirschmann-hellcreek.h      |   23 +
 7 files changed, 1576 insertions(+)
 create mode 100644 drivers/net/dsa/hirschmann/Kconfig
 create mode 100644 drivers/net/dsa/hirschmann/Makefile
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek.c
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek.h
 create mode 100644 include/linux/platform_data/hirschmann-hellcreek.h

diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 2451f61a38e4..f6a0488589fc 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -24,6 +24,8 @@ config NET_DSA_LOOP
 	  This enables support for a fake mock-up switch chip which
 	  exercises the DSA APIs.
 
+source "drivers/net/dsa/hirschmann/Kconfig"
+
 config NET_DSA_LANTIQ_GSWIP
 	tristate "Lantiq / Intel GSWIP"
 	depends on HAS_IOMEM && NET_DSA
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index 4a943ccc2ca4..a84adb140a04 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o
 obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o
 obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o
 obj-y				+= b53/
+obj-y				+= hirschmann/
 obj-y				+= microchip/
 obj-y				+= mv88e6xxx/
 obj-y				+= ocelot/
diff --git a/drivers/net/dsa/hirschmann/Kconfig b/drivers/net/dsa/hirschmann/Kconfig
new file mode 100644
index 000000000000..7d189cb936e3
--- /dev/null
+++ b/drivers/net/dsa/hirschmann/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+config NET_DSA_HIRSCHMANN_HELLCREEK
+	tristate "Hirschmann Hellcreek TSN Switch support"
+	depends on HAS_IOMEM
+	depends on NET_DSA
+	select NET_DSA_TAG_HELLCREEK
+	help
+	  This driver adds support for Hirschmann Hellcreek TSN switches.
diff --git a/drivers/net/dsa/hirschmann/Makefile b/drivers/net/dsa/hirschmann/Makefile
new file mode 100644
index 000000000000..0e12e149e40f
--- /dev/null
+++ b/drivers/net/dsa/hirschmann/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_NET_DSA_HIRSCHMANN_HELLCREEK)	+= hellcreek.o
diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
new file mode 100644
index 000000000000..2fe080010f8a
--- /dev/null
+++ b/drivers/net/dsa/hirschmann/hellcreek.c
@@ -0,0 +1,1285 @@
+// SPDX-License-Identifier: (GPL-2.0 or MIT)
+/*
+ * DSA driver for:
+ * Hirschmann Hellcreek TSN switch.
+ *
+ * Copyright (C) 2019,2020 Linutronix GmbH
+ * Author Kurt Kanzenbach <kurt@linutronix.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_mdio.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/if_bridge.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <linux/iopoll.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <net/dsa.h>
+
+#include "hellcreek.h"
+
+static const struct hellcreek_counter hellcreek_counter[] = {
+	{ 0x00, "RxFiltered", },
+	{ 0x01, "RxOctets1k", },
+	{ 0x02, "RxVTAG", },
+	{ 0x03, "RxL2BAD", },
+	{ 0x04, "RxOverloadDrop", },
+	{ 0x05, "RxUC", },
+	{ 0x06, "RxMC", },
+	{ 0x07, "RxBC", },
+	{ 0x08, "RxRS<64", },
+	{ 0x09, "RxRS64", },
+	{ 0x0a, "RxRS65_127", },
+	{ 0x0b, "RxRS128_255", },
+	{ 0x0c, "RxRS256_511", },
+	{ 0x0d, "RxRS512_1023", },
+	{ 0x0e, "RxRS1024_1518", },
+	{ 0x0f, "RxRS>1518", },
+	{ 0x10, "TxTailDropQueue0", },
+	{ 0x11, "TxTailDropQueue1", },
+	{ 0x12, "TxTailDropQueue2", },
+	{ 0x13, "TxTailDropQueue3", },
+	{ 0x14, "TxTailDropQueue4", },
+	{ 0x15, "TxTailDropQueue5", },
+	{ 0x16, "TxTailDropQueue6", },
+	{ 0x17, "TxTailDropQueue7", },
+	{ 0x18, "RxTrafficClass0", },
+	{ 0x19, "RxTrafficClass1", },
+	{ 0x1a, "RxTrafficClass2", },
+	{ 0x1b, "RxTrafficClass3", },
+	{ 0x1c, "RxTrafficClass4", },
+	{ 0x1d, "RxTrafficClass5", },
+	{ 0x1e, "RxTrafficClass6", },
+	{ 0x1f, "RxTrafficClass7", },
+	{ 0x21, "TxOctets1k", },
+	{ 0x22, "TxVTAG", },
+	{ 0x23, "TxL2BAD", },
+	{ 0x25, "TxUC", },
+	{ 0x26, "TxMC", },
+	{ 0x27, "TxBC", },
+	{ 0x28, "TxTS<64", },
+	{ 0x29, "TxTS64", },
+	{ 0x2a, "TxTS65_127", },
+	{ 0x2b, "TxTS128_255", },
+	{ 0x2c, "TxTS256_511", },
+	{ 0x2d, "TxTS512_1023", },
+	{ 0x2e, "TxTS1024_1518", },
+	{ 0x2f, "TxTS>1518", },
+	{ 0x30, "TxTrafficClassOverrun0", },
+	{ 0x31, "TxTrafficClassOverrun1", },
+	{ 0x32, "TxTrafficClassOverrun2", },
+	{ 0x33, "TxTrafficClassOverrun3", },
+	{ 0x34, "TxTrafficClassOverrun4", },
+	{ 0x35, "TxTrafficClassOverrun5", },
+	{ 0x36, "TxTrafficClassOverrun6", },
+	{ 0x37, "TxTrafficClassOverrun7", },
+	{ 0x38, "TxTrafficClass0", },
+	{ 0x39, "TxTrafficClass1", },
+	{ 0x3a, "TxTrafficClass2", },
+	{ 0x3b, "TxTrafficClass3", },
+	{ 0x3c, "TxTrafficClass4", },
+	{ 0x3d, "TxTrafficClass5", },
+	{ 0x3e, "TxTrafficClass6", },
+	{ 0x3f, "TxTrafficClass7", },
+};
+
+static u16 hellcreek_read(struct hellcreek *hellcreek, unsigned int offset)
+{
+	return readw(hellcreek->base + offset);
+}
+
+static u16 hellcreek_read_ctrl(struct hellcreek *hellcreek)
+{
+	return readw(hellcreek->base + HR_CTRL_C);
+}
+
+static u16 hellcreek_read_stat(struct hellcreek *hellcreek)
+{
+	return readw(hellcreek->base + HR_SWSTAT);
+}
+
+static void hellcreek_write(struct hellcreek *hellcreek, u16 data,
+			    unsigned int offset)
+{
+	writew(data, hellcreek->base + offset);
+}
+
+static void hellcreek_select_port(struct hellcreek *hellcreek, int port)
+{
+	u16 val = port << HR_PSEL_PTWSEL_SHIFT;
+
+	hellcreek_write(hellcreek, val, HR_PSEL);
+}
+
+static void hellcreek_select_prio(struct hellcreek *hellcreek, int prio)
+{
+	u16 val = prio << HR_PSEL_PRTCWSEL_SHIFT;
+
+	hellcreek_write(hellcreek, val, HR_PSEL);
+}
+
+static void hellcreek_select_counter(struct hellcreek *hellcreek, int counter)
+{
+	u16 val = counter << HR_CSEL_SHIFT;
+
+	hellcreek_write(hellcreek, val, HR_CSEL);
+
+	/* Data sheet states to wait at least 20 internal clock cycles */
+	ndelay(200);
+}
+
+static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid,
+				  bool pvid)
+{
+	u16 val = 0;
+
+	/* Set pvid bit first */
+	if (pvid)
+		val |= HR_VIDCFG_PVID;
+	hellcreek_write(hellcreek, val, HR_VIDCFG);
+
+	/* Set vlan */
+	val |= vid << HR_VIDCFG_VID_SHIFT;
+	hellcreek_write(hellcreek, val, HR_VIDCFG);
+}
+
+static int hellcreek_wait_until_ready(struct hellcreek *hellcreek)
+{
+	u16 val;
+
+	/* Wait up to 1ms, although 3 us should be enough */
+	return readx_poll_timeout(hellcreek_read_ctrl, hellcreek,
+				  val, val & HR_CTRL_C_READY,
+				  3, 1000);
+}
+
+static int hellcreek_wait_until_transitioned(struct hellcreek *hellcreek)
+{
+	u16 val;
+
+	return readx_poll_timeout_atomic(hellcreek_read_ctrl, hellcreek,
+					 val, !(val & HR_CTRL_C_TRANSITION),
+					 1, 1000);
+}
+
+static int hellcreek_wait_fdb_ready(struct hellcreek *hellcreek)
+{
+	u16 val;
+
+	return readx_poll_timeout_atomic(hellcreek_read_stat, hellcreek,
+					 val, !(val & HR_SWSTAT_BUSY),
+					 1, 1000);
+}
+
+static int hellcreek_detect(struct hellcreek *hellcreek)
+{
+	u16 id, rel_low, rel_high, date_low, date_high, tgd_ver;
+	u8 tgd_maj, tgd_min;
+	u32 rel, date;
+
+	id	  = hellcreek_read(hellcreek, HR_MODID_C);
+	rel_low	  = hellcreek_read(hellcreek, HR_REL_L_C);
+	rel_high  = hellcreek_read(hellcreek, HR_REL_H_C);
+	date_low  = hellcreek_read(hellcreek, HR_BLD_L_C);
+	date_high = hellcreek_read(hellcreek, HR_BLD_H_C);
+	tgd_ver   = hellcreek_read(hellcreek, TR_TGDVER);
+
+	if (id != hellcreek->pdata->module_id)
+		return -ENODEV;
+
+	rel	= rel_low | (rel_high << 16);
+	date	= date_low | (date_high << 16);
+	tgd_maj = (tgd_ver & TR_TGDVER_REV_MAJ_MASK) >> TR_TGDVER_REV_MAJ_SHIFT;
+	tgd_min = (tgd_ver & TR_TGDVER_REV_MIN_MASK) >> TR_TGDVER_REV_MIN_SHIFT;
+
+	dev_info(hellcreek->dev, "Module ID=%02x Release=%04x Date=%04x TGD Version=%02x.%02x\n",
+		 id, rel, date, tgd_maj, tgd_min);
+
+	return 0;
+}
+
+static void hellcreek_feature_detect(struct hellcreek *hellcreek)
+{
+	u16 features;
+
+	features = hellcreek_read(hellcreek, HR_FEABITS0);
+
+	/* Currently we only detect the size of the FDB table */
+	hellcreek->fdb_entries = ((features & HR_FEABITS0_FDBBINS_MASK) >>
+			       HR_FEABITS0_FDBBINS_SHIFT) * 32;
+
+	dev_info(hellcreek->dev, "Feature detect: FDB entries=%zu\n",
+		 hellcreek->fdb_entries);
+}
+
+static enum dsa_tag_protocol hellcreek_get_tag_protocol(struct dsa_switch *ds,
+							int port,
+							enum dsa_tag_protocol mp)
+{
+	return DSA_TAG_PROTO_HELLCREEK;
+}
+
+static int hellcreek_port_enable(struct dsa_switch *ds, int port,
+				 struct phy_device *phy)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	struct hellcreek_port *hellcreek_port;
+	u16 val;
+
+	hellcreek_port = &hellcreek->ports[port];
+
+	dev_dbg(hellcreek->dev, "Enable port %d\n", port);
+
+	mutex_lock(&hellcreek->reg_lock);
+
+	hellcreek_select_port(hellcreek, port);
+	val = hellcreek_port->ptcfg;
+	val |= HR_PTCFG_ADMIN_EN;
+	hellcreek_write(hellcreek, val, HR_PTCFG);
+	hellcreek_port->ptcfg = val;
+
+	mutex_unlock(&hellcreek->reg_lock);
+
+	return 0;
+}
+
+static void hellcreek_port_disable(struct dsa_switch *ds, int port)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	struct hellcreek_port *hellcreek_port;
+	u16 val;
+
+	hellcreek_port = &hellcreek->ports[port];
+
+	dev_dbg(hellcreek->dev, "Disable port %d\n", port);
+
+	mutex_lock(&hellcreek->reg_lock);
+
+	hellcreek_select_port(hellcreek, port);
+	val = hellcreek_port->ptcfg;
+	val &= ~HR_PTCFG_ADMIN_EN;
+	hellcreek_write(hellcreek, val, HR_PTCFG);
+	hellcreek_port->ptcfg = val;
+
+	mutex_unlock(&hellcreek->reg_lock);
+}
+
+static void hellcreek_get_strings(struct dsa_switch *ds, int port,
+				  u32 stringset, uint8_t *data)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) {
+		const struct hellcreek_counter *counter = &hellcreek_counter[i];
+
+		strlcpy(data + i * ETH_GSTRING_LEN,
+			counter->name, ETH_GSTRING_LEN);
+	}
+}
+
+static int hellcreek_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+	if (sset != ETH_SS_STATS)
+		return 0;
+
+	return ARRAY_SIZE(hellcreek_counter);
+}
+
+static void hellcreek_get_ethtool_stats(struct dsa_switch *ds, int port,
+					uint64_t *data)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	struct hellcreek_port *hellcreek_port;
+	int i;
+
+	hellcreek_port = &hellcreek->ports[port];
+
+	for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) {
+		const struct hellcreek_counter *counter = &hellcreek_counter[i];
+		u8 offset = counter->offset + port * 64;
+		u16 high, low;
+		u64 value = 0;
+
+		mutex_lock(&hellcreek->reg_lock);
+
+		hellcreek_select_counter(hellcreek, offset);
+
+		/* The registers are locked internally by selecting the
+		 * counter. So low and high can be read without reading high
+		 * again.
+		 */
+		high  = hellcreek_read(hellcreek, HR_CRDH);
+		low   = hellcreek_read(hellcreek, HR_CRDL);
+		value = (high << 16) | low;
+
+		hellcreek_port->counter_values[i] += value;
+		data[i] = hellcreek_port->counter_values[i];
+
+		mutex_unlock(&hellcreek->reg_lock);
+	}
+}
+
+static int hellcreek_vlan_prepare(struct dsa_switch *ds, int port,
+				  const struct switchdev_obj_port_vlan *vlan)
+{
+	struct hellcreek *hellcreek = ds->priv;
+
+	/* Nothing todo */
+	dev_dbg(hellcreek->dev, "VLAN prepare for port %d\n", port);
+
+	return 0;
+}
+
+static void hellcreek_select_vlan_params(struct hellcreek *hellcreek, int port,
+					 int *shift, int *mask)
+{
+	switch (port) {
+	case 0:
+		*shift = HR_VIDMBRCFG_P0MBR_SHIFT;
+		*mask  = HR_VIDMBRCFG_P0MBR_MASK;
+		break;
+	case 1:
+		*shift = HR_VIDMBRCFG_P1MBR_SHIFT;
+		*mask  = HR_VIDMBRCFG_P1MBR_MASK;
+		break;
+	case 2:
+		*shift = HR_VIDMBRCFG_P2MBR_SHIFT;
+		*mask  = HR_VIDMBRCFG_P2MBR_MASK;
+		break;
+	case 3:
+		*shift = HR_VIDMBRCFG_P3MBR_SHIFT;
+		*mask  = HR_VIDMBRCFG_P3MBR_MASK;
+		break;
+	default:
+		*shift = *mask = 0;
+		dev_err(hellcreek->dev, "Unknown port %d selected!\n", port);
+	}
+}
+
+static void hellcreek_apply_vlan(struct hellcreek *hellcreek, int port, u16 vid,
+				 bool pvid, bool untagged)
+{
+	int shift, mask;
+	u16 val;
+
+	dev_dbg(hellcreek->dev, "Apply VLAN: port=%d vid=%u pvid=%d untagged=%d",
+		port, vid, pvid, untagged);
+
+	mutex_lock(&hellcreek->reg_lock);
+
+	hellcreek_select_port(hellcreek, port);
+	hellcreek_select_vlan(hellcreek, vid, pvid);
+
+	/* Setup port vlan membership */
+	hellcreek_select_vlan_params(hellcreek, port, &shift, &mask);
+	val = hellcreek->vidmbrcfg[vid];
+	val &= ~mask;
+	if (untagged)
+		val |= HELLCREEK_VLAN_UNTAGGED_MEMBER << shift;
+	else
+		val |= HELLCREEK_VLAN_TAGGED_MEMBER << shift;
+
+	hellcreek_write(hellcreek, val, HR_VIDMBRCFG);
+	hellcreek->vidmbrcfg[vid] = val;
+
+	mutex_unlock(&hellcreek->reg_lock);
+}
+
+static void hellcreek_unapply_vlan(struct hellcreek *hellcreek, int port,
+				   u16 vid)
+{
+	int shift, mask;
+	u16 val;
+
+	dev_dbg(hellcreek->dev, "Unapply VLAN: port=%d vid=%u\n", port, vid);
+
+	mutex_lock(&hellcreek->reg_lock);
+
+	hellcreek_select_vlan(hellcreek, vid, 0);
+
+	/* Setup port vlan membership */
+	hellcreek_select_vlan_params(hellcreek, port, &shift, &mask);
+	val = hellcreek->vidmbrcfg[vid];
+	val &= ~mask;
+	val |= HELLCREEK_VLAN_NO_MEMBER << shift;
+
+	hellcreek_write(hellcreek, val, HR_VIDMBRCFG);
+	hellcreek->vidmbrcfg[vid] = val;
+
+	mutex_unlock(&hellcreek->reg_lock);
+}
+
+static int hellcreek_queue_vlan(struct hellcreek *hellcreek, int port, u16 vid,
+				bool pvid, bool untagged)
+{
+	struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
+	struct list_head *vlan_list = &hellcreek_port->vlan_list;
+	struct hellcreek_vlan *v;
+	bool found = false;
+
+	dev_dbg(hellcreek->dev, "Queue VLAN: port=%d vid=%u pvid=%d untagged=%d\n",
+		port, vid, pvid, untagged);
+
+	list_for_each_entry(v, vlan_list, list) {
+		if (v->vid == vid &&
+		    v->pvid == pvid &&
+		    v->untagged == untagged) {
+			found = true;
+			break;
+		}
+	}
+
+	if (found)
+		return 0;
+
+	v = kzalloc(sizeof(*v), GFP_KERNEL);
+	if (!v)
+		return -ENOMEM;
+
+	v->vid	    = vid;
+	v->pvid	    = pvid;
+	v->untagged = untagged;
+
+	list_add(&v->list, vlan_list);
+
+	return 0;
+}
+
+static void hellcreek_unqueue_vlan(struct hellcreek *hellcreek, int port,
+				   u16 vid)
+{
+	struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
+	struct list_head *vlan_list = &hellcreek_port->vlan_list;
+	struct hellcreek_vlan *v, *n;
+
+	dev_dbg(hellcreek->dev, "Unqueue VLAN: port=%d vid=%u\n", port, vid);
+
+	list_for_each_entry_safe(v, n, vlan_list, list) {
+		if (v->vid == vid) {
+			list_del(&v->list);
+			kfree(v);
+			return;
+		}
+	}
+}
+
+static void hellcreek_apply_queued_vlans(struct hellcreek *hellcreek, int port)
+{
+	struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
+	struct list_head *vlan_list = &hellcreek_port->vlan_list;
+	struct hellcreek_vlan *v;
+
+	dev_dbg(hellcreek->dev, "Apply queued VLANs: port%d\n", port);
+
+	list_for_each_entry(v, vlan_list, list) {
+		hellcreek_apply_vlan(hellcreek, port, v->vid, v->pvid,
+				     v->untagged);
+	}
+}
+
+static void hellcreek_clear_queued_vlans(struct hellcreek *hellcreek, int port)
+{
+	struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
+	struct list_head *vlan_list = &hellcreek_port->vlan_list;
+	struct hellcreek_vlan *v, *n;
+
+	dev_dbg(hellcreek->dev, "Clear queued VLANs: port%d\n", port);
+
+	list_for_each_entry_safe(v, n, vlan_list, list) {
+		list_del(&v->list);
+		kfree(v);
+	}
+}
+
+static void hellcreek_vlan_add(struct dsa_switch *ds, int port,
+			       const struct switchdev_obj_port_vlan *vlan)
+{
+	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+	struct hellcreek *hellcreek = ds->priv;
+	u16 vid;
+
+	dev_dbg(hellcreek->dev, "Add VLANs (%d -- %d) on port %d, %s, %s\n",
+		vlan->vid_begin, vlan->vid_end, port,
+		untagged ? "untagged" : "tagged",
+		pvid ? "PVID" : "no PVID");
+
+	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+		/* When vlan_filtering is set, the configuration can be applied
+		 * immediately. If not, the configuration has to be saved and
+		 * restored when vlan_filtering is set. This is because the
+		 * driver internally uses VLANs for the port separation.
+		 */
+		mutex_lock(&hellcreek->ports[port].vlan_lock);
+		if (hellcreek->ports[port].vlan_filtering)
+			hellcreek_apply_vlan(hellcreek, port, vid,
+					     pvid, untagged);
+		else
+			/* This function can fail due to memory allocations.
+			 * However, there's nothing we can do in that case.
+			 */
+			hellcreek_queue_vlan(hellcreek, port, vid,
+					     pvid, untagged);
+		mutex_unlock(&hellcreek->ports[port].vlan_lock);
+	}
+}
+
+static int hellcreek_vlan_del(struct dsa_switch *ds, int port,
+			      const struct switchdev_obj_port_vlan *vlan)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	u16 vid;
+
+	dev_dbg(hellcreek->dev, "Remove VLANs (%d -- %d) on port %d\n",
+		vlan->vid_begin, vlan->vid_end, port);
+
+	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+		mutex_lock(&hellcreek->ports[port].vlan_lock);
+		if (hellcreek->ports[port].vlan_filtering)
+			hellcreek_unapply_vlan(hellcreek, port, vid);
+		else
+			hellcreek_unqueue_vlan(hellcreek, port, vid);
+		mutex_unlock(&hellcreek->ports[port].vlan_lock);
+	}
+
+	return 0;
+}
+
+static void hellcreek_port_stp_state_set(struct dsa_switch *ds, int port,
+					 u8 state)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	struct hellcreek_port *hellcreek_port;
+	const char *new_state;
+	u16 val;
+
+	mutex_lock(&hellcreek->reg_lock);
+
+	hellcreek_port = &hellcreek->ports[port];
+	val = hellcreek_port->ptcfg;
+
+	switch (state) {
+	case BR_STATE_DISABLED:
+		new_state = "DISABLED";
+		val |= HR_PTCFG_BLOCKED;
+		val &= ~HR_PTCFG_LEARNING_EN;
+		break;
+	case BR_STATE_BLOCKING:
+		new_state = "BLOCKING";
+		val |= HR_PTCFG_BLOCKED;
+		val &= ~HR_PTCFG_LEARNING_EN;
+		break;
+	case BR_STATE_LISTENING:
+		new_state = "LISTENING";
+		val |= HR_PTCFG_BLOCKED;
+		val &= ~HR_PTCFG_LEARNING_EN;
+		break;
+	case BR_STATE_LEARNING:
+		new_state = "LEARNING";
+		val |= HR_PTCFG_BLOCKED;
+		val |= HR_PTCFG_LEARNING_EN;
+		break;
+	case BR_STATE_FORWARDING:
+		new_state = "FORWARDING";
+		val &= ~HR_PTCFG_BLOCKED;
+		val |= HR_PTCFG_LEARNING_EN;
+		break;
+	default:
+		new_state = "UNKNOWN";
+	}
+
+	hellcreek_select_port(hellcreek, port);
+	hellcreek_write(hellcreek, val, HR_PTCFG);
+	hellcreek_port->ptcfg = val;
+
+	mutex_unlock(&hellcreek->reg_lock);
+
+	dev_dbg(hellcreek->dev, "Configured STP state for port %d: %s\n",
+		port, new_state);
+}
+
+static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,
+				      struct net_device *br)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	int i;
+
+	dev_dbg(hellcreek->dev, "Port %d joins a bridge\n", port);
+
+	/* Configure port's vid to all other ports as egress untagged */
+	for (i = 0; i < ds->num_ports; ++i) {
+		if (!dsa_is_user_port(ds, i))
+			continue;
+
+		if (i == port)
+			continue;
+
+		hellcreek_apply_vlan(hellcreek, i, port, false, true);
+	}
+
+	return 0;
+}
+
+static void hellcreek_port_bridge_leave(struct dsa_switch *ds, int port,
+					struct net_device *br)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	int i;
+
+	dev_dbg(hellcreek->dev, "Port %d leaves a bridge\n", port);
+
+	/* Remove port's vid from all other ports */
+	for (i = 0; i < ds->num_ports; ++i) {
+		if (!dsa_is_user_port(ds, i))
+			continue;
+
+		if (i == port)
+			continue;
+
+		hellcreek_unapply_vlan(hellcreek, i, port);
+	}
+}
+
+static int __hellcreek_fdb_add(struct hellcreek *hellcreek,
+			       const struct hellcreek_fdb_entry *entry)
+{
+	u16 meta = 0;
+
+	dev_dbg(hellcreek->dev, "Add static FDB entry: MAC=%pM, MASK=0x%02x, "
+		"OBT=%d, REPRIO_EN=%d, PRIO=%d\n", entry->mac, entry->portmask,
+		entry->is_obt, entry->reprio_en, entry->reprio_tc);
+
+	/* Add mac address */
+	hellcreek_write(hellcreek, entry->mac[1] | (entry->mac[0] << 8), HR_FDBWDH);
+	hellcreek_write(hellcreek, entry->mac[3] | (entry->mac[2] << 8), HR_FDBWDM);
+	hellcreek_write(hellcreek, entry->mac[5] | (entry->mac[4] << 8), HR_FDBWDL);
+
+	/* Meta data */
+	meta |= entry->portmask << HR_FDBWRM0_PORTMASK_SHIFT;
+	if (entry->is_obt)
+		meta |= HR_FDBWRM0_OBT;
+	if (entry->reprio_en) {
+		meta |= HR_FDBWRM0_REPRIO_EN;
+		meta |= entry->reprio_tc << HR_FDBWRM0_REPRIO_TC_SHIFT;
+	}
+	hellcreek_write(hellcreek, meta, HR_FDBWRM0);
+
+	/* Commit */
+	hellcreek_write(hellcreek, 0x00, HR_FDBWRCMD);
+
+	/* Wait until done */
+	return hellcreek_wait_fdb_ready(hellcreek);
+}
+
+static int __hellcreek_fdb_del(struct hellcreek *hellcreek,
+			       const struct hellcreek_fdb_entry *entry)
+{
+	dev_dbg(hellcreek->dev, "Delete FDB entry: MAC=%pM!\n", entry->mac);
+
+	/* Delete by matching idx */
+	hellcreek_write(hellcreek, entry->idx | HR_FDBWRCMD_FDBDEL, HR_FDBWRCMD);
+
+	/* Wait until done */
+	return hellcreek_wait_fdb_ready(hellcreek);
+}
+
+/* Retrieve the index of a FDB entry by mac address. Currently we search through
+ * the complete table in hardware. If that's too slow, we might have to cache
+ * the complete FDB table in software.
+ */
+static int hellcreek_fdb_get(struct hellcreek *hellcreek,
+			     const unsigned char *dest,
+			     struct hellcreek_fdb_entry *entry)
+{
+	size_t i;
+
+	/* Set read pointer to zero: The read of HR_FDBMAX (read-only register)
+	 * should reset the internal pointer. But, that doesn't work. The vendor
+	 * suggested a subsequent write as workaround. Same for HR_FDBRDH below.
+	 */
+	hellcreek_read(hellcreek, HR_FDBMAX);
+	hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
+
+	/* We have to read the complete table, because the switch/driver might
+	 * enter new entries anywhere.
+	 */
+	for (i = 0; i < hellcreek->fdb_entries; ++i) {
+		unsigned char addr[ETH_ALEN];
+		u16 meta, mac;
+
+		meta	= hellcreek_read(hellcreek, HR_FDBMDRD);
+		mac	= hellcreek_read(hellcreek, HR_FDBRDL);
+		addr[5] = mac & 0xff;
+		addr[4] = (mac & 0xff00) >> 8;
+		mac	= hellcreek_read(hellcreek, HR_FDBRDM);
+		addr[3] = mac & 0xff;
+		addr[2] = (mac & 0xff00) >> 8;
+		mac	= hellcreek_read(hellcreek, HR_FDBRDH);
+		addr[1] = mac & 0xff;
+		addr[0] = (mac & 0xff00) >> 8;
+
+		/* Force next entry */
+		hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
+
+		if (memcmp(addr, dest, ETH_ALEN))
+			continue;
+
+		/* Match found */
+		entry->idx	    = i;
+		entry->portmask	    = (meta & HR_FDBMDRD_PORTMASK_MASK) >>
+			HR_FDBMDRD_PORTMASK_SHIFT;
+		entry->age	    = (meta & HR_FDBMDRD_AGE_MASK) >>
+			HR_FDBMDRD_AGE_SHIFT;
+		entry->is_obt	    = !!(meta & HR_FDBMDRD_OBT);
+		entry->pass_blocked = !!(meta & HR_FDBMDRD_PASS_BLOCKED);
+		entry->is_static    = !!(meta & HR_FDBMDRD_STATIC);
+		entry->reprio_tc    = (meta & HR_FDBMDRD_REPRIO_TC_MASK) >>
+			HR_FDBMDRD_REPRIO_TC_SHIFT;
+		entry->reprio_en    = !!(meta & HR_FDBMDRD_REPRIO_EN);
+		memcpy(entry->mac, addr, sizeof(addr));
+
+		return 0;
+	}
+
+	return -ENOENT;
+}
+
+static int hellcreek_fdb_add(struct dsa_switch *ds, int port,
+			     const unsigned char *addr, u16 vid)
+{
+	struct hellcreek_fdb_entry entry = { 0 };
+	struct hellcreek *hellcreek = ds->priv;
+	int ret;
+
+	dev_dbg(hellcreek->dev, "Add FDB entry for MAC=%pM\n", addr);
+
+	mutex_lock(&hellcreek->reg_lock);
+
+	ret = hellcreek_fdb_get(hellcreek, addr, &entry);
+	if (ret) {
+		/* Not found */
+		memcpy(entry.mac, addr, sizeof(entry.mac));
+		entry.portmask = BIT(port);
+
+		ret = __hellcreek_fdb_add(hellcreek, &entry);
+		if (ret) {
+			dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
+			goto out;
+		}
+	} else {
+		/* Found */
+		ret = __hellcreek_fdb_del(hellcreek, &entry);
+		if (ret) {
+			dev_err(hellcreek->dev, "Failed to delete FDB entry!\n");
+			goto out;
+		}
+
+		entry.portmask |= BIT(port);
+
+		ret = __hellcreek_fdb_add(hellcreek, &entry);
+		if (ret) {
+			dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
+			goto out;
+		}
+	}
+
+out:
+	mutex_unlock(&hellcreek->reg_lock);
+
+	return ret;
+}
+
+static int hellcreek_fdb_del(struct dsa_switch *ds, int port,
+			     const unsigned char *addr, u16 vid)
+{
+	struct hellcreek_fdb_entry entry = { 0 };
+	struct hellcreek *hellcreek = ds->priv;
+	int ret;
+
+	dev_dbg(hellcreek->dev, "Delete FDB entry for MAC=%pM\n", addr);
+
+	mutex_lock(&hellcreek->reg_lock);
+
+	ret = hellcreek_fdb_get(hellcreek, addr, &entry);
+	if (ret) {
+		/* Not found */
+		dev_err(hellcreek->dev, "FDB entry for deletion not found!\n");
+	} else {
+		/* Found */
+		ret = __hellcreek_fdb_del(hellcreek, &entry);
+		if (ret) {
+			dev_err(hellcreek->dev, "Failed to delete FDB entry!\n");
+			goto out;
+		}
+
+		entry.portmask &= ~BIT(port);
+
+		if (entry.portmask != 0x00) {
+			ret = __hellcreek_fdb_add(hellcreek, &entry);
+			if (ret) {
+				dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
+				goto out;
+			}
+		}
+	}
+
+out:
+	mutex_unlock(&hellcreek->reg_lock);
+
+	return ret;
+}
+
+static int hellcreek_fdb_dump(struct dsa_switch *ds, int port,
+			      dsa_fdb_dump_cb_t *cb, void *data)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	u16 entries;
+	size_t i;
+
+	mutex_lock(&hellcreek->reg_lock);
+
+	/* Set read pointer to zero: The read of HR_FDBMAX (read-only register)
+	 * should reset the internal pointer. But, that doesn't work. The vendor
+	 * suggested a subsequent write as workaround. Same for HR_FDBRDH below.
+	 */
+	entries = hellcreek_read(hellcreek, HR_FDBMAX);
+	hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
+
+	dev_dbg(hellcreek->dev, "FDB dump for port %d, entries=%d!\n", port, entries);
+
+	/* Read table */
+	for (i = 0; i < hellcreek->fdb_entries; ++i) {
+		unsigned char null_addr[ETH_ALEN] = { 0 };
+		struct hellcreek_fdb_entry entry = { 0 };
+		u16 meta, mac;
+
+		meta	= hellcreek_read(hellcreek, HR_FDBMDRD);
+		mac	= hellcreek_read(hellcreek, HR_FDBRDL);
+		entry.mac[5] = mac & 0xff;
+		entry.mac[4] = (mac & 0xff00) >> 8;
+		mac	= hellcreek_read(hellcreek, HR_FDBRDM);
+		entry.mac[3] = mac & 0xff;
+		entry.mac[2] = (mac & 0xff00) >> 8;
+		mac	= hellcreek_read(hellcreek, HR_FDBRDH);
+		entry.mac[1] = mac & 0xff;
+		entry.mac[0] = (mac & 0xff00) >> 8;
+
+		/* Force next entry */
+		hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
+
+		/* Check valid */
+		if (!memcmp(entry.mac, null_addr, ETH_ALEN))
+			continue;
+
+		entry.portmask	= (meta & HR_FDBMDRD_PORTMASK_MASK) >>
+			HR_FDBMDRD_PORTMASK_SHIFT;
+		entry.is_static	= !!(meta & HR_FDBMDRD_STATIC);
+
+		/* Check port mask */
+		if (!(entry.portmask & BIT(port)))
+			continue;
+
+		cb(entry.mac, 0, entry.is_static, data);
+	}
+
+	mutex_unlock(&hellcreek->reg_lock);
+
+	return 0;
+}
+
+/* Default setup for DSA: VLAN <X>: CPU and Port <X> egress untagged. */
+static int hellcreek_setup_vlan_membership(struct dsa_switch *ds, int port,
+					   bool enabled)
+{
+	int upstream = dsa_upstream_port(ds, port);
+	struct hellcreek *hellcreek = ds->priv;
+	u16 vid = port;
+
+	/* The CPU port is implicitly configured by configuring the front-panel
+	 * ports.
+	 */
+	if (!dsa_is_user_port(ds, port))
+		return 0;
+
+	/* Apply vid to port as egress untagged and port vlan id */
+	if (enabled)
+		hellcreek_apply_vlan(hellcreek, port, vid, true, true);
+	else
+		hellcreek_unapply_vlan(hellcreek, port, vid);
+
+	/* Apply vid to cpu port as well */
+	if (enabled)
+		hellcreek_apply_vlan(hellcreek, upstream, vid, false, true);
+	else
+		hellcreek_unapply_vlan(hellcreek, upstream, vid);
+
+	return 0;
+}
+
+static void hellcreek_setup_ingressflt(struct hellcreek *hellcreek, int port,
+				       bool enable)
+{
+	struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
+	u16 ptcfg;
+
+	mutex_lock(&hellcreek->reg_lock);
+
+	ptcfg = hellcreek_port->ptcfg;
+
+	if (enable)
+		ptcfg |= HR_PTCFG_INGRESSFLT;
+	else
+		ptcfg &= ~HR_PTCFG_INGRESSFLT;
+
+	hellcreek_select_port(hellcreek, port);
+	hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
+	hellcreek_port->ptcfg = ptcfg;
+
+	mutex_unlock(&hellcreek->reg_lock);
+}
+
+static void hellcreek_apply_vlan_filtering(struct hellcreek *hellcreek,
+					   int port, bool vlan_filtering)
+{
+	mutex_lock(&hellcreek->ports[port].vlan_lock);
+	if (vlan_filtering) {
+		hellcreek_apply_queued_vlans(hellcreek, port);
+		hellcreek_clear_queued_vlans(hellcreek, port);
+	}
+	hellcreek->ports[port].vlan_filtering = vlan_filtering;
+	mutex_unlock(&hellcreek->ports[port].vlan_lock);
+}
+
+static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port,
+				    bool vlan_filtering)
+{
+	struct hellcreek *hellcreek = ds->priv;
+
+	dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n",
+		vlan_filtering ? "Enable" : "Disable", port);
+
+	/* Configure port to drop packages with not known vids */
+	hellcreek_setup_ingressflt(hellcreek, port, vlan_filtering);
+
+	/* Drop DSA vlan membership config. The user can now do it. */
+	hellcreek_setup_vlan_membership(ds, port, !vlan_filtering);
+
+	/* Apply saved vlan configurations while not filtering for port <X>. */
+	hellcreek_apply_vlan_filtering(hellcreek, port, vlan_filtering);
+
+	/* Do the same for the cpu port. */
+	hellcreek_apply_vlan_filtering(hellcreek, CPU_PORT, vlan_filtering);
+
+	return 0;
+}
+
+static int hellcreek_enable_ip_core(struct hellcreek *hellcreek)
+{
+	int ret;
+	u16 val;
+
+	mutex_lock(&hellcreek->reg_lock);
+
+	val = hellcreek_read(hellcreek, HR_CTRL_C);
+	val |= HR_CTRL_C_ENABLE;
+	hellcreek_write(hellcreek, val, HR_CTRL_C);
+	ret = hellcreek_wait_until_transitioned(hellcreek);
+
+	mutex_unlock(&hellcreek->reg_lock);
+
+	return ret;
+}
+
+static void hellcreek_setup_cpu_and_tunnel_port(struct hellcreek *hellcreek)
+{
+	struct hellcreek_port *tunnel_port = &hellcreek->ports[TUNNEL_PORT];
+	struct hellcreek_port *cpu_port = &hellcreek->ports[CPU_PORT];
+	u16 ptcfg = 0;
+
+	ptcfg |= HR_PTCFG_LEARNING_EN | HR_PTCFG_ADMIN_EN;
+
+	mutex_lock(&hellcreek->reg_lock);
+
+	hellcreek_select_port(hellcreek, CPU_PORT);
+	hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
+
+	hellcreek_select_port(hellcreek, TUNNEL_PORT);
+	hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
+
+	cpu_port->ptcfg	   = ptcfg;
+	tunnel_port->ptcfg = ptcfg;
+
+	mutex_unlock(&hellcreek->reg_lock);
+}
+
+static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
+{
+	int i;
+
+	/* The switch has multiple egress queues per port. The queue is selected
+	 * via the PCP field in the VLAN header. The switch internally deals
+	 * with traffic classes instead of PCP values and this mapping is
+	 * configurable.
+	 *
+	 * The default mapping is (PCP - TC):
+	 *  7 - 7
+	 *  6 - 6
+	 *  5 - 5
+	 *  4 - 4
+	 *  3 - 3
+	 *  2 - 1
+	 *  1 - 0
+	 *  0 - 2
+	 *
+	 * The default should be an identity mapping.
+	 */
+
+	for (i = 0; i < 8; ++i) {
+		mutex_lock(&hellcreek->reg_lock);
+
+		hellcreek_select_prio(hellcreek, i);
+		hellcreek_write(hellcreek,
+				i << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
+				HR_PRTCCFG);
+
+		mutex_unlock(&hellcreek->reg_lock);
+	}
+}
+
+static int hellcreek_setup(struct dsa_switch *ds)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	int ret, i;
+
+	dev_dbg(hellcreek->dev, "Set up the switch\n");
+
+	/* Let's go */
+	ret = hellcreek_enable_ip_core(hellcreek);
+	if (ret) {
+		dev_err(hellcreek->dev, "Failed to enable IP core!\n");
+		return ret;
+	}
+
+	/* Enable CPU/Tunnel ports */
+	hellcreek_setup_cpu_and_tunnel_port(hellcreek);
+
+	/* Switch config: Keep defaults, enable FDB aging and learning, and tag
+	 * each frame from/to cpu port for DSA tagging.  Also enable the length
+	 * aware shaping mode. This eliminates the need for Qbv guard bands.
+	 */
+	hellcreek_write(hellcreek, HR_SWCFG_FDBAGE_EN | HR_SWCFG_FDBLRN_EN |
+			HR_SWCFG_ALWAYS_OBT |
+			(HR_SWCFG_LAS_ON << HR_SWCFG_LAS_MODE_SHIFT),
+			HR_SWCFG);
+
+	/* Initial vlan membership to reflect port separation */
+	for (i = 0; i < ds->num_ports; ++i) {
+		if (!dsa_is_user_port(ds, i))
+			continue;
+
+		ret = hellcreek_setup_vlan_membership(ds, i, true);
+		if (ret) {
+			dev_err(hellcreek->dev,
+				"Failed to setup VLAN membership config!\n");
+			return ret;
+		}
+	}
+
+	/* Configure PCP <-> TC mapping */
+	hellcreek_setup_tc_identity_mapping(hellcreek);
+
+	/* Allow VLAN configurations while not filtering */
+	ds->configure_vlan_while_not_filtering = true;
+
+	return 0;
+}
+
+static void hellcreek_phylink_validate(struct dsa_switch *ds, int port,
+				       unsigned long *supported,
+				       struct phylink_link_state *state)
+{
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+	struct hellcreek *hellcreek = ds->priv;
+
+	dev_dbg(hellcreek->dev, "Phylink validate for port %d\n", port);
+
+	/* The MAC settings are a hardware configuration option and cannot be
+	 * changed at run time or by strapping. Therefore the attached PHYs
+	 * should be programmed to only advertise settings which are supported
+	 * by the hardware.
+	 */
+	if (hellcreek->pdata->is_100_mbits)
+		phylink_set(mask, 100baseT_Full);
+	else
+		phylink_set(mask, 1000baseT_Full);
+
+	bitmap_and(supported, supported, mask,
+		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+	bitmap_and(state->advertising, state->advertising, mask,
+		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static const struct dsa_switch_ops hellcreek_ds_ops = {
+	.get_tag_protocol    = hellcreek_get_tag_protocol,
+	.setup		     = hellcreek_setup,
+	.get_strings	     = hellcreek_get_strings,
+	.get_ethtool_stats   = hellcreek_get_ethtool_stats,
+	.get_sset_count	     = hellcreek_get_sset_count,
+	.port_enable	     = hellcreek_port_enable,
+	.port_disable	     = hellcreek_port_disable,
+	.port_vlan_filtering = hellcreek_vlan_filtering,
+	.port_vlan_prepare   = hellcreek_vlan_prepare,
+	.port_vlan_add	     = hellcreek_vlan_add,
+	.port_vlan_del	     = hellcreek_vlan_del,
+	.port_fdb_dump	     = hellcreek_fdb_dump,
+	.port_fdb_add	     = hellcreek_fdb_add,
+	.port_fdb_del	     = hellcreek_fdb_del,
+	.port_bridge_join    = hellcreek_port_bridge_join,
+	.port_bridge_leave   = hellcreek_port_bridge_leave,
+	.port_stp_state_set  = hellcreek_port_stp_state_set,
+	.phylink_validate    = hellcreek_phylink_validate,
+};
+
+static int hellcreek_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct hellcreek *hellcreek;
+	struct resource *res;
+	int ret, i;
+
+	hellcreek = devm_kzalloc(dev, sizeof(*hellcreek), GFP_KERNEL);
+	if (!hellcreek)
+		return -ENOMEM;
+
+	hellcreek->vidmbrcfg = devm_kcalloc(dev, 4096,
+					    sizeof(*hellcreek->vidmbrcfg),
+					    GFP_KERNEL);
+	if (!hellcreek->vidmbrcfg)
+		return -ENOMEM;
+
+	hellcreek->pdata = of_device_get_match_data(dev);
+
+	hellcreek->ports = devm_kcalloc(dev, hellcreek->pdata->num_ports,
+					sizeof(*hellcreek->ports),
+					GFP_KERNEL);
+	if (!hellcreek->ports)
+		return -ENOMEM;
+
+	for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
+		struct hellcreek_port *port = &hellcreek->ports[i];
+
+		port->counter_values =
+			devm_kcalloc(dev,
+				     ARRAY_SIZE(hellcreek_counter),
+				     sizeof(*port->counter_values),
+				     GFP_KERNEL);
+		if (!port->counter_values)
+			return -ENOMEM;
+
+		port->hellcreek	     = hellcreek;
+		port->vlan_filtering = 0;
+		port->port	     = i;
+
+		INIT_LIST_HEAD(&port->vlan_list);
+		mutex_init(&port->vlan_lock);
+	}
+
+	mutex_init(&hellcreek->reg_lock);
+
+	hellcreek->dev = dev;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tsn");
+	if (!res) {
+		dev_err(dev, "No memory region provided!\n");
+		return -ENODEV;
+	}
+
+	hellcreek->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(hellcreek->base)) {
+		dev_err(dev, "No memory available!\n");
+		return PTR_ERR(hellcreek->base);
+	}
+
+	ret = hellcreek_detect(hellcreek);
+	if (ret) {
+		dev_err(dev, "No (known) chip found!\n");
+		return ret;
+	}
+
+	ret = hellcreek_wait_until_ready(hellcreek);
+	if (ret) {
+		dev_err(dev, "Switch didn't become ready!\n");
+		return ret;
+	}
+
+	hellcreek_feature_detect(hellcreek);
+
+	hellcreek->ds = devm_kzalloc(dev, sizeof(*hellcreek->ds), GFP_KERNEL);
+	if (!hellcreek->ds)
+		return -ENOMEM;
+
+	hellcreek->ds->dev	     = dev;
+	hellcreek->ds->priv	     = hellcreek;
+	hellcreek->ds->ops	     = &hellcreek_ds_ops;
+	hellcreek->ds->num_ports     = hellcreek->pdata->num_ports;
+	hellcreek->ds->num_tx_queues = HELLCREEK_NUM_EGRESS_QUEUES;
+
+	ret = dsa_register_switch(hellcreek->ds);
+	if (ret) {
+		dev_err(dev, "Unable to register switch\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, hellcreek);
+
+	return 0;
+}
+
+static int hellcreek_remove(struct platform_device *pdev)
+{
+	struct hellcreek *hellcreek = platform_get_drvdata(pdev);
+
+	dsa_unregister_switch(hellcreek->ds);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static const struct hellcreek_platform_data de1soc_r1_pdata = {
+	.num_ports	 = 4,
+	.is_100_mbits	 = 1,
+	.qbv_support	 = 1,
+	.qbv_on_cpu_port = 1,
+	.qbu_support	 = 0,
+	.module_id	 = 0x4c30,
+};
+
+static const struct of_device_id hellcreek_of_match[] = {
+	{
+		.compatible = "hirschmann,hellcreek-de1soc-r1",
+		.data	    = &de1soc_r1_pdata,
+	},
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, hellcreek_of_match);
+
+static struct platform_driver hellcreek_driver = {
+	.probe	= hellcreek_probe,
+	.remove = hellcreek_remove,
+	.driver = {
+		.name = "hellcreek",
+		.of_match_table = hellcreek_of_match,
+	},
+};
+module_platform_driver(hellcreek_driver);
+
+MODULE_AUTHOR("Kurt Kanzenbach <kurt@linutronix.de>");
+MODULE_DESCRIPTION("Hirschmann Hellcreek driver");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h
new file mode 100644
index 000000000000..d57055aadc6f
--- /dev/null
+++ b/drivers/net/dsa/hirschmann/hellcreek.h
@@ -0,0 +1,255 @@
+/* SPDX-License-Identifier: (GPL-2.0 or MIT) */
+/*
+ * DSA driver for:
+ * Hirschmann Hellcreek TSN switch.
+ *
+ * Copyright (C) 2019,2020 Linutronix GmbH
+ * Author Kurt Kanzenbach <kurt@linutronix.de>
+ */
+
+#ifndef _HELLCREEK_H_
+#define _HELLCREEK_H_
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/timecounter.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/hirschmann-hellcreek.h>
+#include <net/dsa.h>
+
+/* Ports:
+ *  - 0: CPU
+ *  - 1: Tunnel
+ *  - 2: TSN front port 1
+ *  - 3: TSN front port 2
+ *  - ...
+ */
+#define CPU_PORT			0
+#define TUNNEL_PORT			1
+
+#define HELLCREEK_VLAN_NO_MEMBER	0x0
+#define HELLCREEK_VLAN_UNTAGGED_MEMBER	0x1
+#define HELLCREEK_VLAN_TAGGED_MEMBER	0x3
+#define HELLCREEK_NUM_EGRESS_QUEUES	8
+
+/* Register definitions */
+#define HR_MODID_C			(0 * 2)
+#define HR_REL_L_C			(1 * 2)
+#define HR_REL_H_C			(2 * 2)
+#define HR_BLD_L_C			(3 * 2)
+#define HR_BLD_H_C			(4 * 2)
+#define HR_CTRL_C			(5 * 2)
+#define HR_CTRL_C_READY			BIT(14)
+#define HR_CTRL_C_TRANSITION		BIT(13)
+#define HR_CTRL_C_ENABLE		BIT(0)
+
+#define HR_PSEL				(0xa6 * 2)
+#define HR_PSEL_PTWSEL_SHIFT		4
+#define HR_PSEL_PTWSEL_MASK		GENMASK(5, 4)
+#define HR_PSEL_PRTCWSEL_SHIFT		0
+#define HR_PSEL_PRTCWSEL_MASK		GENMASK(2, 0)
+
+#define HR_PTCFG			(0xa7 * 2)
+#define HR_PTCFG_MLIMIT_EN		BIT(13)
+#define HR_PTCFG_UMC_FLT		BIT(10)
+#define HR_PTCFG_UUC_FLT		BIT(9)
+#define HR_PTCFG_UNTRUST		BIT(8)
+#define HR_PTCFG_TAG_REQUIRED		BIT(7)
+#define HR_PTCFG_PPRIO_SHIFT		4
+#define HR_PTCFG_PPRIO_MASK		GENMASK(6, 4)
+#define HR_PTCFG_INGRESSFLT		BIT(3)
+#define HR_PTCFG_BLOCKED		BIT(2)
+#define HR_PTCFG_LEARNING_EN		BIT(1)
+#define HR_PTCFG_ADMIN_EN		BIT(0)
+
+#define HR_PRTCCFG			(0xa8 * 2)
+#define HR_PRTCCFG_PCP_TC_MAP_SHIFT	0
+#define HR_PRTCCFG_PCP_TC_MAP_MASK	GENMASK(2, 0)
+
+#define HR_CSEL				(0x8d * 2)
+#define HR_CSEL_SHIFT			0
+#define HR_CSEL_MASK			GENMASK(7, 0)
+#define HR_CRDL				(0x8e * 2)
+#define HR_CRDH				(0x8f * 2)
+
+#define HR_SWTRC_CFG			(0x90 * 2)
+#define HR_SWTRC0			(0x91 * 2)
+#define HR_SWTRC1			(0x92 * 2)
+#define HR_PFREE			(0x93 * 2)
+#define HR_MFREE			(0x94 * 2)
+
+#define HR_FDBAGE			(0x97 * 2)
+#define HR_FDBMAX			(0x98 * 2)
+#define HR_FDBRDL			(0x99 * 2)
+#define HR_FDBRDM			(0x9a * 2)
+#define HR_FDBRDH			(0x9b * 2)
+
+#define HR_FDBMDRD			(0x9c * 2)
+#define HR_FDBMDRD_PORTMASK_SHIFT	0
+#define HR_FDBMDRD_PORTMASK_MASK	GENMASK(3, 0)
+#define HR_FDBMDRD_AGE_SHIFT		4
+#define HR_FDBMDRD_AGE_MASK		GENMASK(7, 4)
+#define HR_FDBMDRD_OBT			BIT(8)
+#define HR_FDBMDRD_PASS_BLOCKED		BIT(9)
+#define HR_FDBMDRD_STATIC		BIT(11)
+#define HR_FDBMDRD_REPRIO_TC_SHIFT	12
+#define HR_FDBMDRD_REPRIO_TC_MASK	GENMASK(14, 12)
+#define HR_FDBMDRD_REPRIO_EN		BIT(15)
+
+#define HR_FDBWDL			(0x9d * 2)
+#define HR_FDBWDM			(0x9e * 2)
+#define HR_FDBWDH			(0x9f * 2)
+#define HR_FDBWRM0			(0xa0 * 2)
+#define HR_FDBWRM0_PORTMASK_SHIFT	0
+#define HR_FDBWRM0_PORTMASK_MASK	GENMASK(3, 0)
+#define HR_FDBWRM0_OBT			BIT(8)
+#define HR_FDBWRM0_PASS_BLOCKED		BIT(9)
+#define HR_FDBWRM0_REPRIO_TC_SHIFT	12
+#define HR_FDBWRM0_REPRIO_TC_MASK	GENMASK(14, 12)
+#define HR_FDBWRM0_REPRIO_EN		BIT(15)
+#define HR_FDBWRM1			(0xa1 * 2)
+
+#define HR_FDBWRCMD			(0xa2 * 2)
+#define HR_FDBWRCMD_FDBDEL		BIT(9)
+
+#define HR_SWCFG			(0xa3 * 2)
+#define HR_SWCFG_GM_STATEMD		BIT(15)
+#define HR_SWCFG_LAS_MODE_SHIFT		12
+#define HR_SWCFG_LAS_MODE_MASK		GENMASK(13, 12)
+#define HR_SWCFG_LAS_OFF		(0x00)
+#define HR_SWCFG_LAS_ON			(0x01)
+#define HR_SWCFG_LAS_STATIC		(0x10)
+#define HR_SWCFG_CT_EN			BIT(11)
+#define HR_SWCFG_LAN_UNAWARE		BIT(10)
+#define HR_SWCFG_ALWAYS_OBT		BIT(9)
+#define HR_SWCFG_FDBAGE_EN		BIT(5)
+#define HR_SWCFG_FDBLRN_EN		BIT(4)
+
+#define HR_SWSTAT			(0xa4 * 2)
+#define HR_SWSTAT_FAIL			BIT(4)
+#define HR_SWSTAT_BUSY			BIT(0)
+
+#define HR_SWCMD			(0xa5 * 2)
+#define HW_SWCMD_FLUSH			BIT(0)
+
+#define HR_VIDCFG			(0xaa * 2)
+#define HR_VIDCFG_VID_SHIFT		0
+#define HR_VIDCFG_VID_MASK		GENMASK(11, 0)
+#define HR_VIDCFG_PVID			BIT(12)
+
+#define HR_VIDMBRCFG			(0xab * 2)
+#define HR_VIDMBRCFG_P0MBR_SHIFT	0
+#define HR_VIDMBRCFG_P0MBR_MASK		GENMASK(1, 0)
+#define HR_VIDMBRCFG_P1MBR_SHIFT	2
+#define HR_VIDMBRCFG_P1MBR_MASK		GENMASK(3, 2)
+#define HR_VIDMBRCFG_P2MBR_SHIFT	4
+#define HR_VIDMBRCFG_P2MBR_MASK		GENMASK(5, 4)
+#define HR_VIDMBRCFG_P3MBR_SHIFT	6
+#define HR_VIDMBRCFG_P3MBR_MASK		GENMASK(7, 6)
+
+#define HR_FEABITS0			(0xac * 2)
+#define HR_FEABITS0_FDBBINS_SHIFT	4
+#define HR_FEABITS0_FDBBINS_MASK	GENMASK(7, 4)
+#define HR_FEABITS0_PCNT_SHIFT		8
+#define HR_FEABITS0_PCNT_MASK		GENMASK(11, 8)
+#define HR_FEABITS0_MCNT_SHIFT		12
+#define HR_FEABITS0_MCNT_MASK		GENMASK(15, 12)
+
+#define TR_QTRACK			(0xb1 * 2)
+#define TR_TGDVER			(0xb3 * 2)
+#define TR_TGDVER_REV_MIN_MASK		GENMASK(7, 0)
+#define TR_TGDVER_REV_MIN_SHIFT		0
+#define TR_TGDVER_REV_MAJ_MASK		GENMASK(15, 8)
+#define TR_TGDVER_REV_MAJ_SHIFT		8
+#define TR_TGDSEL			(0xb4 * 2)
+#define TR_TGDSEL_TDGSEL_MASK		GENMASK(1, 0)
+#define TR_TGDSEL_TDGSEL_SHIFT		0
+#define TR_TGDCTRL			(0xb5 * 2)
+#define TR_TGDCTRL_GATE_EN		BIT(0)
+#define TR_TGDCTRL_CYC_SNAP		BIT(4)
+#define TR_TGDCTRL_SNAP_EST		BIT(5)
+#define TR_TGDCTRL_ADMINGATESTATES_MASK	GENMASK(15, 8)
+#define TR_TGDCTRL_ADMINGATESTATES_SHIFT	8
+#define TR_TGDSTAT0			(0xb6 * 2)
+#define TR_TGDSTAT1			(0xb7 * 2)
+#define TR_ESTWRL			(0xb8 * 2)
+#define TR_ESTWRH			(0xb9 * 2)
+#define TR_ESTCMD			(0xba * 2)
+#define TR_ESTCMD_ESTSEC_MASK		GENMASK(2, 0)
+#define TR_ESTCMD_ESTSEC_SHIFT		0
+#define TR_ESTCMD_ESTARM		BIT(4)
+#define TR_ESTCMD_ESTSWCFG		BIT(5)
+#define TR_EETWRL			(0xbb * 2)
+#define TR_EETWRH			(0xbc * 2)
+#define TR_EETCMD			(0xbd * 2)
+#define TR_EETCMD_EETSEC_MASK		GEMASK(2, 0)
+#define TR_EETCMD_EETSEC_SHIFT		0
+#define TR_EETCMD_EETARM		BIT(4)
+#define TR_CTWRL			(0xbe * 2)
+#define TR_CTWRH			(0xbf * 2)
+#define TR_LCNSL			(0xc1 * 2)
+#define TR_LCNSH			(0xc2 * 2)
+#define TR_LCS				(0xc3 * 2)
+#define TR_GCLDAT			(0xc4 * 2)
+#define TR_GCLDAT_GCLWRGATES_MASK	GENMASK(7, 0)
+#define TR_GCLDAT_GCLWRGATES_SHIFT	0
+#define TR_GCLDAT_GCLWRLAST		BIT(8)
+#define TR_GCLDAT_GCLOVRI		BIT(9)
+#define TR_GCLTIL			(0xc5 * 2)
+#define TR_GCLTIH			(0xc6 * 2)
+#define TR_GCLCMD			(0xc7 * 2)
+#define TR_GCLCMD_GCLWRADR_MASK		GENMASK(7, 0)
+#define TR_GCLCMD_GCLWRADR_SHIFT	0
+#define TR_GCLCMD_INIT_GATE_STATES_MASK	GENMASK(15, 8)
+#define TR_GCLCMD_INIT_GATE_STATES_SHIFT	8
+
+struct hellcreek_counter {
+	u8 offset;
+	const char *name;
+};
+
+struct hellcreek;
+
+struct hellcreek_port {
+	struct hellcreek *hellcreek;
+	struct list_head vlan_list;
+	struct mutex vlan_lock;	/* VLAN list mutex */
+	int port;
+	int vlan_filtering;	/* Is VLAN filtering activated */
+	u16 ptcfg;		/* ptcfg shadow */
+	u64 *counter_values;
+};
+
+struct hellcreek_fdb_entry {
+	size_t idx;
+	unsigned char mac[ETH_ALEN];
+	u8 portmask;
+	u8 age;
+	u8 is_obt;
+	u8 pass_blocked;
+	u8 is_static;
+	u8 reprio_tc;
+	u8 reprio_en;
+};
+
+struct hellcreek_vlan {
+	struct list_head list;
+	int pvid;
+	int untagged;
+	u16 vid;
+};
+
+struct hellcreek {
+	const struct hellcreek_platform_data *pdata;
+	struct device *dev;
+	struct dsa_switch *ds;
+	struct hellcreek_port *ports;
+	struct mutex reg_lock;	/* Switch IP register lock */
+	void __iomem *base;
+	u8 *vidmbrcfg;		/* vidmbrcfg shadow */
+	size_t fdb_entries;
+};
+
+#endif /* _HELLCREEK_H_ */
diff --git a/include/linux/platform_data/hirschmann-hellcreek.h b/include/linux/platform_data/hirschmann-hellcreek.h
new file mode 100644
index 000000000000..388846766bb2
--- /dev/null
+++ b/include/linux/platform_data/hirschmann-hellcreek.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: (GPL-2.0 or MIT) */
+/*
+ * Hirschmann Hellcreek TSN switch platform data.
+ *
+ * Copyright (C) 2020 Linutronix GmbH
+ * Author Kurt Kanzenbach <kurt@linutronix.de>
+ */
+
+#ifndef _HIRSCHMANN_HELLCREEK_H_
+#define _HIRSCHMANN_HELLCREEK_H_
+
+#include <linux/types.h>
+
+struct hellcreek_platform_data {
+	int num_ports;		/* Amount of switch ports */
+	int is_100_mbits;	/* Is it configured to 100 or 1000 mbit/s */
+	int qbv_support;	/* Qbv support on front TSN ports */
+	int qbv_on_cpu_port;	/* Qbv support on the CPU port */
+	int qbu_support;	/* Qbu support on front TSN ports */
+	u16 module_id;		/* Module identificaton */
+};
+
+#endif /* _HIRSCHMANN_HELLCREEK_H_ */
-- 
2.20.1


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

* [PATCH net-next v6 3/7] net: dsa: hellcreek: Add PTP clock support
  2020-10-04 11:29 [PATCH net-next v6 0/7] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
  2020-10-04 11:29 ` [PATCH net-next v6 1/7] net: dsa: Add tag handling for Hirschmann Hellcreek switches Kurt Kanzenbach
  2020-10-04 11:29 ` [PATCH net-next v6 2/7] net: dsa: Add DSA driver " Kurt Kanzenbach
@ 2020-10-04 11:29 ` Kurt Kanzenbach
  2020-10-04 11:29 ` [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping Kurt Kanzenbach
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-04 11:29 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Kurt Kanzenbach

From: Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>

The switch has internal PTP hardware clocks. Add support for it. There are three
clocks:

 * Synchronized
 * Syntonized
 * Free running

Currently the synchronized clock is exported to user space which is a good
default for the beginning. The free running clock might be exported later
e.g. for implementing 802.1AS-2011/2020 Time Aware Bridges (TAB). The switch
also supports cross time stamping for that purpose.

The implementation adds support setting/getting the time as well as offset and
frequency adjustments. However, the clock only holds a partial timeofday
timestamp. This is why we track the seconds completely in software (see overflow
work and last_ts).

Furthermore, add the PTP multicast addresses into the FDB to forward that
packages only to the CPU port where they are processed by a PTP program.

Signed-off-by: Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>
Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
Acked-by: Richard Cochran <richardcochran@gmail.com>
---
 drivers/net/dsa/hirschmann/Kconfig         |   1 +
 drivers/net/dsa/hirschmann/Makefile        |   4 +-
 drivers/net/dsa/hirschmann/hellcreek.c     |  71 ++++++
 drivers/net/dsa/hirschmann/hellcreek.h     |   8 +
 drivers/net/dsa/hirschmann/hellcreek_ptp.c | 283 +++++++++++++++++++++
 drivers/net/dsa/hirschmann/hellcreek_ptp.h |  69 +++++
 6 files changed, 435 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek_ptp.c
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek_ptp.h

diff --git a/drivers/net/dsa/hirschmann/Kconfig b/drivers/net/dsa/hirschmann/Kconfig
index 7d189cb936e3..222dd35e2c9d 100644
--- a/drivers/net/dsa/hirschmann/Kconfig
+++ b/drivers/net/dsa/hirschmann/Kconfig
@@ -3,6 +3,7 @@ config NET_DSA_HIRSCHMANN_HELLCREEK
 	tristate "Hirschmann Hellcreek TSN Switch support"
 	depends on HAS_IOMEM
 	depends on NET_DSA
+	depends on PTP_1588_CLOCK
 	select NET_DSA_TAG_HELLCREEK
 	help
 	  This driver adds support for Hirschmann Hellcreek TSN switches.
diff --git a/drivers/net/dsa/hirschmann/Makefile b/drivers/net/dsa/hirschmann/Makefile
index 0e12e149e40f..39de02a03640 100644
--- a/drivers/net/dsa/hirschmann/Makefile
+++ b/drivers/net/dsa/hirschmann/Makefile
@@ -1,2 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_NET_DSA_HIRSCHMANN_HELLCREEK)	+= hellcreek.o
+obj-$(CONFIG_NET_DSA_HIRSCHMANN_HELLCREEK)	+= hellcreek_sw.o
+hellcreek_sw-objs := hellcreek.o
+hellcreek_sw-objs += hellcreek_ptp.o
diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
index 2fe080010f8a..3e1040039a96 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.c
+++ b/drivers/net/dsa/hirschmann/hellcreek.c
@@ -24,6 +24,7 @@
 #include <net/dsa.h>
 
 #include "hellcreek.h"
+#include "hellcreek_ptp.h"
 
 static const struct hellcreek_counter hellcreek_counter[] = {
 	{ 0x00, "RxFiltered", },
@@ -1053,6 +1054,43 @@ static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
 	}
 }
 
+static int hellcreek_setup_fdb(struct hellcreek *hellcreek)
+{
+	static struct hellcreek_fdb_entry ptp = {
+		/* MAC: 01-1B-19-00-00-00 */
+		.mac	      = { 0x01, 0x1b, 0x19, 0x00, 0x00, 0x00 },
+		.portmask     = 0x03,	/* Management ports */
+		.age	      = 0,
+		.is_obt	      = 0,
+		.pass_blocked = 0,
+		.is_static    = 1,
+		.reprio_tc    = 6,	/* TC: 6 as per IEEE 802.1AS */
+		.reprio_en    = 1,
+	};
+	static struct hellcreek_fdb_entry p2p = {
+		/* MAC: 01-80-C2-00-00-0E */
+		.mac	      = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e },
+		.portmask     = 0x03,	/* Management ports */
+		.age	      = 0,
+		.is_obt	      = 0,
+		.pass_blocked = 0,
+		.is_static    = 1,
+		.reprio_tc    = 6,	/* TC: 6 as per IEEE 802.1AS */
+		.reprio_en    = 1,
+	};
+	int ret;
+
+	mutex_lock(&hellcreek->reg_lock);
+	ret = __hellcreek_fdb_add(hellcreek, &ptp);
+	if (ret)
+		goto out;
+	ret = __hellcreek_fdb_add(hellcreek, &p2p);
+out:
+	mutex_unlock(&hellcreek->reg_lock);
+
+	return ret;
+}
+
 static int hellcreek_setup(struct dsa_switch *ds)
 {
 	struct hellcreek *hellcreek = ds->priv;
@@ -1098,6 +1136,14 @@ static int hellcreek_setup(struct dsa_switch *ds)
 	/* Allow VLAN configurations while not filtering */
 	ds->configure_vlan_while_not_filtering = true;
 
+	/* Intercept _all_ PTP multicast traffic */
+	ret = hellcreek_setup_fdb(hellcreek);
+	if (ret) {
+		dev_err(hellcreek->dev,
+			"Failed to insert static PTP FDB entries\n");
+		return ret;
+	}
+
 	return 0;
 }
 
@@ -1192,6 +1238,7 @@ static int hellcreek_probe(struct platform_device *pdev)
 	}
 
 	mutex_init(&hellcreek->reg_lock);
+	mutex_init(&hellcreek->ptp_lock);
 
 	hellcreek->dev = dev;
 
@@ -1207,6 +1254,18 @@ static int hellcreek_probe(struct platform_device *pdev)
 		return PTR_ERR(hellcreek->base);
 	}
 
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ptp");
+	if (!res) {
+		dev_err(dev, "No PTP memory region provided!\n");
+		return -ENODEV;
+	}
+
+	hellcreek->ptp_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(hellcreek->ptp_base)) {
+		dev_err(dev, "No memory available!\n");
+		return PTR_ERR(hellcreek->ptp_base);
+	}
+
 	ret = hellcreek_detect(hellcreek);
 	if (ret) {
 		dev_err(dev, "No (known) chip found!\n");
@@ -1237,15 +1296,27 @@ static int hellcreek_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	ret = hellcreek_ptp_setup(hellcreek);
+	if (ret) {
+		dev_err(dev, "Failed to setup PTP!\n");
+		goto err_ptp_setup;
+	}
+
 	platform_set_drvdata(pdev, hellcreek);
 
 	return 0;
+
+err_ptp_setup:
+	dsa_unregister_switch(hellcreek->ds);
+
+	return ret;
 }
 
 static int hellcreek_remove(struct platform_device *pdev)
 {
 	struct hellcreek *hellcreek = platform_get_drvdata(pdev);
 
+	hellcreek_ptp_free(hellcreek);
 	dsa_unregister_switch(hellcreek->ds);
 	platform_set_drvdata(pdev, NULL);
 
diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h
index d57055aadc6f..0c95577b81ad 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.h
+++ b/drivers/net/dsa/hirschmann/hellcreek.h
@@ -16,6 +16,7 @@
 #include <linux/ptp_clock_kernel.h>
 #include <linux/timecounter.h>
 #include <linux/mutex.h>
+#include <linux/workqueue.h>
 #include <linux/platform_data/hirschmann-hellcreek.h>
 #include <net/dsa.h>
 
@@ -245,10 +246,17 @@ struct hellcreek {
 	const struct hellcreek_platform_data *pdata;
 	struct device *dev;
 	struct dsa_switch *ds;
+	struct ptp_clock *ptp_clock;
+	struct ptp_clock_info ptp_clock_info;
 	struct hellcreek_port *ports;
+	struct delayed_work overflow_work;
 	struct mutex reg_lock;	/* Switch IP register lock */
+	struct mutex ptp_lock;	/* PTP IP register lock */
 	void __iomem *base;
+	void __iomem *ptp_base;
 	u8 *vidmbrcfg;		/* vidmbrcfg shadow */
+	u64 seconds;		/* PTP seconds */
+	u64 last_ts;		/* Used for overflow detection */
 	size_t fdb_entries;
 };
 
diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.c b/drivers/net/dsa/hirschmann/hellcreek_ptp.c
new file mode 100644
index 000000000000..856fcb9ba3c6
--- /dev/null
+++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.c
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * DSA driver for:
+ * Hirschmann Hellcreek TSN switch.
+ *
+ * Copyright (C) 2019,2020 Hochschule Offenburg
+ * Copyright (C) 2019,2020 Linutronix GmbH
+ * Authors: Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>
+ *	    Kurt Kanzenbach <kurt@linutronix.de>
+ */
+
+#include <linux/ptp_clock_kernel.h>
+#include "hellcreek.h"
+#include "hellcreek_ptp.h"
+
+static u16 hellcreek_ptp_read(struct hellcreek *hellcreek, unsigned int offset)
+{
+	return readw(hellcreek->ptp_base + offset);
+}
+
+static void hellcreek_ptp_write(struct hellcreek *hellcreek, u16 data,
+				unsigned int offset)
+{
+	writew(data, hellcreek->ptp_base + offset);
+}
+
+/* Get nanoseconds from PTP clock */
+static u64 hellcreek_ptp_clock_read(struct hellcreek *hellcreek)
+{
+	u16 nsl, nsh;
+
+	/* Take a snapshot */
+	hellcreek_ptp_write(hellcreek, PR_COMMAND_C_SS, PR_COMMAND_C);
+
+	/* The time of the day is saved as 96 bits. However, due to hardware
+	 * limitations the seconds are not or only partly kept in the PTP
+	 * core. Currently only three bits for the seconds are available. That's
+	 * why only the nanoseconds are used and the seconds are tracked in
+	 * software. Anyway due to internal locking all five registers should be
+	 * read.
+	 */
+	nsh = hellcreek_ptp_read(hellcreek, PR_SS_SYNC_DATA_C);
+	nsh = hellcreek_ptp_read(hellcreek, PR_SS_SYNC_DATA_C);
+	nsh = hellcreek_ptp_read(hellcreek, PR_SS_SYNC_DATA_C);
+	nsh = hellcreek_ptp_read(hellcreek, PR_SS_SYNC_DATA_C);
+	nsl = hellcreek_ptp_read(hellcreek, PR_SS_SYNC_DATA_C);
+
+	return (u64)nsl | ((u64)nsh << 16);
+}
+
+static u64 __hellcreek_ptp_gettime(struct hellcreek *hellcreek)
+{
+	u64 ns;
+
+	ns = hellcreek_ptp_clock_read(hellcreek);
+	if (ns < hellcreek->last_ts)
+		hellcreek->seconds++;
+	hellcreek->last_ts = ns;
+	ns += hellcreek->seconds * NSEC_PER_SEC;
+
+	return ns;
+}
+
+static int hellcreek_ptp_gettime(struct ptp_clock_info *ptp,
+				 struct timespec64 *ts)
+{
+	struct hellcreek *hellcreek = ptp_to_hellcreek(ptp);
+	u64 ns;
+
+	mutex_lock(&hellcreek->ptp_lock);
+	ns = __hellcreek_ptp_gettime(hellcreek);
+	mutex_unlock(&hellcreek->ptp_lock);
+
+	*ts = ns_to_timespec64(ns);
+
+	return 0;
+}
+
+static int hellcreek_ptp_settime(struct ptp_clock_info *ptp,
+				 const struct timespec64 *ts)
+{
+	struct hellcreek *hellcreek = ptp_to_hellcreek(ptp);
+	u16 secl, nsh, nsl;
+
+	secl = ts->tv_sec & 0xffff;
+	nsh  = ((u32)ts->tv_nsec & 0xffff0000) >> 16;
+	nsl  = ts->tv_nsec & 0xffff;
+
+	mutex_lock(&hellcreek->ptp_lock);
+
+	/* Update overflow data structure */
+	hellcreek->seconds = ts->tv_sec;
+	hellcreek->last_ts = ts->tv_nsec;
+
+	/* Set time in clock */
+	hellcreek_ptp_write(hellcreek, 0x00, PR_CLOCK_WRITE_C);
+	hellcreek_ptp_write(hellcreek, 0x00, PR_CLOCK_WRITE_C);
+	hellcreek_ptp_write(hellcreek, secl, PR_CLOCK_WRITE_C);
+	hellcreek_ptp_write(hellcreek, nsh,  PR_CLOCK_WRITE_C);
+	hellcreek_ptp_write(hellcreek, nsl,  PR_CLOCK_WRITE_C);
+
+	mutex_unlock(&hellcreek->ptp_lock);
+
+	return 0;
+}
+
+static int hellcreek_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+	struct hellcreek *hellcreek = ptp_to_hellcreek(ptp);
+	u16 negative = 0, addendh, addendl;
+	u32 addend;
+	u64 adj;
+
+	if (scaled_ppm < 0) {
+		negative = 1;
+		scaled_ppm = -scaled_ppm;
+	}
+
+	/* IP-Core adjusts the nominal frequency by adding or subtracting 1 ns
+	 * from the 8 ns (period of the oscillator) every time the accumulator
+	 * register overflows. The value stored in the addend register is added
+	 * to the accumulator register every 8 ns.
+	 *
+	 * addend value = (2^30 * accumulator_overflow_rate) /
+	 *                oscillator_frequency
+	 * where:
+	 *
+	 * oscillator_frequency = 125 MHz
+	 * accumulator_overflow_rate = 125 MHz * scaled_ppm * 2^-16 * 10^-6 * 8
+	 */
+	adj = scaled_ppm;
+	adj <<= 11;
+	addend = (u32)div_u64(adj, 15625);
+
+	addendh = (addend & 0xffff0000) >> 16;
+	addendl = addend & 0xffff;
+
+	negative = (negative << 15) & 0x8000;
+
+	mutex_lock(&hellcreek->ptp_lock);
+
+	/* Set drift register */
+	hellcreek_ptp_write(hellcreek, negative, PR_CLOCK_DRIFT_C);
+	hellcreek_ptp_write(hellcreek, 0x00, PR_CLOCK_DRIFT_C);
+	hellcreek_ptp_write(hellcreek, 0x00, PR_CLOCK_DRIFT_C);
+	hellcreek_ptp_write(hellcreek, addendh,  PR_CLOCK_DRIFT_C);
+	hellcreek_ptp_write(hellcreek, addendl,  PR_CLOCK_DRIFT_C);
+
+	mutex_unlock(&hellcreek->ptp_lock);
+
+	return 0;
+}
+
+static int hellcreek_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+	struct hellcreek *hellcreek = ptp_to_hellcreek(ptp);
+	u16 negative = 0, counth, countl;
+	u32 count_val;
+
+	/* If the offset is larger than IP-Core slow offset resources. Don't
+	 * consider slow adjustment. Rather, add the offset directly to the
+	 * current time
+	 */
+	if (abs(delta) > MAX_SLOW_OFFSET_ADJ) {
+		struct timespec64 now, then = ns_to_timespec64(delta);
+
+		hellcreek_ptp_gettime(ptp, &now);
+		now = timespec64_add(now, then);
+		hellcreek_ptp_settime(ptp, &now);
+
+		return 0;
+	}
+
+	if (delta < 0) {
+		negative = 1;
+		delta = -delta;
+	}
+
+	/* 'count_val' does not exceed the maximum register size (2^30) */
+	count_val = div_s64(delta, MAX_NS_PER_STEP);
+
+	counth = (count_val & 0xffff0000) >> 16;
+	countl = count_val & 0xffff;
+
+	negative = (negative << 15) & 0x8000;
+
+	mutex_lock(&hellcreek->ptp_lock);
+
+	/* Set offset write register */
+	hellcreek_ptp_write(hellcreek, negative, PR_CLOCK_OFFSET_C);
+	hellcreek_ptp_write(hellcreek, MAX_NS_PER_STEP, PR_CLOCK_OFFSET_C);
+	hellcreek_ptp_write(hellcreek, MIN_CLK_CYCLES_BETWEEN_STEPS,
+			    PR_CLOCK_OFFSET_C);
+	hellcreek_ptp_write(hellcreek, countl,  PR_CLOCK_OFFSET_C);
+	hellcreek_ptp_write(hellcreek, counth,  PR_CLOCK_OFFSET_C);
+
+	mutex_unlock(&hellcreek->ptp_lock);
+
+	return 0;
+}
+
+static int hellcreek_ptp_enable(struct ptp_clock_info *ptp,
+				struct ptp_clock_request *rq, int on)
+{
+	return -EOPNOTSUPP;
+}
+
+static void hellcreek_ptp_overflow_check(struct work_struct *work)
+{
+	struct delayed_work *dw = to_delayed_work(work);
+	struct hellcreek *hellcreek;
+
+	hellcreek = dw_overflow_to_hellcreek(dw);
+
+	mutex_lock(&hellcreek->ptp_lock);
+	__hellcreek_ptp_gettime(hellcreek);
+	mutex_unlock(&hellcreek->ptp_lock);
+
+	schedule_delayed_work(&hellcreek->overflow_work,
+			      HELLCREEK_OVERFLOW_PERIOD);
+}
+
+int hellcreek_ptp_setup(struct hellcreek *hellcreek)
+{
+	u16 status;
+
+	/* Set up the overflow work */
+	INIT_DELAYED_WORK(&hellcreek->overflow_work,
+			  hellcreek_ptp_overflow_check);
+
+	/* Setup PTP clock */
+	hellcreek->ptp_clock_info.owner = THIS_MODULE;
+	snprintf(hellcreek->ptp_clock_info.name,
+		 sizeof(hellcreek->ptp_clock_info.name),
+		 dev_name(hellcreek->dev));
+
+	/* IP-Core can add up to 0.5 ns per 8 ns cycle, which means
+	 * accumulator_overflow_rate shall not exceed 62.5 MHz (which adjusts
+	 * the nominal frequency by 6.25%)
+	 */
+	hellcreek->ptp_clock_info.max_adj   = 62500000;
+	hellcreek->ptp_clock_info.n_alarm   = 0;
+	hellcreek->ptp_clock_info.n_pins    = 0;
+	hellcreek->ptp_clock_info.n_ext_ts  = 0;
+	hellcreek->ptp_clock_info.n_per_out = 0;
+	hellcreek->ptp_clock_info.pps	    = 0;
+	hellcreek->ptp_clock_info.adjfine   = hellcreek_ptp_adjfine;
+	hellcreek->ptp_clock_info.adjtime   = hellcreek_ptp_adjtime;
+	hellcreek->ptp_clock_info.gettime64 = hellcreek_ptp_gettime;
+	hellcreek->ptp_clock_info.settime64 = hellcreek_ptp_settime;
+	hellcreek->ptp_clock_info.enable    = hellcreek_ptp_enable;
+
+	hellcreek->ptp_clock = ptp_clock_register(&hellcreek->ptp_clock_info,
+						  hellcreek->dev);
+	if (IS_ERR(hellcreek->ptp_clock))
+		return PTR_ERR(hellcreek->ptp_clock);
+
+	/* Enable the offset correction process, if no offset correction is
+	 * already taking place
+	 */
+	status = hellcreek_ptp_read(hellcreek, PR_CLOCK_STATUS_C);
+	if (!(status & PR_CLOCK_STATUS_C_OFS_ACT))
+		hellcreek_ptp_write(hellcreek,
+				    status | PR_CLOCK_STATUS_C_ENA_OFS,
+				    PR_CLOCK_STATUS_C);
+
+	/* Enable the drift correction process */
+	hellcreek_ptp_write(hellcreek, status | PR_CLOCK_STATUS_C_ENA_DRIFT,
+			    PR_CLOCK_STATUS_C);
+
+	schedule_delayed_work(&hellcreek->overflow_work,
+			      HELLCREEK_OVERFLOW_PERIOD);
+
+	return 0;
+}
+
+void hellcreek_ptp_free(struct hellcreek *hellcreek)
+{
+	cancel_delayed_work_sync(&hellcreek->overflow_work);
+	if (hellcreek->ptp_clock)
+		ptp_clock_unregister(hellcreek->ptp_clock);
+	hellcreek->ptp_clock = NULL;
+}
diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.h b/drivers/net/dsa/hirschmann/hellcreek_ptp.h
new file mode 100644
index 000000000000..2dd8aaa532d0
--- /dev/null
+++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * DSA driver for:
+ * Hirschmann Hellcreek TSN switch.
+ *
+ * Copyright (C) 2019,2020 Hochschule Offenburg
+ * Copyright (C) 2019,2020 Linutronix GmbH
+ * Authors: Kurt Kanzenbach <kurt@linutronix.de>
+ *	    Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>
+ */
+
+#ifndef _HELLCREEK_PTP_H_
+#define _HELLCREEK_PTP_H_
+
+#include <linux/bitops.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include "hellcreek.h"
+
+/* Every jump in time is 7 ns */
+#define MAX_NS_PER_STEP			7L
+
+/* Correct offset at every clock cycle */
+#define MIN_CLK_CYCLES_BETWEEN_STEPS	0
+
+/* Maximum available slow offset resources */
+#define MAX_SLOW_OFFSET_ADJ					\
+	((unsigned long long)((1 << 30) - 1) * MAX_NS_PER_STEP)
+
+/* four times a second overflow check */
+#define HELLCREEK_OVERFLOW_PERIOD	(HZ / 4)
+
+/* PTP Register */
+#define PR_SETTINGS_C			(0x09 * 2)
+#define PR_SETTINGS_C_RES3TS		BIT(4)
+#define PR_SETTINGS_C_TS_SRC_TK_SHIFT	8
+#define PR_SETTINGS_C_TS_SRC_TK_MASK	GENMASK(9, 8)
+#define PR_COMMAND_C			(0x0a * 2)
+#define PR_COMMAND_C_SS			BIT(0)
+
+#define PR_CLOCK_STATUS_C		(0x0c * 2)
+#define PR_CLOCK_STATUS_C_ENA_DRIFT	BIT(12)
+#define PR_CLOCK_STATUS_C_OFS_ACT	BIT(13)
+#define PR_CLOCK_STATUS_C_ENA_OFS	BIT(14)
+
+#define PR_CLOCK_READ_C			(0x0d * 2)
+#define PR_CLOCK_WRITE_C		(0x0e * 2)
+#define PR_CLOCK_OFFSET_C		(0x0f * 2)
+#define PR_CLOCK_DRIFT_C		(0x10 * 2)
+
+#define PR_SS_FREE_DATA_C		(0x12 * 2)
+#define PR_SS_SYNT_DATA_C		(0x14 * 2)
+#define PR_SS_SYNC_DATA_C		(0x16 * 2)
+#define PR_SS_DRAC_DATA_C		(0x18 * 2)
+
+#define STATUS_OUT			(0x60 * 2)
+#define STATUS_OUT_SYNC_GOOD		BIT(0)
+#define STATUS_OUT_IS_GM		BIT(1)
+
+int hellcreek_ptp_setup(struct hellcreek *hellcreek);
+void hellcreek_ptp_free(struct hellcreek *hellcreek);
+
+#define ptp_to_hellcreek(ptp)					\
+	container_of(ptp, struct hellcreek, ptp_clock_info)
+
+#define dw_overflow_to_hellcreek(dw)				\
+	container_of(dw, struct hellcreek, overflow_work)
+
+#endif /* _HELLCREEK_PTP_H_ */
-- 
2.20.1


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

* [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-04 11:29 [PATCH net-next v6 0/7] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
                   ` (2 preceding siblings ...)
  2020-10-04 11:29 ` [PATCH net-next v6 3/7] net: dsa: hellcreek: Add PTP clock support Kurt Kanzenbach
@ 2020-10-04 11:29 ` Kurt Kanzenbach
  2020-10-04 14:30   ` Vladimir Oltean
  2020-10-04 11:29 ` [PATCH net-next v6 5/7] net: dsa: hellcreek: Add PTP status LEDs Kurt Kanzenbach
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-04 11:29 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Kurt Kanzenbach

From: Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>

The switch has the ability to take hardware generated time stamps per port for
PTPv2 event messages in Rx and Tx direction. That is useful for achieving needed
time synchronization precision for TSN devices/switches. So add support for it.

There are two directions:

 * RX

   The switch has a single register per port to capture a timestamp. That
   mechanism is not used due to correlation problems. If the software processing
   is too slow and a PTPv2 event message is received before the previous one has
   been processed, false timestamps will be captured. Therefore, the switch can
   do "inline" timestamping which means it can insert the nanoseconds part of
   the timestamp directly into the PTPv2 event message. The reserved field (4
   bytes) is leveraged for that. This might not be in accordance with (older)
   PTP standards, but is the only way to get reliable results.

 * TX

   In Tx direction there is no correlation problem, because the software and the
   driver has to ensure that only one event message is "on the fly". However,
   the switch provides also a mechanism to check whether a timestamp is
   lost. That can only happen when a timestamp is read and at this point another
   message is timestamped. So, that lost bit is checked just in case to indicate
   to the user that the driver or the software is somewhat buggy.

Signed-off-by: Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>
Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
Acked-by: Richard Cochran <richardcochran@gmail.com>
---
 drivers/net/dsa/hirschmann/Makefile           |   1 +
 drivers/net/dsa/hirschmann/hellcreek.c        |  15 +
 drivers/net/dsa/hirschmann/hellcreek.h        |  25 +
 .../net/dsa/hirschmann/hellcreek_hwtstamp.c   | 479 ++++++++++++++++++
 .../net/dsa/hirschmann/hellcreek_hwtstamp.h   |  58 +++
 drivers/net/dsa/hirschmann/hellcreek_ptp.c    |  48 +-
 drivers/net/dsa/hirschmann/hellcreek_ptp.h    |   4 +
 7 files changed, 616 insertions(+), 14 deletions(-)
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c
 create mode 100644 drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h

diff --git a/drivers/net/dsa/hirschmann/Makefile b/drivers/net/dsa/hirschmann/Makefile
index 39de02a03640..f4075c2998b5 100644
--- a/drivers/net/dsa/hirschmann/Makefile
+++ b/drivers/net/dsa/hirschmann/Makefile
@@ -2,3 +2,4 @@
 obj-$(CONFIG_NET_DSA_HIRSCHMANN_HELLCREEK)	+= hellcreek_sw.o
 hellcreek_sw-objs := hellcreek.o
 hellcreek_sw-objs += hellcreek_ptp.o
+hellcreek_sw-objs += hellcreek_hwtstamp.o
diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
index 3e1040039a96..e9ec9dcca0d8 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.c
+++ b/drivers/net/dsa/hirschmann/hellcreek.c
@@ -25,6 +25,7 @@
 
 #include "hellcreek.h"
 #include "hellcreek_ptp.h"
+#include "hellcreek_hwtstamp.h"
 
 static const struct hellcreek_counter hellcreek_counter[] = {
 	{ 0x00, "RxFiltered", },
@@ -1191,6 +1192,11 @@ static const struct dsa_switch_ops hellcreek_ds_ops = {
 	.port_bridge_leave   = hellcreek_port_bridge_leave,
 	.port_stp_state_set  = hellcreek_port_stp_state_set,
 	.phylink_validate    = hellcreek_phylink_validate,
+	.port_hwtstamp_set   = hellcreek_port_hwtstamp_set,
+	.port_hwtstamp_get   = hellcreek_port_hwtstamp_get,
+	.port_txtstamp	     = hellcreek_port_txtstamp,
+	.port_rxtstamp	     = hellcreek_port_rxtstamp,
+	.get_ts_info	     = hellcreek_get_ts_info,
 };
 
 static int hellcreek_probe(struct platform_device *pdev)
@@ -1302,10 +1308,18 @@ static int hellcreek_probe(struct platform_device *pdev)
 		goto err_ptp_setup;
 	}
 
+	ret = hellcreek_hwtstamp_setup(hellcreek);
+	if (ret) {
+		dev_err(dev, "Failed to setup hardware timestamping!\n");
+		goto err_tstamp_setup;
+	}
+
 	platform_set_drvdata(pdev, hellcreek);
 
 	return 0;
 
+err_tstamp_setup:
+	hellcreek_ptp_free(hellcreek);
 err_ptp_setup:
 	dsa_unregister_switch(hellcreek->ds);
 
@@ -1316,6 +1330,7 @@ static int hellcreek_remove(struct platform_device *pdev)
 {
 	struct hellcreek *hellcreek = platform_get_drvdata(pdev);
 
+	hellcreek_hwtstamp_free(hellcreek);
 	hellcreek_ptp_free(hellcreek);
 	dsa_unregister_switch(hellcreek->ds);
 	platform_set_drvdata(pdev, NULL);
diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h
index 0c95577b81ad..0b42c15736ce 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.h
+++ b/drivers/net/dsa/hirschmann/hellcreek.h
@@ -213,6 +213,28 @@ struct hellcreek_counter {
 
 struct hellcreek;
 
+/* State flags for hellcreek_port_hwtstamp::state */
+enum {
+	HELLCREEK_HWTSTAMP_ENABLED,
+	HELLCREEK_HWTSTAMP_TX_IN_PROGRESS,
+};
+
+/* A structure to hold hardware timestamping information per port */
+struct hellcreek_port_hwtstamp {
+	/* Timestamping state */
+	unsigned long state;
+
+	/* Resources for receive timestamping */
+	struct sk_buff_head rx_queue; /* For synchronization messages */
+
+	/* Resources for transmit timestamping */
+	unsigned long tx_tstamp_start;
+	struct sk_buff *tx_skb;
+
+	/* Current timestamp configuration */
+	struct hwtstamp_config tstamp_config;
+};
+
 struct hellcreek_port {
 	struct hellcreek *hellcreek;
 	struct list_head vlan_list;
@@ -221,6 +243,9 @@ struct hellcreek_port {
 	int vlan_filtering;	/* Is VLAN filtering activated */
 	u16 ptcfg;		/* ptcfg shadow */
 	u64 *counter_values;
+
+	/* Per-port timestamping resources */
+	struct hellcreek_port_hwtstamp port_hwtstamp;
 };
 
 struct hellcreek_fdb_entry {
diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c
new file mode 100644
index 000000000000..69dd9a2e8bb6
--- /dev/null
+++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c
@@ -0,0 +1,479 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * DSA driver for:
+ * Hirschmann Hellcreek TSN switch.
+ *
+ * Copyright (C) 2019,2020 Hochschule Offenburg
+ * Copyright (C) 2019,2020 Linutronix GmbH
+ * Authors: Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>
+ *	    Kurt Kanzenbach <kurt@linutronix.de>
+ */
+
+#include <linux/ptp_classify.h>
+
+#include "hellcreek.h"
+#include "hellcreek_hwtstamp.h"
+#include "hellcreek_ptp.h"
+
+int hellcreek_get_ts_info(struct dsa_switch *ds, int port,
+			  struct ethtool_ts_info *info)
+{
+	struct hellcreek *hellcreek = ds->priv;
+
+	info->phc_index = hellcreek->ptp_clock ?
+		ptp_clock_index(hellcreek->ptp_clock) : -1;
+	info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+		SOF_TIMESTAMPING_RX_HARDWARE |
+		SOF_TIMESTAMPING_RAW_HARDWARE;
+
+	/* enabled tx timestamping */
+	info->tx_types = BIT(HWTSTAMP_TX_ON);
+
+	/* L2 & L4 PTPv2 event rx messages are timestamped */
+	info->rx_filters = BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
+
+	return 0;
+}
+
+/* Enabling/disabling TX and RX HW timestamping for different PTP messages is
+ * not available in the switch. Thus, this function only serves as a check if
+ * the user requested what is actually available or not
+ */
+static int hellcreek_set_hwtstamp_config(struct hellcreek *hellcreek, int port,
+					 struct hwtstamp_config *config)
+{
+	struct hellcreek_port_hwtstamp *ps =
+		&hellcreek->ports[port].port_hwtstamp;
+	bool tx_tstamp_enable = false;
+	bool rx_tstamp_enable = false;
+
+	/* Interaction with the timestamp hardware is prevented here.  It is
+	 * enabled when this config function ends successfully
+	 */
+	clear_bit_unlock(HELLCREEK_HWTSTAMP_ENABLED, &ps->state);
+
+	/* Reserved for future extensions */
+	if (config->flags)
+		return -EINVAL;
+
+	switch (config->tx_type) {
+	case HWTSTAMP_TX_ON:
+		tx_tstamp_enable = true;
+		break;
+
+	/* TX HW timestamping can't be disabled on the switch */
+	case HWTSTAMP_TX_OFF:
+		config->tx_type = HWTSTAMP_TX_ON;
+		break;
+
+	default:
+		return -ERANGE;
+	}
+
+	switch (config->rx_filter) {
+	/* RX HW timestamping can't be disabled on the switch */
+	case HWTSTAMP_FILTER_NONE:
+		config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+		break;
+
+	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+	case HWTSTAMP_FILTER_PTP_V2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+		config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+		rx_tstamp_enable = true;
+		break;
+
+	/* RX HW timestamping can't be enabled for all messages on the switch */
+	case HWTSTAMP_FILTER_ALL:
+		config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+		break;
+
+	default:
+		return -ERANGE;
+	}
+
+	if (!tx_tstamp_enable)
+		return -ERANGE;
+
+	if (!rx_tstamp_enable)
+		return -ERANGE;
+
+	/* If this point is reached, then the requested hwtstamp config is
+	 * compatible with the hwtstamp offered by the switch.  Therefore,
+	 * enable the interaction with the HW timestamping
+	 */
+	set_bit(HELLCREEK_HWTSTAMP_ENABLED, &ps->state);
+
+	return 0;
+}
+
+int hellcreek_port_hwtstamp_set(struct dsa_switch *ds, int port,
+				struct ifreq *ifr)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	struct hellcreek_port_hwtstamp *ps;
+	struct hwtstamp_config config;
+	int err;
+
+	ps = &hellcreek->ports[port].port_hwtstamp;
+
+	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+		return -EFAULT;
+
+	err = hellcreek_set_hwtstamp_config(hellcreek, port, &config);
+	if (err)
+		return err;
+
+	/* Save the chosen configuration to be returned later */
+	memcpy(&ps->tstamp_config, &config, sizeof(config));
+
+	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+		-EFAULT : 0;
+}
+
+int hellcreek_port_hwtstamp_get(struct dsa_switch *ds, int port,
+				struct ifreq *ifr)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	struct hellcreek_port_hwtstamp *ps;
+	struct hwtstamp_config *config;
+
+	ps = &hellcreek->ports[port].port_hwtstamp;
+	config = &ps->tstamp_config;
+
+	return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
+		-EFAULT : 0;
+}
+
+/* Returns a pointer to the PTP header if the caller should time stamp, or NULL
+ * if the caller should not.
+ */
+static struct ptp_header *hellcreek_should_tstamp(struct hellcreek *hellcreek,
+						  int port, struct sk_buff *skb,
+						  unsigned int type)
+{
+	struct hellcreek_port_hwtstamp *ps =
+		&hellcreek->ports[port].port_hwtstamp;
+	struct ptp_header *hdr;
+
+	hdr = ptp_parse_header(skb, type);
+	if (!hdr)
+		return NULL;
+
+	if (!test_bit(HELLCREEK_HWTSTAMP_ENABLED, &ps->state))
+		return NULL;
+
+	return hdr;
+}
+
+static u64 hellcreek_get_reserved_field(const struct ptp_header *hdr)
+{
+	return be32_to_cpu(hdr->reserved2);
+}
+
+static void hellcreek_clear_reserved_field(struct ptp_header *hdr)
+{
+	hdr->reserved2 = 0;
+}
+
+static int hellcreek_ptp_hwtstamp_available(struct hellcreek *hellcreek,
+					    unsigned int ts_reg)
+{
+	u16 status;
+
+	status = hellcreek_ptp_read(hellcreek, ts_reg);
+
+	if (status & PR_TS_STATUS_TS_LOST)
+		dev_err(hellcreek->dev,
+			"Tx time stamp lost! This should never happen!\n");
+
+	/* If hwtstamp is not available, this means the previous hwtstamp was
+	 * successfully read, and the one we need is not yet available
+	 */
+	return (status & PR_TS_STATUS_TS_AVAIL) ? 1 : 0;
+}
+
+/* Get nanoseconds timestamp from timestamping unit */
+static u64 hellcreek_ptp_hwtstamp_read(struct hellcreek *hellcreek,
+				       unsigned int ts_reg)
+{
+	u16 nsl, nsh;
+
+	nsh = hellcreek_ptp_read(hellcreek, ts_reg);
+	nsh = hellcreek_ptp_read(hellcreek, ts_reg);
+	nsh = hellcreek_ptp_read(hellcreek, ts_reg);
+	nsh = hellcreek_ptp_read(hellcreek, ts_reg);
+	nsl = hellcreek_ptp_read(hellcreek, ts_reg);
+
+	return (u64)nsl | ((u64)nsh << 16);
+}
+
+static int hellcreek_txtstamp_work(struct hellcreek *hellcreek,
+				   struct hellcreek_port_hwtstamp *ps, int port)
+{
+	struct skb_shared_hwtstamps shhwtstamps;
+	unsigned int status_reg, data_reg;
+	struct sk_buff *tmp_skb;
+	int ts_status;
+	u64 ns = 0;
+
+	if (!ps->tx_skb)
+		return 0;
+
+	switch (port) {
+	case 2:
+		status_reg = PR_TS_TX_P1_STATUS_C;
+		data_reg   = PR_TS_TX_P1_DATA_C;
+		break;
+	case 3:
+		status_reg = PR_TS_TX_P2_STATUS_C;
+		data_reg   = PR_TS_TX_P2_DATA_C;
+		break;
+	default:
+		dev_err(hellcreek->dev, "Wrong port for timestamping!\n");
+		return 0;
+	}
+
+	ts_status = hellcreek_ptp_hwtstamp_available(hellcreek, status_reg);
+
+	/* Not available yet? */
+	if (ts_status == 0) {
+		/* Check whether the operation of reading the tx timestamp has
+		 * exceeded its allowed period
+		 */
+		if (time_is_before_jiffies(ps->tx_tstamp_start +
+					   TX_TSTAMP_TIMEOUT)) {
+			dev_err(hellcreek->dev,
+				"Timeout while waiting for Tx timestamp!\n");
+			goto free_and_clear_skb;
+		}
+
+		/* The timestamp should be available quickly, while getting it
+		 * in high priority. Restart the work
+		 */
+		return 1;
+	}
+
+	mutex_lock(&hellcreek->ptp_lock);
+	ns  = hellcreek_ptp_hwtstamp_read(hellcreek, data_reg);
+	ns += hellcreek_ptp_gettime_seconds(hellcreek, ns);
+	mutex_unlock(&hellcreek->ptp_lock);
+
+	/* Now we have the timestamp in nanoseconds, store it in the correct
+	 * structure in order to send it to the user
+	 */
+	memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+	shhwtstamps.hwtstamp = ns_to_ktime(ns);
+
+	tmp_skb = ps->tx_skb;
+	ps->tx_skb = NULL;
+
+	/* skb_complete_tx_timestamp() frees up the client to make another
+	 * timestampable transmit.  We have to be ready for it by clearing the
+	 * ps->tx_skb "flag" beforehand
+	 */
+	clear_bit_unlock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS, &ps->state);
+
+	/* Deliver a clone of the original outgoing tx_skb with tx hwtstamp */
+	skb_complete_tx_timestamp(tmp_skb, &shhwtstamps);
+
+	return 0;
+
+free_and_clear_skb:
+	dev_kfree_skb_any(ps->tx_skb);
+	ps->tx_skb = NULL;
+	clear_bit_unlock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS, &ps->state);
+
+	return 0;
+}
+
+static void hellcreek_get_rxts(struct hellcreek *hellcreek,
+			       struct hellcreek_port_hwtstamp *ps,
+			       struct sk_buff *skb, struct sk_buff_head *rxq,
+			       int port)
+{
+	struct skb_shared_hwtstamps *shwt;
+	struct sk_buff_head received;
+	unsigned long flags;
+
+	/* The latched timestamp belongs to one of the received frames. */
+	__skb_queue_head_init(&received);
+
+	/* Lock & disable interrupts */
+	spin_lock_irqsave(&rxq->lock, flags);
+
+	/* Add the reception queue "rxq" to the "received" queue an reintialize
+	 * "rxq".  From now on, we deal with "received" not with "rxq"
+	 */
+	skb_queue_splice_tail_init(rxq, &received);
+
+	spin_unlock_irqrestore(&rxq->lock, flags);
+
+	for (; skb; skb = __skb_dequeue(&received)) {
+		struct ptp_header *hdr;
+		unsigned int type;
+		u64 ns;
+
+		/* Get nanoseconds from ptp packet */
+		type = SKB_PTP_TYPE(skb);
+		hdr  = ptp_parse_header(skb, type);
+		ns   = hellcreek_get_reserved_field(hdr);
+		hellcreek_clear_reserved_field(hdr);
+
+		/* Add seconds part */
+		mutex_lock(&hellcreek->ptp_lock);
+		ns += hellcreek_ptp_gettime_seconds(hellcreek, ns);
+		mutex_unlock(&hellcreek->ptp_lock);
+
+		/* Save time stamp */
+		shwt = skb_hwtstamps(skb);
+		memset(shwt, 0, sizeof(*shwt));
+		shwt->hwtstamp = ns_to_ktime(ns);
+		netif_rx_ni(skb);
+	}
+}
+
+static void hellcreek_rxtstamp_work(struct hellcreek *hellcreek,
+				    struct hellcreek_port_hwtstamp *ps,
+				    int port)
+{
+	struct sk_buff *skb;
+
+	skb = skb_dequeue(&ps->rx_queue);
+	if (skb)
+		hellcreek_get_rxts(hellcreek, ps, skb, &ps->rx_queue, port);
+}
+
+long hellcreek_hwtstamp_work(struct ptp_clock_info *ptp)
+{
+	struct hellcreek *hellcreek = ptp_to_hellcreek(ptp);
+	struct dsa_switch *ds = hellcreek->ds;
+	int i, restart = 0;
+
+	for (i = 0; i < ds->num_ports; i++) {
+		struct hellcreek_port_hwtstamp *ps;
+
+		if (!dsa_is_user_port(ds, i))
+			continue;
+
+		ps = &hellcreek->ports[i].port_hwtstamp;
+
+		if (test_bit(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS, &ps->state))
+			restart |= hellcreek_txtstamp_work(hellcreek, ps, i);
+
+		hellcreek_rxtstamp_work(hellcreek, ps, i);
+	}
+
+	return restart ? 1 : -1;
+}
+
+bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
+			     struct sk_buff *clone, unsigned int type)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	struct hellcreek_port_hwtstamp *ps;
+	struct ptp_header *hdr;
+
+	ps = &hellcreek->ports[port].port_hwtstamp;
+
+	/* Check if the driver is expected to do HW timestamping */
+	if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP))
+		return false;
+
+	/* Make sure the message is a PTP message that needs to be timestamped
+	 * and the interaction with the HW timestamping is enabled. If not, stop
+	 * here
+	 */
+	hdr = hellcreek_should_tstamp(hellcreek, port, clone, type);
+	if (!hdr)
+		return false;
+
+	if (test_and_set_bit_lock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS,
+				  &ps->state))
+		return false;
+
+	ps->tx_skb = clone;
+
+	/* store the number of ticks occurred since system start-up till this
+	 * moment
+	 */
+	ps->tx_tstamp_start = jiffies;
+
+	ptp_schedule_worker(hellcreek->ptp_clock, 0);
+
+	return true;
+}
+
+bool hellcreek_port_rxtstamp(struct dsa_switch *ds, int port,
+			     struct sk_buff *skb, unsigned int type)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	struct hellcreek_port_hwtstamp *ps;
+	struct ptp_header *hdr;
+
+	ps = &hellcreek->ports[port].port_hwtstamp;
+
+	/* This check only fails if the user did not initialize hardware
+	 * timestamping beforehand.
+	 */
+	if (ps->tstamp_config.rx_filter != HWTSTAMP_FILTER_PTP_V2_EVENT)
+		return false;
+
+	/* Make sure the message is a PTP message that needs to be timestamped
+	 * and the interaction with the HW timestamping is enabled. If not, stop
+	 * here
+	 */
+	hdr = hellcreek_should_tstamp(hellcreek, port, skb, type);
+	if (!hdr)
+		return false;
+
+	SKB_PTP_TYPE(skb) = type;
+
+	skb_queue_tail(&ps->rx_queue, skb);
+
+	ptp_schedule_worker(hellcreek->ptp_clock, 0);
+
+	return true;
+}
+
+static void hellcreek_hwtstamp_port_setup(struct hellcreek *hellcreek, int port)
+{
+	struct hellcreek_port_hwtstamp *ps =
+		&hellcreek->ports[port].port_hwtstamp;
+
+	skb_queue_head_init(&ps->rx_queue);
+}
+
+int hellcreek_hwtstamp_setup(struct hellcreek *hellcreek)
+{
+	struct dsa_switch *ds = hellcreek->ds;
+	int i;
+
+	/* Initialize timestamping ports. */
+	for (i = 0; i < ds->num_ports; ++i) {
+		if (!dsa_is_user_port(ds, i))
+			continue;
+
+		hellcreek_hwtstamp_port_setup(hellcreek, i);
+	}
+
+	/* Select the synchronized clock as the source timekeeper for the
+	 * timestamps and enable inline timestamping.
+	 */
+	hellcreek_ptp_write(hellcreek, PR_SETTINGS_C_TS_SRC_TK_MASK |
+			    PR_SETTINGS_C_RES3TS,
+			    PR_SETTINGS_C);
+
+	return 0;
+}
+
+void hellcreek_hwtstamp_free(struct hellcreek *hellcreek)
+{
+	/* Nothing todo */
+}
diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h
new file mode 100644
index 000000000000..c0745ffa1ebb
--- /dev/null
+++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * DSA driver for:
+ * Hirschmann Hellcreek TSN switch.
+ *
+ * Copyright (C) 2019,2020 Hochschule Offenburg
+ * Copyright (C) 2019,2020 Linutronix GmbH
+ * Authors: Kurt Kanzenbach <kurt@linutronix.de>
+ *	    Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>
+ */
+
+#ifndef _HELLCREEK_HWTSTAMP_H_
+#define _HELLCREEK_HWTSTAMP_H_
+
+#include <net/dsa.h>
+#include "hellcreek.h"
+
+/* Timestamp Register */
+#define PR_TS_RX_P1_STATUS_C	(0x1d * 2)
+#define PR_TS_RX_P1_DATA_C	(0x1e * 2)
+#define PR_TS_TX_P1_STATUS_C	(0x1f * 2)
+#define PR_TS_TX_P1_DATA_C	(0x20 * 2)
+#define PR_TS_RX_P2_STATUS_C	(0x25 * 2)
+#define PR_TS_RX_P2_DATA_C	(0x26 * 2)
+#define PR_TS_TX_P2_STATUS_C	(0x27 * 2)
+#define PR_TS_TX_P2_DATA_C	(0x28 * 2)
+
+#define PR_TS_STATUS_TS_AVAIL	BIT(2)
+#define PR_TS_STATUS_TS_LOST	BIT(3)
+
+#define SKB_PTP_TYPE(__skb) (*(unsigned int *)((__skb)->cb))
+
+/* TX_TSTAMP_TIMEOUT: This limits the time spent polling for a TX
+ * timestamp. When working properly, hardware will produce a timestamp
+ * within 1ms. Software may enounter delays, so the timeout is set
+ * accordingly.
+ */
+#define TX_TSTAMP_TIMEOUT	msecs_to_jiffies(40)
+
+int hellcreek_port_hwtstamp_set(struct dsa_switch *ds, int port,
+				struct ifreq *ifr);
+int hellcreek_port_hwtstamp_get(struct dsa_switch *ds, int port,
+				struct ifreq *ifr);
+
+bool hellcreek_port_rxtstamp(struct dsa_switch *ds, int port,
+			     struct sk_buff *clone, unsigned int type);
+bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
+			     struct sk_buff *clone, unsigned int type);
+
+int hellcreek_get_ts_info(struct dsa_switch *ds, int port,
+			  struct ethtool_ts_info *info);
+
+long hellcreek_hwtstamp_work(struct ptp_clock_info *ptp);
+
+int hellcreek_hwtstamp_setup(struct hellcreek *chip);
+void hellcreek_hwtstamp_free(struct hellcreek *chip);
+
+#endif /* _HELLCREEK_HWTSTAMP_H_ */
diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.c b/drivers/net/dsa/hirschmann/hellcreek_ptp.c
index 856fcb9ba3c6..12ad956abd5c 100644
--- a/drivers/net/dsa/hirschmann/hellcreek_ptp.c
+++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.c
@@ -12,14 +12,15 @@
 #include <linux/ptp_clock_kernel.h>
 #include "hellcreek.h"
 #include "hellcreek_ptp.h"
+#include "hellcreek_hwtstamp.h"
 
-static u16 hellcreek_ptp_read(struct hellcreek *hellcreek, unsigned int offset)
+u16 hellcreek_ptp_read(struct hellcreek *hellcreek, unsigned int offset)
 {
 	return readw(hellcreek->ptp_base + offset);
 }
 
-static void hellcreek_ptp_write(struct hellcreek *hellcreek, u16 data,
-				unsigned int offset)
+void hellcreek_ptp_write(struct hellcreek *hellcreek, u16 data,
+			 unsigned int offset)
 {
 	writew(data, hellcreek->ptp_base + offset);
 }
@@ -61,6 +62,24 @@ static u64 __hellcreek_ptp_gettime(struct hellcreek *hellcreek)
 	return ns;
 }
 
+/* Retrieve the seconds parts in nanoseconds for a packet timestamped with @ns.
+ * There has to be a check whether an overflow occurred between the packet
+ * arrival and now. If so use the correct seconds (-1) for calculating the
+ * packet arrival time.
+ */
+u64 hellcreek_ptp_gettime_seconds(struct hellcreek *hellcreek, u64 ns)
+{
+	u64 s;
+
+	__hellcreek_ptp_gettime(hellcreek);
+	if (hellcreek->last_ts > ns)
+		s = hellcreek->seconds * NSEC_PER_SEC;
+	else
+		s = (hellcreek->seconds - 1) * NSEC_PER_SEC;
+
+	return s;
+}
+
 static int hellcreek_ptp_gettime(struct ptp_clock_info *ptp,
 				 struct timespec64 *ts)
 {
@@ -238,17 +257,18 @@ int hellcreek_ptp_setup(struct hellcreek *hellcreek)
 	 * accumulator_overflow_rate shall not exceed 62.5 MHz (which adjusts
 	 * the nominal frequency by 6.25%)
 	 */
-	hellcreek->ptp_clock_info.max_adj   = 62500000;
-	hellcreek->ptp_clock_info.n_alarm   = 0;
-	hellcreek->ptp_clock_info.n_pins    = 0;
-	hellcreek->ptp_clock_info.n_ext_ts  = 0;
-	hellcreek->ptp_clock_info.n_per_out = 0;
-	hellcreek->ptp_clock_info.pps	    = 0;
-	hellcreek->ptp_clock_info.adjfine   = hellcreek_ptp_adjfine;
-	hellcreek->ptp_clock_info.adjtime   = hellcreek_ptp_adjtime;
-	hellcreek->ptp_clock_info.gettime64 = hellcreek_ptp_gettime;
-	hellcreek->ptp_clock_info.settime64 = hellcreek_ptp_settime;
-	hellcreek->ptp_clock_info.enable    = hellcreek_ptp_enable;
+	hellcreek->ptp_clock_info.max_adj     = 62500000;
+	hellcreek->ptp_clock_info.n_alarm     = 0;
+	hellcreek->ptp_clock_info.n_pins      = 0;
+	hellcreek->ptp_clock_info.n_ext_ts    = 0;
+	hellcreek->ptp_clock_info.n_per_out   = 0;
+	hellcreek->ptp_clock_info.pps	      = 0;
+	hellcreek->ptp_clock_info.adjfine     = hellcreek_ptp_adjfine;
+	hellcreek->ptp_clock_info.adjtime     = hellcreek_ptp_adjtime;
+	hellcreek->ptp_clock_info.gettime64   = hellcreek_ptp_gettime;
+	hellcreek->ptp_clock_info.settime64   = hellcreek_ptp_settime;
+	hellcreek->ptp_clock_info.enable      = hellcreek_ptp_enable;
+	hellcreek->ptp_clock_info.do_aux_work = hellcreek_hwtstamp_work;
 
 	hellcreek->ptp_clock = ptp_clock_register(&hellcreek->ptp_clock_info,
 						  hellcreek->dev);
diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.h b/drivers/net/dsa/hirschmann/hellcreek_ptp.h
index 2dd8aaa532d0..e0eca1f4a494 100644
--- a/drivers/net/dsa/hirschmann/hellcreek_ptp.h
+++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.h
@@ -59,6 +59,10 @@
 
 int hellcreek_ptp_setup(struct hellcreek *hellcreek);
 void hellcreek_ptp_free(struct hellcreek *hellcreek);
+u16 hellcreek_ptp_read(struct hellcreek *hellcreek, unsigned int offset);
+void hellcreek_ptp_write(struct hellcreek *hellcreek, u16 data,
+			 unsigned int offset);
+u64 hellcreek_ptp_gettime_seconds(struct hellcreek *hellcreek, u64 ns);
 
 #define ptp_to_hellcreek(ptp)					\
 	container_of(ptp, struct hellcreek, ptp_clock_info)
-- 
2.20.1


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

* [PATCH net-next v6 5/7] net: dsa: hellcreek: Add PTP status LEDs
  2020-10-04 11:29 [PATCH net-next v6 0/7] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
                   ` (3 preceding siblings ...)
  2020-10-04 11:29 ` [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping Kurt Kanzenbach
@ 2020-10-04 11:29 ` Kurt Kanzenbach
  2020-10-04 11:29 ` [PATCH net-next v6 6/7] dt-bindings: Add vendor prefix for Hirschmann Kurt Kanzenbach
  2020-10-04 11:29 ` [PATCH net-next v6 7/7] dt-bindings: net: dsa: Add documentation for Hellcreek switches Kurt Kanzenbach
  6 siblings, 0 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-04 11:29 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Kurt Kanzenbach

The switch has two controllable I/Os which are usually connected to LEDs. This
is useful to immediately visually see the PTP status.

These provide two signals:

 * is_gm

   This LED can be activated if the current device is the grand master in that
   PTP domain.

 * sync_good

   This LED can be activated if the current device is in sync with the network
   time.

Expose these via the LED framework to be controlled via user space
e.g. linuxptp.

Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
 drivers/net/dsa/hirschmann/hellcreek.h     |   4 +
 drivers/net/dsa/hirschmann/hellcreek_ptp.c | 149 +++++++++++++++++++++
 drivers/net/dsa/hirschmann/hellcreek_ptp.h |   3 +
 3 files changed, 156 insertions(+)

diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h
index 0b42c15736ce..baf60ee69125 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.h
+++ b/drivers/net/dsa/hirschmann/hellcreek.h
@@ -17,6 +17,7 @@
 #include <linux/timecounter.h>
 #include <linux/mutex.h>
 #include <linux/workqueue.h>
+#include <linux/leds.h>
 #include <linux/platform_data/hirschmann-hellcreek.h>
 #include <net/dsa.h>
 
@@ -275,6 +276,8 @@ struct hellcreek {
 	struct ptp_clock_info ptp_clock_info;
 	struct hellcreek_port *ports;
 	struct delayed_work overflow_work;
+	struct led_classdev led_is_gm;
+	struct led_classdev led_sync_good;
 	struct mutex reg_lock;	/* Switch IP register lock */
 	struct mutex ptp_lock;	/* PTP IP register lock */
 	void __iomem *base;
@@ -282,6 +285,7 @@ struct hellcreek {
 	u8 *vidmbrcfg;		/* vidmbrcfg shadow */
 	u64 seconds;		/* PTP seconds */
 	u64 last_ts;		/* Used for overflow detection */
+	u16 status_out;		/* ptp.status_out shadow */
 	size_t fdb_entries;
 };
 
diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.c b/drivers/net/dsa/hirschmann/hellcreek_ptp.c
index 12ad956abd5c..2572c6087bb5 100644
--- a/drivers/net/dsa/hirschmann/hellcreek_ptp.c
+++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.c
@@ -239,9 +239,148 @@ static void hellcreek_ptp_overflow_check(struct work_struct *work)
 			      HELLCREEK_OVERFLOW_PERIOD);
 }
 
+static enum led_brightness hellcreek_get_brightness(struct hellcreek *hellcreek,
+						    int led)
+{
+	return (hellcreek->status_out & led) ? 1 : 0;
+}
+
+static void hellcreek_set_brightness(struct hellcreek *hellcreek, int led,
+				     enum led_brightness b)
+{
+	mutex_lock(&hellcreek->ptp_lock);
+
+	if (b)
+		hellcreek->status_out |= led;
+	else
+		hellcreek->status_out &= ~led;
+
+	hellcreek_ptp_write(hellcreek, hellcreek->status_out, STATUS_OUT);
+
+	mutex_unlock(&hellcreek->ptp_lock);
+}
+
+static void hellcreek_led_sync_good_set(struct led_classdev *ldev,
+					enum led_brightness b)
+{
+	struct hellcreek *hellcreek = led_to_hellcreek(ldev, led_sync_good);
+
+	hellcreek_set_brightness(hellcreek, STATUS_OUT_SYNC_GOOD, b);
+}
+
+static enum led_brightness hellcreek_led_sync_good_get(struct led_classdev *ldev)
+{
+	struct hellcreek *hellcreek = led_to_hellcreek(ldev, led_sync_good);
+
+	return hellcreek_get_brightness(hellcreek, STATUS_OUT_SYNC_GOOD);
+}
+
+static void hellcreek_led_is_gm_set(struct led_classdev *ldev,
+				    enum led_brightness b)
+{
+	struct hellcreek *hellcreek = led_to_hellcreek(ldev, led_is_gm);
+
+	hellcreek_set_brightness(hellcreek, STATUS_OUT_IS_GM, b);
+}
+
+static enum led_brightness hellcreek_led_is_gm_get(struct led_classdev *ldev)
+{
+	struct hellcreek *hellcreek = led_to_hellcreek(ldev, led_is_gm);
+
+	return hellcreek_get_brightness(hellcreek, STATUS_OUT_IS_GM);
+}
+
+/* There two available LEDs internally called sync_good and is_gm. However, the
+ * user might want to use a different label and specify the default state. Take
+ * those properties from device tree.
+ */
+static int hellcreek_led_setup(struct hellcreek *hellcreek)
+{
+	struct device_node *leds, *led = NULL;
+	const char *label, *state;
+	int ret = -EINVAL;
+
+	leds = of_find_node_by_name(hellcreek->dev->of_node, "leds");
+	if (!leds) {
+		dev_err(hellcreek->dev, "No LEDs specified in device tree!\n");
+		return ret;
+	}
+
+	hellcreek->status_out = 0;
+
+	led = of_get_next_available_child(leds, led);
+	if (!led) {
+		dev_err(hellcreek->dev, "First LED not specified!\n");
+		goto out;
+	}
+
+	ret = of_property_read_string(led, "label", &label);
+	hellcreek->led_sync_good.name = ret ? "sync_good" : label;
+
+	ret = of_property_read_string(led, "default-state", &state);
+	if (!ret) {
+		if (!strcmp(state, "on"))
+			hellcreek->led_sync_good.brightness = 1;
+		else if (!strcmp(state, "off"))
+			hellcreek->led_sync_good.brightness = 0;
+		else if (!strcmp(state, "keep"))
+			hellcreek->led_sync_good.brightness =
+				hellcreek_get_brightness(hellcreek,
+							 STATUS_OUT_SYNC_GOOD);
+	}
+
+	hellcreek->led_sync_good.max_brightness = 1;
+	hellcreek->led_sync_good.brightness_set = hellcreek_led_sync_good_set;
+	hellcreek->led_sync_good.brightness_get = hellcreek_led_sync_good_get;
+
+	led = of_get_next_available_child(leds, led);
+	if (!led) {
+		dev_err(hellcreek->dev, "Second LED not specified!\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = of_property_read_string(led, "label", &label);
+	hellcreek->led_is_gm.name = ret ? "is_gm" : label;
+
+	ret = of_property_read_string(led, "default-state", &state);
+	if (!ret) {
+		if (!strcmp(state, "on"))
+			hellcreek->led_is_gm.brightness = 1;
+		else if (!strcmp(state, "off"))
+			hellcreek->led_is_gm.brightness = 0;
+		else if (!strcmp(state, "keep"))
+			hellcreek->led_is_gm.brightness =
+				hellcreek_get_brightness(hellcreek,
+							 STATUS_OUT_IS_GM);
+	}
+
+	hellcreek->led_is_gm.max_brightness = 1;
+	hellcreek->led_is_gm.brightness_set = hellcreek_led_is_gm_set;
+	hellcreek->led_is_gm.brightness_get = hellcreek_led_is_gm_get;
+
+	/* Set initial state */
+	if (hellcreek->led_sync_good.brightness == 1)
+		hellcreek_set_brightness(hellcreek, STATUS_OUT_SYNC_GOOD, 1);
+	if (hellcreek->led_is_gm.brightness == 1)
+		hellcreek_set_brightness(hellcreek, STATUS_OUT_IS_GM, 1);
+
+	/* Register both leds */
+	led_classdev_register(hellcreek->dev, &hellcreek->led_sync_good);
+	led_classdev_register(hellcreek->dev, &hellcreek->led_is_gm);
+
+	ret = 0;
+
+out:
+	of_node_put(leds);
+
+	return ret;
+}
+
 int hellcreek_ptp_setup(struct hellcreek *hellcreek)
 {
 	u16 status;
+	int ret;
 
 	/* Set up the overflow work */
 	INIT_DELAYED_WORK(&hellcreek->overflow_work,
@@ -288,6 +427,14 @@ int hellcreek_ptp_setup(struct hellcreek *hellcreek)
 	hellcreek_ptp_write(hellcreek, status | PR_CLOCK_STATUS_C_ENA_DRIFT,
 			    PR_CLOCK_STATUS_C);
 
+	/* LED setup */
+	ret = hellcreek_led_setup(hellcreek);
+	if (ret) {
+		if (hellcreek->ptp_clock)
+			ptp_clock_unregister(hellcreek->ptp_clock);
+		return ret;
+	}
+
 	schedule_delayed_work(&hellcreek->overflow_work,
 			      HELLCREEK_OVERFLOW_PERIOD);
 
@@ -296,6 +443,8 @@ int hellcreek_ptp_setup(struct hellcreek *hellcreek)
 
 void hellcreek_ptp_free(struct hellcreek *hellcreek)
 {
+	led_classdev_unregister(&hellcreek->led_is_gm);
+	led_classdev_unregister(&hellcreek->led_sync_good);
 	cancel_delayed_work_sync(&hellcreek->overflow_work);
 	if (hellcreek->ptp_clock)
 		ptp_clock_unregister(hellcreek->ptp_clock);
diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.h b/drivers/net/dsa/hirschmann/hellcreek_ptp.h
index e0eca1f4a494..0b51392c7e56 100644
--- a/drivers/net/dsa/hirschmann/hellcreek_ptp.h
+++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.h
@@ -70,4 +70,7 @@ u64 hellcreek_ptp_gettime_seconds(struct hellcreek *hellcreek, u64 ns);
 #define dw_overflow_to_hellcreek(dw)				\
 	container_of(dw, struct hellcreek, overflow_work)
 
+#define led_to_hellcreek(ldev, led)				\
+	container_of(ldev, struct hellcreek, led)
+
 #endif /* _HELLCREEK_PTP_H_ */
-- 
2.20.1


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

* [PATCH net-next v6 6/7] dt-bindings: Add vendor prefix for Hirschmann
  2020-10-04 11:29 [PATCH net-next v6 0/7] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
                   ` (4 preceding siblings ...)
  2020-10-04 11:29 ` [PATCH net-next v6 5/7] net: dsa: hellcreek: Add PTP status LEDs Kurt Kanzenbach
@ 2020-10-04 11:29 ` Kurt Kanzenbach
  2020-10-04 11:29 ` [PATCH net-next v6 7/7] dt-bindings: net: dsa: Add documentation for Hellcreek switches Kurt Kanzenbach
  6 siblings, 0 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-04 11:29 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Kurt Kanzenbach, Rob Herring

Hirschmann is building devices for automation and networking. Add them to the
vendor prefixes.

Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Acked-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 63996ab03521..6c4268b585cd 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -433,6 +433,8 @@ patternProperties:
     description: HiDeep Inc.
   "^himax,.*":
     description: Himax Technologies, Inc.
+  "^hirschmann,.*":
+    description: Hirschmann Automation and Control GmbH
   "^hisilicon,.*":
     description: Hisilicon Limited.
   "^hit,.*":
-- 
2.20.1


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

* [PATCH net-next v6 7/7] dt-bindings: net: dsa: Add documentation for Hellcreek switches
  2020-10-04 11:29 [PATCH net-next v6 0/7] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
                   ` (5 preceding siblings ...)
  2020-10-04 11:29 ` [PATCH net-next v6 6/7] dt-bindings: Add vendor prefix for Hirschmann Kurt Kanzenbach
@ 2020-10-04 11:29 ` Kurt Kanzenbach
  6 siblings, 0 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-04 11:29 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Vladimir Oltean
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Kurt Kanzenbach, Rob Herring

Add basic documentation and example.

Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
 .../bindings/net/dsa/hellcreek.yaml           | 127 ++++++++++++++++++
 1 file changed, 127 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/dsa/hellcreek.yaml

diff --git a/Documentation/devicetree/bindings/net/dsa/hellcreek.yaml b/Documentation/devicetree/bindings/net/dsa/hellcreek.yaml
new file mode 100644
index 000000000000..e26c5a2a360b
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/hellcreek.yaml
@@ -0,0 +1,127 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/hellcreek.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Hirschmann Hellcreek TSN Switch Device Tree Bindings
+
+allOf:
+  - $ref: dsa.yaml#
+
+maintainers:
+  - Andrew Lunn <andrew@lunn.ch>
+  - Florian Fainelli <f.fainelli@gmail.com>
+  - Vivien Didelot <vivien.didelot@gmail.com>
+  - Kurt Kanzenbach <kurt@linutronix.de>
+
+description:
+  The Hellcreek TSN Switch IP is a 802.1Q Ethernet compliant switch. It supports
+  the Precision Time Protocol, Hardware Timestamping as well the Time Aware
+  Shaper.
+
+properties:
+  compatible:
+    items:
+      - const: hirschmann,hellcreek-de1soc-r1
+
+  reg:
+    description:
+      The physical base address and size of TSN and PTP memory base
+    minItems: 2
+    maxItems: 2
+
+  reg-names:
+    items:
+      - const: tsn
+      - const: ptp
+
+  leds:
+    type: object
+    properties:
+      '#address-cells':
+        const: 1
+      '#size-cells':
+        const: 0
+
+    patternProperties:
+      "^led@[01]$":
+          type: object
+          description: Hellcreek leds
+          $ref: ../../leds/common.yaml#
+
+          properties:
+            reg:
+              items:
+                - enum: [0, 1]
+              description: Led number
+
+            label: true
+
+            default-state: true
+
+          required:
+            - reg
+
+          additionalProperties: false
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - ethernet-ports
+  - leds
+
+unevaluatedProperties: false
+
+examples:
+  - |
+        switch0: switch@ff240000 {
+            compatible = "hirschmann,hellcreek-de1soc-r1";
+            reg = <0xff240000 0x1000>,
+                  <0xff250000 0x1000>;
+            reg-names = "tsn", "ptp";
+            dsa,member = <0 0>;
+
+            ethernet-ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                port@0 {
+                    reg = <0>;
+                    label = "cpu";
+                    ethernet = <&gmac0>;
+                };
+
+                port@2 {
+                    reg = <2>;
+                    label = "lan0";
+                    phy-handle = <&phy1>;
+                };
+
+                port@3 {
+                    reg = <3>;
+                    label = "lan1";
+                    phy-handle = <&phy2>;
+                };
+            };
+
+            leds {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                led@0 {
+                    reg = <0>;
+                    label = "sync_good";
+                    default-state = "on";
+                };
+
+                led@1 {
+                    reg = <1>;
+                    label = "is_gm";
+                    default-state = "off";
+                };
+            };
+        };
-- 
2.20.1


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

* Re: [PATCH net-next v6 1/7] net: dsa: Add tag handling for Hirschmann Hellcreek switches
  2020-10-04 11:29 ` [PATCH net-next v6 1/7] net: dsa: Add tag handling for Hirschmann Hellcreek switches Kurt Kanzenbach
@ 2020-10-04 11:56   ` Vladimir Oltean
  2020-10-05  6:14     ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-04 11:56 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Sun, Oct 04, 2020 at 01:29:05PM +0200, Kurt Kanzenbach wrote:
> The Hirschmann Hellcreek TSN switches have a special tagging protocol for frames
> exchanged between the CPU port and the master interface. The format is a one
> byte trailer indicating the destination or origin port.
> 
> It's quite similar to the Micrel KSZ tagging. That's why the implementation is
> based on that code.
> 
> Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
> ---
>  include/net/dsa.h       |   2 +
>  net/dsa/Kconfig         |   6 +++
>  net/dsa/Makefile        |   1 +
>  net/dsa/tag_hellcreek.c | 101 ++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 110 insertions(+)
>  create mode 100644 net/dsa/tag_hellcreek.c
> 
> diff --git a/include/net/dsa.h b/include/net/dsa.h
> index 8b0696e08cac..ee24476a1a4c 100644
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -45,6 +45,7 @@ struct phylink_link_state;
>  #define DSA_TAG_PROTO_OCELOT_VALUE		15
>  #define DSA_TAG_PROTO_AR9331_VALUE		16
>  #define DSA_TAG_PROTO_RTL4_A_VALUE		17
> +#define DSA_TAG_PROTO_HELLCREEK_VALUE		18
>  
>  enum dsa_tag_protocol {
>  	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
> @@ -65,6 +66,7 @@ enum dsa_tag_protocol {
>  	DSA_TAG_PROTO_OCELOT		= DSA_TAG_PROTO_OCELOT_VALUE,
>  	DSA_TAG_PROTO_AR9331		= DSA_TAG_PROTO_AR9331_VALUE,
>  	DSA_TAG_PROTO_RTL4_A		= DSA_TAG_PROTO_RTL4_A_VALUE,
> +	DSA_TAG_PROTO_HELLCREEK		= DSA_TAG_PROTO_HELLCREEK_VALUE,
>  };
>  
>  struct packet_type;
> diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
> index 1f9b9b11008c..d975614f7dd6 100644
> --- a/net/dsa/Kconfig
> +++ b/net/dsa/Kconfig
> @@ -56,6 +56,12 @@ config NET_DSA_TAG_BRCM_PREPEND
>  	  Broadcom switches which places the tag before the Ethernet header
>  	  (prepended).
>  
> +config NET_DSA_TAG_HELLCREEK
> +	tristate "Tag driver for Hirschmann Hellcreek TSN switches"
> +	help
> +	  Say Y or M if you want to enable support for tagging frames
> +	  for the Hirschmann Hellcreek TSN switches.
> +
>  config NET_DSA_TAG_GSWIP
>  	tristate "Tag driver for Lantiq / Intel GSWIP switches"
>  	help
> diff --git a/net/dsa/Makefile b/net/dsa/Makefile
> index 4f47b2025ff5..e25d5457964a 100644
> --- a/net/dsa/Makefile
> +++ b/net/dsa/Makefile
> @@ -10,6 +10,7 @@ obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o
>  obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o
>  obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
>  obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o
> +obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += tag_hellcreek.o
>  obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
>  obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o
>  obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
> diff --git a/net/dsa/tag_hellcreek.c b/net/dsa/tag_hellcreek.c
> new file mode 100644
> index 000000000000..0895eda94bb5
> --- /dev/null
> +++ b/net/dsa/tag_hellcreek.c
> @@ -0,0 +1,101 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> +/*
> + * net/dsa/tag_hellcreek.c - Hirschmann Hellcreek switch tag format handling
> + *
> + * Copyright (C) 2019,2020 Linutronix GmbH
> + * Author Kurt Kanzenbach <kurt@linutronix.de>
> + *
> + * Based on tag_ksz.c.
> + */
> +
> +#include <linux/etherdevice.h>
> +#include <linux/list.h>
> +#include <linux/slab.h>
> +#include <net/dsa.h>
> +
> +#include "dsa_priv.h"
> +
> +#define HELLCREEK_TAG_LEN	1
> +
> +static struct sk_buff *hellcreek_xmit(struct sk_buff *skb,
> +				      struct net_device *dev)
> +{
> +	struct dsa_port *dp = dsa_slave_to_port(dev);
> +	struct sk_buff *nskb;
> +	int padlen;
> +	u8 *tag;
> +
> +	padlen = (skb->len >= ETH_ZLEN) ? 0 : ETH_ZLEN - skb->len;
> +
> +	if (skb_tailroom(skb) >= padlen + HELLCREEK_TAG_LEN) {
> +		/* Let dsa_slave_xmit() free skb */
> +		if (__skb_put_padto(skb, skb->len + padlen, false))
> +			return NULL;
> +
> +		nskb = skb;
> +	} else {
> +		nskb = alloc_skb(NET_IP_ALIGN + skb->len +
> +				 padlen + HELLCREEK_TAG_LEN, GFP_ATOMIC);
> +		if (!nskb)
> +			return NULL;
> +		skb_reserve(nskb, NET_IP_ALIGN);
> +
> +		skb_reset_mac_header(nskb);
> +		skb_set_network_header(nskb,
> +				       skb_network_header(skb) - skb->head);
> +		skb_set_transport_header(nskb,
> +					 skb_transport_header(skb) - skb->head);
> +		skb_copy_and_csum_dev(skb, skb_put(nskb, skb->len));
> +
> +		/* Let skb_put_padto() free nskb, and let dsa_slave_xmit() free
> +		 * skb
> +		 */
> +		if (skb_put_padto(nskb, nskb->len + padlen))
> +			return NULL;
> +
> +		consume_skb(skb);
> +	}
> +
> +	if (!nskb)
> +		return NULL;
> +
> +	/* Tag encoding */
> +	tag  = skb_put(nskb, HELLCREEK_TAG_LEN);
> +	*tag = BIT(dp->index);
> +
> +	return nskb;
> +}
> +
> +static struct sk_buff *hellcreek_rcv(struct sk_buff *skb,
> +				     struct net_device *dev,
> +				     struct packet_type *pt)
> +{
> +	/* Tag decoding */
> +	u8 *tag = skb_tail_pointer(skb) - HELLCREEK_TAG_LEN;
> +	unsigned int port = tag[0] & 0x03;
> +
> +	skb->dev = dsa_master_find_slave(dev, 0, port);
> +	if (!skb->dev) {
> +		netdev_warn(dev, "Failed to get source port: %d\n", port);
> +		return NULL;
> +	}
> +
> +	pskb_trim_rcsum(skb, skb->len - HELLCREEK_TAG_LEN);
> +
> +	skb->offload_fwd_mark = true;
> +
> +	return skb;
> +}
> +
> +static const struct dsa_device_ops hellcreek_netdev_ops = {
> +	.name	  = "hellcreek",
> +	.proto	  = DSA_TAG_PROTO_HELLCREEK,
> +	.xmit	  = hellcreek_xmit,
> +	.rcv	  = hellcreek_rcv,
> +	.overhead = HELLCREEK_TAG_LEN,

After the changes in "Generic adjustment for flow dissector in DSA":
https://patchwork.ozlabs.org/project/netdev/list/?series=204347&state=*
you might want to set ".tail_tag = true" (see patch 07/15), either now
or later.

Either way,

Reviewed-by: Vladimir Oltean <olteanv@gmail.com>

> +};
> +
> +MODULE_LICENSE("Dual MIT/GPL");
> +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_HELLCREEK);
> +
> +module_dsa_tag_driver(hellcreek_netdev_ops);
> -- 
> 2.20.1
> 

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-04 11:29 ` [PATCH net-next v6 2/7] net: dsa: Add DSA driver " Kurt Kanzenbach
@ 2020-10-04 12:56   ` Vladimir Oltean
  2020-10-06  6:09     ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-04 12:56 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Sun, Oct 04, 2020 at 01:29:06PM +0200, Kurt Kanzenbach wrote:
> Add a basic DSA driver for Hirschmann Hellcreek switches. Those switches are
> implementing features needed for Time Sensitive Networking (TSN) such as support
> for the Time Precision Protocol and various shapers like the Time Aware Shaper.
> 
> This driver includes basic support for networking:
> 
>  * VLAN handling
>  * FDB handling
>  * Port statistics
>  * STP
>  * Phylink
> 
> Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
> ---
>  drivers/net/dsa/Kconfig                       |    2 +
>  drivers/net/dsa/Makefile                      |    1 +
>  drivers/net/dsa/hirschmann/Kconfig            |    8 +
>  drivers/net/dsa/hirschmann/Makefile           |    2 +
>  drivers/net/dsa/hirschmann/hellcreek.c        | 1285 +++++++++++++++++
>  drivers/net/dsa/hirschmann/hellcreek.h        |  255 ++++
>  .../platform_data/hirschmann-hellcreek.h      |   23 +
>  7 files changed, 1576 insertions(+)
>  create mode 100644 drivers/net/dsa/hirschmann/Kconfig
>  create mode 100644 drivers/net/dsa/hirschmann/Makefile
>  create mode 100644 drivers/net/dsa/hirschmann/hellcreek.c
>  create mode 100644 drivers/net/dsa/hirschmann/hellcreek.h
>  create mode 100644 include/linux/platform_data/hirschmann-hellcreek.h
> 
> diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
> index 2451f61a38e4..f6a0488589fc 100644
> --- a/drivers/net/dsa/Kconfig
> +++ b/drivers/net/dsa/Kconfig
> @@ -24,6 +24,8 @@ config NET_DSA_LOOP
>  	  This enables support for a fake mock-up switch chip which
>  	  exercises the DSA APIs.
>  
> +source "drivers/net/dsa/hirschmann/Kconfig"
> +
>  config NET_DSA_LANTIQ_GSWIP
>  	tristate "Lantiq / Intel GSWIP"
>  	depends on HAS_IOMEM && NET_DSA
> diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
> index 4a943ccc2ca4..a84adb140a04 100644
> --- a/drivers/net/dsa/Makefile
> +++ b/drivers/net/dsa/Makefile
> @@ -18,6 +18,7 @@ obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o
>  obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o
>  obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o
>  obj-y				+= b53/
> +obj-y				+= hirschmann/
>  obj-y				+= microchip/
>  obj-y				+= mv88e6xxx/
>  obj-y				+= ocelot/
> diff --git a/drivers/net/dsa/hirschmann/Kconfig b/drivers/net/dsa/hirschmann/Kconfig
> new file mode 100644
> index 000000000000..7d189cb936e3
> --- /dev/null
> +++ b/drivers/net/dsa/hirschmann/Kconfig
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config NET_DSA_HIRSCHMANN_HELLCREEK
> +	tristate "Hirschmann Hellcreek TSN Switch support"
> +	depends on HAS_IOMEM
> +	depends on NET_DSA
> +	select NET_DSA_TAG_HELLCREEK
> +	help
> +	  This driver adds support for Hirschmann Hellcreek TSN switches.
> diff --git a/drivers/net/dsa/hirschmann/Makefile b/drivers/net/dsa/hirschmann/Makefile
> new file mode 100644
> index 000000000000..0e12e149e40f
> --- /dev/null
> +++ b/drivers/net/dsa/hirschmann/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_NET_DSA_HIRSCHMANN_HELLCREEK)	+= hellcreek.o
> diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
> new file mode 100644
> index 000000000000..2fe080010f8a
> --- /dev/null
> +++ b/drivers/net/dsa/hirschmann/hellcreek.c
> @@ -0,0 +1,1285 @@
> +// SPDX-License-Identifier: (GPL-2.0 or MIT)
> +/*
> + * DSA driver for:
> + * Hirschmann Hellcreek TSN switch.
> + *
> + * Copyright (C) 2019,2020 Linutronix GmbH
> + * Author Kurt Kanzenbach <kurt@linutronix.de>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_mdio.h>
> +#include <linux/platform_device.h>
> +#include <linux/bitops.h>
> +#include <linux/if_bridge.h>
> +#include <linux/etherdevice.h>
> +#include <linux/random.h>
> +#include <linux/iopoll.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +#include <net/dsa.h>
> +
> +#include "hellcreek.h"
> +
> +static const struct hellcreek_counter hellcreek_counter[] = {
> +	{ 0x00, "RxFiltered", },
> +	{ 0x01, "RxOctets1k", },
> +	{ 0x02, "RxVTAG", },
> +	{ 0x03, "RxL2BAD", },
> +	{ 0x04, "RxOverloadDrop", },
> +	{ 0x05, "RxUC", },
> +	{ 0x06, "RxMC", },
> +	{ 0x07, "RxBC", },
> +	{ 0x08, "RxRS<64", },
> +	{ 0x09, "RxRS64", },
> +	{ 0x0a, "RxRS65_127", },
> +	{ 0x0b, "RxRS128_255", },
> +	{ 0x0c, "RxRS256_511", },
> +	{ 0x0d, "RxRS512_1023", },
> +	{ 0x0e, "RxRS1024_1518", },
> +	{ 0x0f, "RxRS>1518", },
> +	{ 0x10, "TxTailDropQueue0", },
> +	{ 0x11, "TxTailDropQueue1", },
> +	{ 0x12, "TxTailDropQueue2", },
> +	{ 0x13, "TxTailDropQueue3", },
> +	{ 0x14, "TxTailDropQueue4", },
> +	{ 0x15, "TxTailDropQueue5", },
> +	{ 0x16, "TxTailDropQueue6", },
> +	{ 0x17, "TxTailDropQueue7", },
> +	{ 0x18, "RxTrafficClass0", },
> +	{ 0x19, "RxTrafficClass1", },
> +	{ 0x1a, "RxTrafficClass2", },
> +	{ 0x1b, "RxTrafficClass3", },
> +	{ 0x1c, "RxTrafficClass4", },
> +	{ 0x1d, "RxTrafficClass5", },
> +	{ 0x1e, "RxTrafficClass6", },
> +	{ 0x1f, "RxTrafficClass7", },
> +	{ 0x21, "TxOctets1k", },
> +	{ 0x22, "TxVTAG", },
> +	{ 0x23, "TxL2BAD", },
> +	{ 0x25, "TxUC", },
> +	{ 0x26, "TxMC", },
> +	{ 0x27, "TxBC", },
> +	{ 0x28, "TxTS<64", },
> +	{ 0x29, "TxTS64", },
> +	{ 0x2a, "TxTS65_127", },
> +	{ 0x2b, "TxTS128_255", },
> +	{ 0x2c, "TxTS256_511", },
> +	{ 0x2d, "TxTS512_1023", },
> +	{ 0x2e, "TxTS1024_1518", },
> +	{ 0x2f, "TxTS>1518", },
> +	{ 0x30, "TxTrafficClassOverrun0", },
> +	{ 0x31, "TxTrafficClassOverrun1", },
> +	{ 0x32, "TxTrafficClassOverrun2", },
> +	{ 0x33, "TxTrafficClassOverrun3", },
> +	{ 0x34, "TxTrafficClassOverrun4", },
> +	{ 0x35, "TxTrafficClassOverrun5", },
> +	{ 0x36, "TxTrafficClassOverrun6", },
> +	{ 0x37, "TxTrafficClassOverrun7", },
> +	{ 0x38, "TxTrafficClass0", },
> +	{ 0x39, "TxTrafficClass1", },
> +	{ 0x3a, "TxTrafficClass2", },
> +	{ 0x3b, "TxTrafficClass3", },
> +	{ 0x3c, "TxTrafficClass4", },
> +	{ 0x3d, "TxTrafficClass5", },
> +	{ 0x3e, "TxTrafficClass6", },
> +	{ 0x3f, "TxTrafficClass7", },
> +};
> +
> +static u16 hellcreek_read(struct hellcreek *hellcreek, unsigned int offset)
> +{
> +	return readw(hellcreek->base + offset);
> +}
> +
> +static u16 hellcreek_read_ctrl(struct hellcreek *hellcreek)
> +{
> +	return readw(hellcreek->base + HR_CTRL_C);
> +}
> +
> +static u16 hellcreek_read_stat(struct hellcreek *hellcreek)
> +{
> +	return readw(hellcreek->base + HR_SWSTAT);
> +}
> +
> +static void hellcreek_write(struct hellcreek *hellcreek, u16 data,
> +			    unsigned int offset)
> +{
> +	writew(data, hellcreek->base + offset);
> +}
> +
> +static void hellcreek_select_port(struct hellcreek *hellcreek, int port)
> +{
> +	u16 val = port << HR_PSEL_PTWSEL_SHIFT;
> +
> +	hellcreek_write(hellcreek, val, HR_PSEL);
> +}
> +
> +static void hellcreek_select_prio(struct hellcreek *hellcreek, int prio)
> +{
> +	u16 val = prio << HR_PSEL_PRTCWSEL_SHIFT;
> +
> +	hellcreek_write(hellcreek, val, HR_PSEL);
> +}
> +
> +static void hellcreek_select_counter(struct hellcreek *hellcreek, int counter)
> +{
> +	u16 val = counter << HR_CSEL_SHIFT;
> +
> +	hellcreek_write(hellcreek, val, HR_CSEL);
> +
> +	/* Data sheet states to wait at least 20 internal clock cycles */
> +	ndelay(200);
> +}
> +
> +static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid,
> +				  bool pvid)
> +{
> +	u16 val = 0;
> +
> +	/* Set pvid bit first */
> +	if (pvid)
> +		val |= HR_VIDCFG_PVID;
> +	hellcreek_write(hellcreek, val, HR_VIDCFG);
> +
> +	/* Set vlan */
> +	val |= vid << HR_VIDCFG_VID_SHIFT;
> +	hellcreek_write(hellcreek, val, HR_VIDCFG);
> +}
> +
> +static int hellcreek_wait_until_ready(struct hellcreek *hellcreek)
> +{
> +	u16 val;
> +
> +	/* Wait up to 1ms, although 3 us should be enough */
> +	return readx_poll_timeout(hellcreek_read_ctrl, hellcreek,
> +				  val, val & HR_CTRL_C_READY,
> +				  3, 1000);
> +}
> +
> +static int hellcreek_wait_until_transitioned(struct hellcreek *hellcreek)
> +{
> +	u16 val;
> +
> +	return readx_poll_timeout_atomic(hellcreek_read_ctrl, hellcreek,
> +					 val, !(val & HR_CTRL_C_TRANSITION),
> +					 1, 1000);
> +}
> +
> +static int hellcreek_wait_fdb_ready(struct hellcreek *hellcreek)
> +{
> +	u16 val;
> +
> +	return readx_poll_timeout_atomic(hellcreek_read_stat, hellcreek,
> +					 val, !(val & HR_SWSTAT_BUSY),
> +					 1, 1000);
> +}
> +
> +static int hellcreek_detect(struct hellcreek *hellcreek)
> +{
> +	u16 id, rel_low, rel_high, date_low, date_high, tgd_ver;
> +	u8 tgd_maj, tgd_min;
> +	u32 rel, date;
> +
> +	id	  = hellcreek_read(hellcreek, HR_MODID_C);
> +	rel_low	  = hellcreek_read(hellcreek, HR_REL_L_C);
> +	rel_high  = hellcreek_read(hellcreek, HR_REL_H_C);
> +	date_low  = hellcreek_read(hellcreek, HR_BLD_L_C);
> +	date_high = hellcreek_read(hellcreek, HR_BLD_H_C);
> +	tgd_ver   = hellcreek_read(hellcreek, TR_TGDVER);
> +
> +	if (id != hellcreek->pdata->module_id)
> +		return -ENODEV;
> +
> +	rel	= rel_low | (rel_high << 16);
> +	date	= date_low | (date_high << 16);
> +	tgd_maj = (tgd_ver & TR_TGDVER_REV_MAJ_MASK) >> TR_TGDVER_REV_MAJ_SHIFT;
> +	tgd_min = (tgd_ver & TR_TGDVER_REV_MIN_MASK) >> TR_TGDVER_REV_MIN_SHIFT;
> +
> +	dev_info(hellcreek->dev, "Module ID=%02x Release=%04x Date=%04x TGD Version=%02x.%02x\n",
> +		 id, rel, date, tgd_maj, tgd_min);
> +
> +	return 0;
> +}
> +
> +static void hellcreek_feature_detect(struct hellcreek *hellcreek)
> +{
> +	u16 features;
> +
> +	features = hellcreek_read(hellcreek, HR_FEABITS0);
> +
> +	/* Currently we only detect the size of the FDB table */
> +	hellcreek->fdb_entries = ((features & HR_FEABITS0_FDBBINS_MASK) >>
> +			       HR_FEABITS0_FDBBINS_SHIFT) * 32;
> +
> +	dev_info(hellcreek->dev, "Feature detect: FDB entries=%zu\n",
> +		 hellcreek->fdb_entries);
> +}
> +
> +static enum dsa_tag_protocol hellcreek_get_tag_protocol(struct dsa_switch *ds,
> +							int port,
> +							enum dsa_tag_protocol mp)
> +{
> +	return DSA_TAG_PROTO_HELLCREEK;
> +}
> +
> +static int hellcreek_port_enable(struct dsa_switch *ds, int port,
> +				 struct phy_device *phy)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port *hellcreek_port;
> +	u16 val;
> +
> +	hellcreek_port = &hellcreek->ports[port];
> +
> +	dev_dbg(hellcreek->dev, "Enable port %d\n", port);
> +
> +	mutex_lock(&hellcreek->reg_lock);
> +
> +	hellcreek_select_port(hellcreek, port);
> +	val = hellcreek_port->ptcfg;
> +	val |= HR_PTCFG_ADMIN_EN;
> +	hellcreek_write(hellcreek, val, HR_PTCFG);
> +	hellcreek_port->ptcfg = val;
> +
> +	mutex_unlock(&hellcreek->reg_lock);
> +
> +	return 0;
> +}
> +
> +static void hellcreek_port_disable(struct dsa_switch *ds, int port)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port *hellcreek_port;
> +	u16 val;
> +
> +	hellcreek_port = &hellcreek->ports[port];
> +
> +	dev_dbg(hellcreek->dev, "Disable port %d\n", port);
> +
> +	mutex_lock(&hellcreek->reg_lock);
> +
> +	hellcreek_select_port(hellcreek, port);
> +	val = hellcreek_port->ptcfg;
> +	val &= ~HR_PTCFG_ADMIN_EN;
> +	hellcreek_write(hellcreek, val, HR_PTCFG);
> +	hellcreek_port->ptcfg = val;
> +
> +	mutex_unlock(&hellcreek->reg_lock);
> +}
> +
> +static void hellcreek_get_strings(struct dsa_switch *ds, int port,
> +				  u32 stringset, uint8_t *data)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) {

Is ++i faster, or why do you prefer it over the more conventional "i++"?

> +		const struct hellcreek_counter *counter = &hellcreek_counter[i];
> +
> +		strlcpy(data + i * ETH_GSTRING_LEN,
> +			counter->name, ETH_GSTRING_LEN);
> +	}
> +}
> +
> +static int hellcreek_get_sset_count(struct dsa_switch *ds, int port, int sset)
> +{
> +	if (sset != ETH_SS_STATS)
> +		return 0;
> +
> +	return ARRAY_SIZE(hellcreek_counter);
> +}
> +
> +static void hellcreek_get_ethtool_stats(struct dsa_switch *ds, int port,
> +					uint64_t *data)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port *hellcreek_port;
> +	int i;
> +
> +	hellcreek_port = &hellcreek->ports[port];
> +
> +	for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) {
> +		const struct hellcreek_counter *counter = &hellcreek_counter[i];
> +		u8 offset = counter->offset + port * 64;
> +		u16 high, low;
> +		u64 value = 0;
> +
> +		mutex_lock(&hellcreek->reg_lock);
> +
> +		hellcreek_select_counter(hellcreek, offset);
> +
> +		/* The registers are locked internally by selecting the
> +		 * counter. So low and high can be read without reading high
> +		 * again.
> +		 */
> +		high  = hellcreek_read(hellcreek, HR_CRDH);
> +		low   = hellcreek_read(hellcreek, HR_CRDL);
> +		value = (high << 16) | low;
> +
> +		hellcreek_port->counter_values[i] += value;
> +		data[i] = hellcreek_port->counter_values[i];
> +
> +		mutex_unlock(&hellcreek->reg_lock);
> +	}
> +}
> +
> +static int hellcreek_vlan_prepare(struct dsa_switch *ds, int port,
> +				  const struct switchdev_obj_port_vlan *vlan)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +
> +	/* Nothing todo */
> +	dev_dbg(hellcreek->dev, "VLAN prepare for port %d\n", port);
> +
> +	return 0;
> +}
> +
> +static void hellcreek_select_vlan_params(struct hellcreek *hellcreek, int port,
> +					 int *shift, int *mask)
> +{
> +	switch (port) {
> +	case 0:
> +		*shift = HR_VIDMBRCFG_P0MBR_SHIFT;
> +		*mask  = HR_VIDMBRCFG_P0MBR_MASK;
> +		break;
> +	case 1:
> +		*shift = HR_VIDMBRCFG_P1MBR_SHIFT;
> +		*mask  = HR_VIDMBRCFG_P1MBR_MASK;
> +		break;
> +	case 2:
> +		*shift = HR_VIDMBRCFG_P2MBR_SHIFT;
> +		*mask  = HR_VIDMBRCFG_P2MBR_MASK;
> +		break;
> +	case 3:
> +		*shift = HR_VIDMBRCFG_P3MBR_SHIFT;
> +		*mask  = HR_VIDMBRCFG_P3MBR_MASK;
> +		break;
> +	default:
> +		*shift = *mask = 0;
> +		dev_err(hellcreek->dev, "Unknown port %d selected!\n", port);
> +	}
> +}
> +
> +static void hellcreek_apply_vlan(struct hellcreek *hellcreek, int port, u16 vid,
> +				 bool pvid, bool untagged)
> +{
> +	int shift, mask;
> +	u16 val;
> +
> +	dev_dbg(hellcreek->dev, "Apply VLAN: port=%d vid=%u pvid=%d untagged=%d",
> +		port, vid, pvid, untagged);
> +
> +	mutex_lock(&hellcreek->reg_lock);
> +
> +	hellcreek_select_port(hellcreek, port);
> +	hellcreek_select_vlan(hellcreek, vid, pvid);
> +
> +	/* Setup port vlan membership */
> +	hellcreek_select_vlan_params(hellcreek, port, &shift, &mask);
> +	val = hellcreek->vidmbrcfg[vid];
> +	val &= ~mask;
> +	if (untagged)
> +		val |= HELLCREEK_VLAN_UNTAGGED_MEMBER << shift;
> +	else
> +		val |= HELLCREEK_VLAN_TAGGED_MEMBER << shift;
> +
> +	hellcreek_write(hellcreek, val, HR_VIDMBRCFG);
> +	hellcreek->vidmbrcfg[vid] = val;
> +
> +	mutex_unlock(&hellcreek->reg_lock);
> +}
> +
> +static void hellcreek_unapply_vlan(struct hellcreek *hellcreek, int port,
> +				   u16 vid)
> +{
> +	int shift, mask;
> +	u16 val;
> +
> +	dev_dbg(hellcreek->dev, "Unapply VLAN: port=%d vid=%u\n", port, vid);
> +
> +	mutex_lock(&hellcreek->reg_lock);
> +
> +	hellcreek_select_vlan(hellcreek, vid, 0);
> +
> +	/* Setup port vlan membership */
> +	hellcreek_select_vlan_params(hellcreek, port, &shift, &mask);
> +	val = hellcreek->vidmbrcfg[vid];
> +	val &= ~mask;
> +	val |= HELLCREEK_VLAN_NO_MEMBER << shift;
> +
> +	hellcreek_write(hellcreek, val, HR_VIDMBRCFG);
> +	hellcreek->vidmbrcfg[vid] = val;
> +
> +	mutex_unlock(&hellcreek->reg_lock);
> +}
> +
> +static int hellcreek_queue_vlan(struct hellcreek *hellcreek, int port, u16 vid,
> +				bool pvid, bool untagged)
> +{
> +	struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
> +	struct list_head *vlan_list = &hellcreek_port->vlan_list;
> +	struct hellcreek_vlan *v;
> +	bool found = false;
> +
> +	dev_dbg(hellcreek->dev, "Queue VLAN: port=%d vid=%u pvid=%d untagged=%d\n",
> +		port, vid, pvid, untagged);
> +
> +	list_for_each_entry(v, vlan_list, list) {
> +		if (v->vid == vid &&
> +		    v->pvid == pvid &&
> +		    v->untagged == untagged) {
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	if (found)
> +		return 0;
> +
> +	v = kzalloc(sizeof(*v), GFP_KERNEL);
> +	if (!v)
> +		return -ENOMEM;
> +
> +	v->vid	    = vid;
> +	v->pvid	    = pvid;
> +	v->untagged = untagged;
> +
> +	list_add(&v->list, vlan_list);
> +
> +	return 0;
> +}
> +
> +static void hellcreek_unqueue_vlan(struct hellcreek *hellcreek, int port,
> +				   u16 vid)
> +{
> +	struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
> +	struct list_head *vlan_list = &hellcreek_port->vlan_list;
> +	struct hellcreek_vlan *v, *n;
> +
> +	dev_dbg(hellcreek->dev, "Unqueue VLAN: port=%d vid=%u\n", port, vid);
> +
> +	list_for_each_entry_safe(v, n, vlan_list, list) {
> +		if (v->vid == vid) {
> +			list_del(&v->list);
> +			kfree(v);
> +			return;
> +		}
> +	}
> +}
> +
> +static void hellcreek_apply_queued_vlans(struct hellcreek *hellcreek, int port)
> +{
> +	struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
> +	struct list_head *vlan_list = &hellcreek_port->vlan_list;
> +	struct hellcreek_vlan *v;
> +
> +	dev_dbg(hellcreek->dev, "Apply queued VLANs: port%d\n", port);
> +
> +	list_for_each_entry(v, vlan_list, list) {
> +		hellcreek_apply_vlan(hellcreek, port, v->vid, v->pvid,
> +				     v->untagged);
> +	}
> +}
> +
> +static void hellcreek_clear_queued_vlans(struct hellcreek *hellcreek, int port)
> +{
> +	struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
> +	struct list_head *vlan_list = &hellcreek_port->vlan_list;
> +	struct hellcreek_vlan *v, *n;
> +
> +	dev_dbg(hellcreek->dev, "Clear queued VLANs: port%d\n", port);
> +
> +	list_for_each_entry_safe(v, n, vlan_list, list) {
> +		list_del(&v->list);
> +		kfree(v);
> +	}
> +}
> +
> +static void hellcreek_vlan_add(struct dsa_switch *ds, int port,
> +			       const struct switchdev_obj_port_vlan *vlan)
> +{
> +	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
> +	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
> +	struct hellcreek *hellcreek = ds->priv;
> +	u16 vid;
> +
> +	dev_dbg(hellcreek->dev, "Add VLANs (%d -- %d) on port %d, %s, %s\n",
> +		vlan->vid_begin, vlan->vid_end, port,
> +		untagged ? "untagged" : "tagged",
> +		pvid ? "PVID" : "no PVID");
> +
> +	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
> +		/* When vlan_filtering is set, the configuration can be applied
> +		 * immediately. If not, the configuration has to be saved and
> +		 * restored when vlan_filtering is set. This is because the
> +		 * driver internally uses VLANs for the port separation.
> +		 */
> +		mutex_lock(&hellcreek->ports[port].vlan_lock);
> +		if (hellcreek->ports[port].vlan_filtering)
> +			hellcreek_apply_vlan(hellcreek, port, vid,
> +					     pvid, untagged);
> +		else
> +			/* This function can fail due to memory allocations.
> +			 * However, there's nothing we can do in that case.
> +			 */
> +			hellcreek_queue_vlan(hellcreek, port, vid,
> +					     pvid, untagged);
> +		mutex_unlock(&hellcreek->ports[port].vlan_lock);
> +	}
> +}
> +
> +static int hellcreek_vlan_del(struct dsa_switch *ds, int port,
> +			      const struct switchdev_obj_port_vlan *vlan)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	u16 vid;
> +
> +	dev_dbg(hellcreek->dev, "Remove VLANs (%d -- %d) on port %d\n",
> +		vlan->vid_begin, vlan->vid_end, port);
> +
> +	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
> +		mutex_lock(&hellcreek->ports[port].vlan_lock);
> +		if (hellcreek->ports[port].vlan_filtering)
> +			hellcreek_unapply_vlan(hellcreek, port, vid);

I don't think this works.

ip link add br0 type bridge vlan_filtering 1
ip link set swp0 master br0
bridge vlan add dev swp0 vid 100
ip link set br0 type bridge vlan_filtering 0
bridge vlan del dev swp0 vid 100
ip link set br0 type bridge vlan_filtering 1

The expectation would be that swp0 blocks vid 100 now, but with your
scheme it doesn't (it is not unapplied, and not unqueued either, because
it was never queued in the first place).

> +		else
> +			hellcreek_unqueue_vlan(hellcreek, port, vid);
> +		mutex_unlock(&hellcreek->ports[port].vlan_lock);
> +	}
> +
> +	return 0;
> +}
> +
> +static void hellcreek_port_stp_state_set(struct dsa_switch *ds, int port,
> +					 u8 state)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port *hellcreek_port;
> +	const char *new_state;
> +	u16 val;
> +
> +	mutex_lock(&hellcreek->reg_lock);
> +
> +	hellcreek_port = &hellcreek->ports[port];
> +	val = hellcreek_port->ptcfg;
> +
> +	switch (state) {
> +	case BR_STATE_DISABLED:
> +		new_state = "DISABLED";
> +		val |= HR_PTCFG_BLOCKED;
> +		val &= ~HR_PTCFG_LEARNING_EN;
> +		break;
> +	case BR_STATE_BLOCKING:
> +		new_state = "BLOCKING";
> +		val |= HR_PTCFG_BLOCKED;
> +		val &= ~HR_PTCFG_LEARNING_EN;
> +		break;
> +	case BR_STATE_LISTENING:
> +		new_state = "LISTENING";
> +		val |= HR_PTCFG_BLOCKED;
> +		val &= ~HR_PTCFG_LEARNING_EN;
> +		break;
> +	case BR_STATE_LEARNING:
> +		new_state = "LEARNING";
> +		val |= HR_PTCFG_BLOCKED;
> +		val |= HR_PTCFG_LEARNING_EN;
> +		break;
> +	case BR_STATE_FORWARDING:
> +		new_state = "FORWARDING";
> +		val &= ~HR_PTCFG_BLOCKED;
> +		val |= HR_PTCFG_LEARNING_EN;
> +		break;
> +	default:
> +		new_state = "UNKNOWN";
> +	}
> +
> +	hellcreek_select_port(hellcreek, port);
> +	hellcreek_write(hellcreek, val, HR_PTCFG);
> +	hellcreek_port->ptcfg = val;
> +
> +	mutex_unlock(&hellcreek->reg_lock);
> +
> +	dev_dbg(hellcreek->dev, "Configured STP state for port %d: %s\n",
> +		port, new_state);
> +}
> +
> +static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,
> +				      struct net_device *br)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	int i;
> +
> +	dev_dbg(hellcreek->dev, "Port %d joins a bridge\n", port);
> +
> +	/* Configure port's vid to all other ports as egress untagged */
> +	for (i = 0; i < ds->num_ports; ++i) {
> +		if (!dsa_is_user_port(ds, i))
> +			continue;
> +
> +		if (i == port)
> +			continue;
> +
> +		hellcreek_apply_vlan(hellcreek, i, port, false, true);
> +	}

I think this is buggy when joining a VLAN filtering bridge. Your ports
will pass frames with VID=2 with no problem, even without the user
specifying 'bridge vlan add dev swp0 vid 2', and that's an issue. My
understanding is that VLANs 1, 2, 3 stop having any sort of special
meaning when the upper bridge has vlan_filtering=1.

And how do you deal with the case where swp1 and swp2 are bridged and
have the VLAN 3 installed via 'bridge vlan', but swp3 isn't bridged?
Will swp1/swp2 communicate with swp3? If yes, that's a problem.

> +
> +	return 0;
> +}
> +
> +static void hellcreek_port_bridge_leave(struct dsa_switch *ds, int port,
> +					struct net_device *br)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	int i;
> +
> +	dev_dbg(hellcreek->dev, "Port %d leaves a bridge\n", port);
> +
> +	/* Remove port's vid from all other ports */
> +	for (i = 0; i < ds->num_ports; ++i) {
> +		if (!dsa_is_user_port(ds, i))
> +			continue;
> +
> +		if (i == port)
> +			continue;
> +
> +		hellcreek_unapply_vlan(hellcreek, i, port);
> +	}
> +}
> +
> +static int __hellcreek_fdb_add(struct hellcreek *hellcreek,
> +			       const struct hellcreek_fdb_entry *entry)
> +{
> +	u16 meta = 0;
> +
> +	dev_dbg(hellcreek->dev, "Add static FDB entry: MAC=%pM, MASK=0x%02x, "
> +		"OBT=%d, REPRIO_EN=%d, PRIO=%d\n", entry->mac, entry->portmask,
> +		entry->is_obt, entry->reprio_en, entry->reprio_tc);
> +
> +	/* Add mac address */
> +	hellcreek_write(hellcreek, entry->mac[1] | (entry->mac[0] << 8), HR_FDBWDH);
> +	hellcreek_write(hellcreek, entry->mac[3] | (entry->mac[2] << 8), HR_FDBWDM);
> +	hellcreek_write(hellcreek, entry->mac[5] | (entry->mac[4] << 8), HR_FDBWDL);
> +
> +	/* Meta data */
> +	meta |= entry->portmask << HR_FDBWRM0_PORTMASK_SHIFT;
> +	if (entry->is_obt)
> +		meta |= HR_FDBWRM0_OBT;
> +	if (entry->reprio_en) {
> +		meta |= HR_FDBWRM0_REPRIO_EN;
> +		meta |= entry->reprio_tc << HR_FDBWRM0_REPRIO_TC_SHIFT;
> +	}
> +	hellcreek_write(hellcreek, meta, HR_FDBWRM0);
> +
> +	/* Commit */
> +	hellcreek_write(hellcreek, 0x00, HR_FDBWRCMD);
> +
> +	/* Wait until done */
> +	return hellcreek_wait_fdb_ready(hellcreek);
> +}
> +
> +static int __hellcreek_fdb_del(struct hellcreek *hellcreek,
> +			       const struct hellcreek_fdb_entry *entry)
> +{
> +	dev_dbg(hellcreek->dev, "Delete FDB entry: MAC=%pM!\n", entry->mac);
> +

Do these dev_dbg statements bring much value at all, even to you?

> +	/* Delete by matching idx */
> +	hellcreek_write(hellcreek, entry->idx | HR_FDBWRCMD_FDBDEL, HR_FDBWRCMD);
> +
> +	/* Wait until done */
> +	return hellcreek_wait_fdb_ready(hellcreek);
> +}
> +
> +/* Retrieve the index of a FDB entry by mac address. Currently we search through
> + * the complete table in hardware. If that's too slow, we might have to cache
> + * the complete FDB table in software.
> + */
> +static int hellcreek_fdb_get(struct hellcreek *hellcreek,
> +			     const unsigned char *dest,
> +			     struct hellcreek_fdb_entry *entry)
> +{
> +	size_t i;
> +
> +	/* Set read pointer to zero: The read of HR_FDBMAX (read-only register)
> +	 * should reset the internal pointer. But, that doesn't work. The vendor
> +	 * suggested a subsequent write as workaround. Same for HR_FDBRDH below.
> +	 */
> +	hellcreek_read(hellcreek, HR_FDBMAX);
> +	hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
> +
> +	/* We have to read the complete table, because the switch/driver might
> +	 * enter new entries anywhere.
> +	 */
> +	for (i = 0; i < hellcreek->fdb_entries; ++i) {
> +		unsigned char addr[ETH_ALEN];
> +		u16 meta, mac;
> +
> +		meta	= hellcreek_read(hellcreek, HR_FDBMDRD);
> +		mac	= hellcreek_read(hellcreek, HR_FDBRDL);
> +		addr[5] = mac & 0xff;
> +		addr[4] = (mac & 0xff00) >> 8;
> +		mac	= hellcreek_read(hellcreek, HR_FDBRDM);
> +		addr[3] = mac & 0xff;
> +		addr[2] = (mac & 0xff00) >> 8;
> +		mac	= hellcreek_read(hellcreek, HR_FDBRDH);
> +		addr[1] = mac & 0xff;
> +		addr[0] = (mac & 0xff00) >> 8;
> +
> +		/* Force next entry */
> +		hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
> +
> +		if (memcmp(addr, dest, ETH_ALEN))
> +			continue;
> +
> +		/* Match found */
> +		entry->idx	    = i;
> +		entry->portmask	    = (meta & HR_FDBMDRD_PORTMASK_MASK) >>
> +			HR_FDBMDRD_PORTMASK_SHIFT;
> +		entry->age	    = (meta & HR_FDBMDRD_AGE_MASK) >>
> +			HR_FDBMDRD_AGE_SHIFT;
> +		entry->is_obt	    = !!(meta & HR_FDBMDRD_OBT);
> +		entry->pass_blocked = !!(meta & HR_FDBMDRD_PASS_BLOCKED);
> +		entry->is_static    = !!(meta & HR_FDBMDRD_STATIC);
> +		entry->reprio_tc    = (meta & HR_FDBMDRD_REPRIO_TC_MASK) >>
> +			HR_FDBMDRD_REPRIO_TC_SHIFT;
> +		entry->reprio_en    = !!(meta & HR_FDBMDRD_REPRIO_EN);
> +		memcpy(entry->mac, addr, sizeof(addr));
> +
> +		return 0;
> +	}
> +
> +	return -ENOENT;
> +}
> +
> +static int hellcreek_fdb_add(struct dsa_switch *ds, int port,
> +			     const unsigned char *addr, u16 vid)
> +{
> +	struct hellcreek_fdb_entry entry = { 0 };
> +	struct hellcreek *hellcreek = ds->priv;
> +	int ret;
> +
> +	dev_dbg(hellcreek->dev, "Add FDB entry for MAC=%pM\n", addr);
> +
> +	mutex_lock(&hellcreek->reg_lock);
> +
> +	ret = hellcreek_fdb_get(hellcreek, addr, &entry);
> +	if (ret) {
> +		/* Not found */
> +		memcpy(entry.mac, addr, sizeof(entry.mac));
> +		entry.portmask = BIT(port);
> +
> +		ret = __hellcreek_fdb_add(hellcreek, &entry);
> +		if (ret) {
> +			dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
> +			goto out;
> +		}
> +	} else {
> +		/* Found */
> +		ret = __hellcreek_fdb_del(hellcreek, &entry);
> +		if (ret) {
> +			dev_err(hellcreek->dev, "Failed to delete FDB entry!\n");
> +			goto out;
> +		}
> +
> +		entry.portmask |= BIT(port);
> +
> +		ret = __hellcreek_fdb_add(hellcreek, &entry);
> +		if (ret) {
> +			dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
> +			goto out;
> +		}
> +	}
> +
> +out:
> +	mutex_unlock(&hellcreek->reg_lock);
> +
> +	return ret;
> +}
> +
> +static int hellcreek_fdb_del(struct dsa_switch *ds, int port,
> +			     const unsigned char *addr, u16 vid)
> +{
> +	struct hellcreek_fdb_entry entry = { 0 };
> +	struct hellcreek *hellcreek = ds->priv;
> +	int ret;
> +
> +	dev_dbg(hellcreek->dev, "Delete FDB entry for MAC=%pM\n", addr);
> +
> +	mutex_lock(&hellcreek->reg_lock);
> +
> +	ret = hellcreek_fdb_get(hellcreek, addr, &entry);
> +	if (ret) {
> +		/* Not found */
> +		dev_err(hellcreek->dev, "FDB entry for deletion not found!\n");
> +	} else {
> +		/* Found */
> +		ret = __hellcreek_fdb_del(hellcreek, &entry);
> +		if (ret) {
> +			dev_err(hellcreek->dev, "Failed to delete FDB entry!\n");
> +			goto out;
> +		}
> +
> +		entry.portmask &= ~BIT(port);
> +
> +		if (entry.portmask != 0x00) {
> +			ret = __hellcreek_fdb_add(hellcreek, &entry);
> +			if (ret) {
> +				dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
> +				goto out;
> +			}
> +		}
> +	}
> +
> +out:
> +	mutex_unlock(&hellcreek->reg_lock);
> +
> +	return ret;
> +}
> +
> +static int hellcreek_fdb_dump(struct dsa_switch *ds, int port,
> +			      dsa_fdb_dump_cb_t *cb, void *data)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	u16 entries;
> +	size_t i;
> +
> +	mutex_lock(&hellcreek->reg_lock);
> +
> +	/* Set read pointer to zero: The read of HR_FDBMAX (read-only register)
> +	 * should reset the internal pointer. But, that doesn't work. The vendor
> +	 * suggested a subsequent write as workaround. Same for HR_FDBRDH below.
> +	 */
> +	entries = hellcreek_read(hellcreek, HR_FDBMAX);
> +	hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
> +
> +	dev_dbg(hellcreek->dev, "FDB dump for port %d, entries=%d!\n", port, entries);
> +
> +	/* Read table */
> +	for (i = 0; i < hellcreek->fdb_entries; ++i) {
> +		unsigned char null_addr[ETH_ALEN] = { 0 };
> +		struct hellcreek_fdb_entry entry = { 0 };
> +		u16 meta, mac;
> +
> +		meta	= hellcreek_read(hellcreek, HR_FDBMDRD);
> +		mac	= hellcreek_read(hellcreek, HR_FDBRDL);
> +		entry.mac[5] = mac & 0xff;
> +		entry.mac[4] = (mac & 0xff00) >> 8;
> +		mac	= hellcreek_read(hellcreek, HR_FDBRDM);
> +		entry.mac[3] = mac & 0xff;
> +		entry.mac[2] = (mac & 0xff00) >> 8;
> +		mac	= hellcreek_read(hellcreek, HR_FDBRDH);
> +		entry.mac[1] = mac & 0xff;
> +		entry.mac[0] = (mac & 0xff00) >> 8;
> +
> +		/* Force next entry */
> +		hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
> +
> +		/* Check valid */
> +		if (!memcmp(entry.mac, null_addr, ETH_ALEN))
> +			continue;
> +
> +		entry.portmask	= (meta & HR_FDBMDRD_PORTMASK_MASK) >>
> +			HR_FDBMDRD_PORTMASK_SHIFT;
> +		entry.is_static	= !!(meta & HR_FDBMDRD_STATIC);
> +
> +		/* Check port mask */
> +		if (!(entry.portmask & BIT(port)))
> +			continue;
> +
> +		cb(entry.mac, 0, entry.is_static, data);
> +	}
> +
> +	mutex_unlock(&hellcreek->reg_lock);
> +
> +	return 0;
> +}
> +
> +/* Default setup for DSA: VLAN <X>: CPU and Port <X> egress untagged. */
> +static int hellcreek_setup_vlan_membership(struct dsa_switch *ds, int port,
> +					   bool enabled)

This function always returns zero, so it should be void.

> +{
> +	int upstream = dsa_upstream_port(ds, port);
> +	struct hellcreek *hellcreek = ds->priv;
> +	u16 vid = port;
> +
> +	/* The CPU port is implicitly configured by configuring the front-panel
> +	 * ports.
> +	 */
> +	if (!dsa_is_user_port(ds, port))
> +		return 0;
> +

Callers already ensure that dsa_is_user_port(ds, port) is true.

> +	/* Apply vid to port as egress untagged and port vlan id */
> +	if (enabled)
> +		hellcreek_apply_vlan(hellcreek, port, vid, true, true);
> +	else
> +		hellcreek_unapply_vlan(hellcreek, port, vid);
> +
> +	/* Apply vid to cpu port as well */
> +	if (enabled)
> +		hellcreek_apply_vlan(hellcreek, upstream, vid, false, true);
> +	else
> +		hellcreek_unapply_vlan(hellcreek, upstream, vid);
> +
> +	return 0;
> +}
> +
> +static void hellcreek_setup_ingressflt(struct hellcreek *hellcreek, int port,
> +				       bool enable)
> +{
> +	struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
> +	u16 ptcfg;
> +
> +	mutex_lock(&hellcreek->reg_lock);
> +
> +	ptcfg = hellcreek_port->ptcfg;
> +
> +	if (enable)
> +		ptcfg |= HR_PTCFG_INGRESSFLT;
> +	else
> +		ptcfg &= ~HR_PTCFG_INGRESSFLT;
> +
> +	hellcreek_select_port(hellcreek, port);
> +	hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
> +	hellcreek_port->ptcfg = ptcfg;
> +
> +	mutex_unlock(&hellcreek->reg_lock);
> +}
> +
> +static void hellcreek_apply_vlan_filtering(struct hellcreek *hellcreek,
> +					   int port, bool vlan_filtering)
> +{
> +	mutex_lock(&hellcreek->ports[port].vlan_lock);
> +	if (vlan_filtering) {
> +		hellcreek_apply_queued_vlans(hellcreek, port);
> +		hellcreek_clear_queued_vlans(hellcreek, port);
> +	}
> +	hellcreek->ports[port].vlan_filtering = vlan_filtering;
> +	mutex_unlock(&hellcreek->ports[port].vlan_lock);
> +}
> +
> +static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port,
> +				    bool vlan_filtering)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +
> +	dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n",
> +		vlan_filtering ? "Enable" : "Disable", port);
> +
> +	/* Configure port to drop packages with not known vids */
> +	hellcreek_setup_ingressflt(hellcreek, port, vlan_filtering);
> +
> +	/* Drop DSA vlan membership config. The user can now do it. */
> +	hellcreek_setup_vlan_membership(ds, port, !vlan_filtering);
> +
> +	/* Apply saved vlan configurations while not filtering for port <X>. */
> +	hellcreek_apply_vlan_filtering(hellcreek, port, vlan_filtering);
> +
> +	/* Do the same for the cpu port. */
> +	hellcreek_apply_vlan_filtering(hellcreek, CPU_PORT, vlan_filtering);

I think we should create a DSA_NOTIFIER_VLAN_FILTERING so you wouldn't
have to do this, but not now.

> +
> +	return 0;
> +}
> +
> +static int hellcreek_enable_ip_core(struct hellcreek *hellcreek)
> +{
> +	int ret;
> +	u16 val;
> +
> +	mutex_lock(&hellcreek->reg_lock);
> +
> +	val = hellcreek_read(hellcreek, HR_CTRL_C);
> +	val |= HR_CTRL_C_ENABLE;
> +	hellcreek_write(hellcreek, val, HR_CTRL_C);
> +	ret = hellcreek_wait_until_transitioned(hellcreek);
> +
> +	mutex_unlock(&hellcreek->reg_lock);
> +
> +	return ret;
> +}
> +
> +static void hellcreek_setup_cpu_and_tunnel_port(struct hellcreek *hellcreek)
> +{
> +	struct hellcreek_port *tunnel_port = &hellcreek->ports[TUNNEL_PORT];
> +	struct hellcreek_port *cpu_port = &hellcreek->ports[CPU_PORT];
> +	u16 ptcfg = 0;
> +
> +	ptcfg |= HR_PTCFG_LEARNING_EN | HR_PTCFG_ADMIN_EN;
> +
> +	mutex_lock(&hellcreek->reg_lock);
> +
> +	hellcreek_select_port(hellcreek, CPU_PORT);
> +	hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
> +
> +	hellcreek_select_port(hellcreek, TUNNEL_PORT);
> +	hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
> +
> +	cpu_port->ptcfg	   = ptcfg;
> +	tunnel_port->ptcfg = ptcfg;
> +
> +	mutex_unlock(&hellcreek->reg_lock);
> +}
> +
> +static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
> +{
> +	int i;
> +
> +	/* The switch has multiple egress queues per port. The queue is selected
> +	 * via the PCP field in the VLAN header. The switch internally deals
> +	 * with traffic classes instead of PCP values and this mapping is
> +	 * configurable.
> +	 *
> +	 * The default mapping is (PCP - TC):
> +	 *  7 - 7
> +	 *  6 - 6
> +	 *  5 - 5
> +	 *  4 - 4
> +	 *  3 - 3
> +	 *  2 - 1
> +	 *  1 - 0
> +	 *  0 - 2
> +	 *
> +	 * The default should be an identity mapping.
> +	 */
> +
> +	for (i = 0; i < 8; ++i) {
> +		mutex_lock(&hellcreek->reg_lock);
> +
> +		hellcreek_select_prio(hellcreek, i);
> +		hellcreek_write(hellcreek,
> +				i << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
> +				HR_PRTCCFG);
> +
> +		mutex_unlock(&hellcreek->reg_lock);
> +	}
> +}
> +
> +static int hellcreek_setup(struct dsa_switch *ds)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	int ret, i;
> +
> +	dev_dbg(hellcreek->dev, "Set up the switch\n");
> +
> +	/* Let's go */
> +	ret = hellcreek_enable_ip_core(hellcreek);
> +	if (ret) {
> +		dev_err(hellcreek->dev, "Failed to enable IP core!\n");
> +		return ret;
> +	}
> +
> +	/* Enable CPU/Tunnel ports */
> +	hellcreek_setup_cpu_and_tunnel_port(hellcreek);
> +
> +	/* Switch config: Keep defaults, enable FDB aging and learning, and tag
> +	 * each frame from/to cpu port for DSA tagging.  Also enable the length
> +	 * aware shaping mode. This eliminates the need for Qbv guard bands.
> +	 */
> +	hellcreek_write(hellcreek, HR_SWCFG_FDBAGE_EN | HR_SWCFG_FDBLRN_EN |
> +			HR_SWCFG_ALWAYS_OBT |
> +			(HR_SWCFG_LAS_ON << HR_SWCFG_LAS_MODE_SHIFT),
> +			HR_SWCFG);
> +
> +	/* Initial vlan membership to reflect port separation */
> +	for (i = 0; i < ds->num_ports; ++i) {
> +		if (!dsa_is_user_port(ds, i))
> +			continue;
> +
> +		ret = hellcreek_setup_vlan_membership(ds, i, true);

...and therefore, the error checking below is bogus.

> +		if (ret) {
> +			dev_err(hellcreek->dev,
> +				"Failed to setup VLAN membership config!\n");
> +			return ret;
> +		}
> +	}
> +
> +	/* Configure PCP <-> TC mapping */
> +	hellcreek_setup_tc_identity_mapping(hellcreek);
> +
> +	/* Allow VLAN configurations while not filtering */
> +	ds->configure_vlan_while_not_filtering = true;
> +
> +	return 0;
> +}
> +
> +static void hellcreek_phylink_validate(struct dsa_switch *ds, int port,
> +				       unsigned long *supported,
> +				       struct phylink_link_state *state)
> +{
> +	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
> +	struct hellcreek *hellcreek = ds->priv;
> +
> +	dev_dbg(hellcreek->dev, "Phylink validate for port %d\n", port);
> +
> +	/* The MAC settings are a hardware configuration option and cannot be
> +	 * changed at run time or by strapping. Therefore the attached PHYs
> +	 * should be programmed to only advertise settings which are supported
> +	 * by the hardware.
> +	 */
> +	if (hellcreek->pdata->is_100_mbits)
> +		phylink_set(mask, 100baseT_Full);
> +	else
> +		phylink_set(mask, 1000baseT_Full);
> +
> +	bitmap_and(supported, supported, mask,
> +		   __ETHTOOL_LINK_MODE_MASK_NBITS);
> +	bitmap_and(state->advertising, state->advertising, mask,
> +		   __ETHTOOL_LINK_MODE_MASK_NBITS);
> +}
> +
> +static const struct dsa_switch_ops hellcreek_ds_ops = {
> +	.get_tag_protocol    = hellcreek_get_tag_protocol,
> +	.setup		     = hellcreek_setup,
> +	.get_strings	     = hellcreek_get_strings,
> +	.get_ethtool_stats   = hellcreek_get_ethtool_stats,
> +	.get_sset_count	     = hellcreek_get_sset_count,
> +	.port_enable	     = hellcreek_port_enable,
> +	.port_disable	     = hellcreek_port_disable,
> +	.port_vlan_filtering = hellcreek_vlan_filtering,
> +	.port_vlan_prepare   = hellcreek_vlan_prepare,
> +	.port_vlan_add	     = hellcreek_vlan_add,
> +	.port_vlan_del	     = hellcreek_vlan_del,
> +	.port_fdb_dump	     = hellcreek_fdb_dump,
> +	.port_fdb_add	     = hellcreek_fdb_add,
> +	.port_fdb_del	     = hellcreek_fdb_del,
> +	.port_bridge_join    = hellcreek_port_bridge_join,
> +	.port_bridge_leave   = hellcreek_port_bridge_leave,
> +	.port_stp_state_set  = hellcreek_port_stp_state_set,
> +	.phylink_validate    = hellcreek_phylink_validate,
> +};
> +
> +static int hellcreek_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct hellcreek *hellcreek;
> +	struct resource *res;
> +	int ret, i;
> +
> +	hellcreek = devm_kzalloc(dev, sizeof(*hellcreek), GFP_KERNEL);
> +	if (!hellcreek)
> +		return -ENOMEM;
> +
> +	hellcreek->vidmbrcfg = devm_kcalloc(dev, 4096,

VLAN_N_VID

> +					    sizeof(*hellcreek->vidmbrcfg),
> +					    GFP_KERNEL);
> +	if (!hellcreek->vidmbrcfg)
> +		return -ENOMEM;
> +
> +	hellcreek->pdata = of_device_get_match_data(dev);
> +
> +	hellcreek->ports = devm_kcalloc(dev, hellcreek->pdata->num_ports,
> +					sizeof(*hellcreek->ports),
> +					GFP_KERNEL);
> +	if (!hellcreek->ports)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
> +		struct hellcreek_port *port = &hellcreek->ports[i];
> +
> +		port->counter_values =
> +			devm_kcalloc(dev,
> +				     ARRAY_SIZE(hellcreek_counter),
> +				     sizeof(*port->counter_values),
> +				     GFP_KERNEL);
> +		if (!port->counter_values)
> +			return -ENOMEM;
> +
> +		port->hellcreek	     = hellcreek;
> +		port->vlan_filtering = 0;
> +		port->port	     = i;
> +
> +		INIT_LIST_HEAD(&port->vlan_list);
> +		mutex_init(&port->vlan_lock);
> +	}
> +
> +	mutex_init(&hellcreek->reg_lock);
> +
> +	hellcreek->dev = dev;
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tsn");
> +	if (!res) {
> +		dev_err(dev, "No memory region provided!\n");
> +		return -ENODEV;
> +	}
> +
> +	hellcreek->base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(hellcreek->base)) {
> +		dev_err(dev, "No memory available!\n");
> +		return PTR_ERR(hellcreek->base);
> +	}
> +
> +	ret = hellcreek_detect(hellcreek);
> +	if (ret) {
> +		dev_err(dev, "No (known) chip found!\n");
> +		return ret;
> +	}
> +
> +	ret = hellcreek_wait_until_ready(hellcreek);
> +	if (ret) {
> +		dev_err(dev, "Switch didn't become ready!\n");
> +		return ret;
> +	}
> +
> +	hellcreek_feature_detect(hellcreek);
> +
> +	hellcreek->ds = devm_kzalloc(dev, sizeof(*hellcreek->ds), GFP_KERNEL);
> +	if (!hellcreek->ds)
> +		return -ENOMEM;
> +
> +	hellcreek->ds->dev	     = dev;
> +	hellcreek->ds->priv	     = hellcreek;
> +	hellcreek->ds->ops	     = &hellcreek_ds_ops;
> +	hellcreek->ds->num_ports     = hellcreek->pdata->num_ports;
> +	hellcreek->ds->num_tx_queues = HELLCREEK_NUM_EGRESS_QUEUES;
> +
> +	ret = dsa_register_switch(hellcreek->ds);
> +	if (ret) {
> +		dev_err(dev, "Unable to register switch\n");
> +		return ret;
> +	}
> +
> +	platform_set_drvdata(pdev, hellcreek);
> +
> +	return 0;
> +}
> +
> +static int hellcreek_remove(struct platform_device *pdev)
> +{
> +	struct hellcreek *hellcreek = platform_get_drvdata(pdev);
> +
> +	dsa_unregister_switch(hellcreek->ds);
> +	platform_set_drvdata(pdev, NULL);
> +
> +	return 0;
> +}
> +
> +static const struct hellcreek_platform_data de1soc_r1_pdata = {
> +	.num_ports	 = 4,
> +	.is_100_mbits	 = 1,
> +	.qbv_support	 = 1,
> +	.qbv_on_cpu_port = 1,

Why does this matter?

> +	.qbu_support	 = 0,
> +	.module_id	 = 0x4c30,
> +};
> +
> +static const struct of_device_id hellcreek_of_match[] = {
> +	{
> +		.compatible = "hirschmann,hellcreek-de1soc-r1",
> +		.data	    = &de1soc_r1_pdata,
> +	},
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, hellcreek_of_match);
> +
> +static struct platform_driver hellcreek_driver = {
> +	.probe	= hellcreek_probe,
> +	.remove = hellcreek_remove,
> +	.driver = {
> +		.name = "hellcreek",
> +		.of_match_table = hellcreek_of_match,
> +	},
> +};
> +module_platform_driver(hellcreek_driver);
> +
> +MODULE_AUTHOR("Kurt Kanzenbach <kurt@linutronix.de>");
> +MODULE_DESCRIPTION("Hirschmann Hellcreek driver");
> +MODULE_LICENSE("Dual MIT/GPL");
> diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h
> new file mode 100644
> index 000000000000..d57055aadc6f
> --- /dev/null
> +++ b/drivers/net/dsa/hirschmann/hellcreek.h
> @@ -0,0 +1,255 @@
> +/* SPDX-License-Identifier: (GPL-2.0 or MIT) */
> +/*
> + * DSA driver for:
> + * Hirschmann Hellcreek TSN switch.
> + *
> + * Copyright (C) 2019,2020 Linutronix GmbH
> + * Author Kurt Kanzenbach <kurt@linutronix.de>
> + */
> +
> +#ifndef _HELLCREEK_H_
> +#define _HELLCREEK_H_
> +
> +#include <linux/bitops.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/ptp_clock_kernel.h>
> +#include <linux/timecounter.h>
> +#include <linux/mutex.h>

Could you sort alphabetically?

> +#include <linux/platform_data/hirschmann-hellcreek.h>
> +#include <net/dsa.h>
> +
> +/* Ports:
> + *  - 0: CPU
> + *  - 1: Tunnel
> + *  - 2: TSN front port 1
> + *  - 3: TSN front port 2
> + *  - ...
> + */
> +#define CPU_PORT			0
> +#define TUNNEL_PORT			1

What's a tunnel port exactly?

> +
> +#define HELLCREEK_VLAN_NO_MEMBER	0x0
> +#define HELLCREEK_VLAN_UNTAGGED_MEMBER	0x1
> +#define HELLCREEK_VLAN_TAGGED_MEMBER	0x3
> +#define HELLCREEK_NUM_EGRESS_QUEUES	8
> +
> +/* Register definitions */
> +#define HR_MODID_C			(0 * 2)
> +#define HR_REL_L_C			(1 * 2)
> +#define HR_REL_H_C			(2 * 2)
> +#define HR_BLD_L_C			(3 * 2)
> +#define HR_BLD_H_C			(4 * 2)
> +#define HR_CTRL_C			(5 * 2)
> +#define HR_CTRL_C_READY			BIT(14)
> +#define HR_CTRL_C_TRANSITION		BIT(13)
> +#define HR_CTRL_C_ENABLE		BIT(0)
> +
> +#define HR_PSEL				(0xa6 * 2)
> +#define HR_PSEL_PTWSEL_SHIFT		4
> +#define HR_PSEL_PTWSEL_MASK		GENMASK(5, 4)
> +#define HR_PSEL_PRTCWSEL_SHIFT		0
> +#define HR_PSEL_PRTCWSEL_MASK		GENMASK(2, 0)
> +
> +#define HR_PTCFG			(0xa7 * 2)
> +#define HR_PTCFG_MLIMIT_EN		BIT(13)
> +#define HR_PTCFG_UMC_FLT		BIT(10)
> +#define HR_PTCFG_UUC_FLT		BIT(9)
> +#define HR_PTCFG_UNTRUST		BIT(8)
> +#define HR_PTCFG_TAG_REQUIRED		BIT(7)
> +#define HR_PTCFG_PPRIO_SHIFT		4
> +#define HR_PTCFG_PPRIO_MASK		GENMASK(6, 4)
> +#define HR_PTCFG_INGRESSFLT		BIT(3)
> +#define HR_PTCFG_BLOCKED		BIT(2)
> +#define HR_PTCFG_LEARNING_EN		BIT(1)
> +#define HR_PTCFG_ADMIN_EN		BIT(0)
> +
> +#define HR_PRTCCFG			(0xa8 * 2)
> +#define HR_PRTCCFG_PCP_TC_MAP_SHIFT	0
> +#define HR_PRTCCFG_PCP_TC_MAP_MASK	GENMASK(2, 0)
> +
> +#define HR_CSEL				(0x8d * 2)
> +#define HR_CSEL_SHIFT			0
> +#define HR_CSEL_MASK			GENMASK(7, 0)
> +#define HR_CRDL				(0x8e * 2)
> +#define HR_CRDH				(0x8f * 2)
> +
> +#define HR_SWTRC_CFG			(0x90 * 2)
> +#define HR_SWTRC0			(0x91 * 2)
> +#define HR_SWTRC1			(0x92 * 2)
> +#define HR_PFREE			(0x93 * 2)
> +#define HR_MFREE			(0x94 * 2)
> +
> +#define HR_FDBAGE			(0x97 * 2)
> +#define HR_FDBMAX			(0x98 * 2)
> +#define HR_FDBRDL			(0x99 * 2)
> +#define HR_FDBRDM			(0x9a * 2)
> +#define HR_FDBRDH			(0x9b * 2)
> +
> +#define HR_FDBMDRD			(0x9c * 2)
> +#define HR_FDBMDRD_PORTMASK_SHIFT	0
> +#define HR_FDBMDRD_PORTMASK_MASK	GENMASK(3, 0)
> +#define HR_FDBMDRD_AGE_SHIFT		4
> +#define HR_FDBMDRD_AGE_MASK		GENMASK(7, 4)
> +#define HR_FDBMDRD_OBT			BIT(8)
> +#define HR_FDBMDRD_PASS_BLOCKED		BIT(9)
> +#define HR_FDBMDRD_STATIC		BIT(11)
> +#define HR_FDBMDRD_REPRIO_TC_SHIFT	12
> +#define HR_FDBMDRD_REPRIO_TC_MASK	GENMASK(14, 12)
> +#define HR_FDBMDRD_REPRIO_EN		BIT(15)
> +
> +#define HR_FDBWDL			(0x9d * 2)
> +#define HR_FDBWDM			(0x9e * 2)
> +#define HR_FDBWDH			(0x9f * 2)
> +#define HR_FDBWRM0			(0xa0 * 2)
> +#define HR_FDBWRM0_PORTMASK_SHIFT	0
> +#define HR_FDBWRM0_PORTMASK_MASK	GENMASK(3, 0)
> +#define HR_FDBWRM0_OBT			BIT(8)
> +#define HR_FDBWRM0_PASS_BLOCKED		BIT(9)
> +#define HR_FDBWRM0_REPRIO_TC_SHIFT	12
> +#define HR_FDBWRM0_REPRIO_TC_MASK	GENMASK(14, 12)
> +#define HR_FDBWRM0_REPRIO_EN		BIT(15)
> +#define HR_FDBWRM1			(0xa1 * 2)
> +
> +#define HR_FDBWRCMD			(0xa2 * 2)
> +#define HR_FDBWRCMD_FDBDEL		BIT(9)
> +
> +#define HR_SWCFG			(0xa3 * 2)
> +#define HR_SWCFG_GM_STATEMD		BIT(15)
> +#define HR_SWCFG_LAS_MODE_SHIFT		12
> +#define HR_SWCFG_LAS_MODE_MASK		GENMASK(13, 12)
> +#define HR_SWCFG_LAS_OFF		(0x00)
> +#define HR_SWCFG_LAS_ON			(0x01)
> +#define HR_SWCFG_LAS_STATIC		(0x10)
> +#define HR_SWCFG_CT_EN			BIT(11)
> +#define HR_SWCFG_LAN_UNAWARE		BIT(10)
> +#define HR_SWCFG_ALWAYS_OBT		BIT(9)
> +#define HR_SWCFG_FDBAGE_EN		BIT(5)
> +#define HR_SWCFG_FDBLRN_EN		BIT(4)
> +
> +#define HR_SWSTAT			(0xa4 * 2)
> +#define HR_SWSTAT_FAIL			BIT(4)
> +#define HR_SWSTAT_BUSY			BIT(0)
> +
> +#define HR_SWCMD			(0xa5 * 2)
> +#define HW_SWCMD_FLUSH			BIT(0)
> +
> +#define HR_VIDCFG			(0xaa * 2)
> +#define HR_VIDCFG_VID_SHIFT		0
> +#define HR_VIDCFG_VID_MASK		GENMASK(11, 0)
> +#define HR_VIDCFG_PVID			BIT(12)
> +
> +#define HR_VIDMBRCFG			(0xab * 2)
> +#define HR_VIDMBRCFG_P0MBR_SHIFT	0
> +#define HR_VIDMBRCFG_P0MBR_MASK		GENMASK(1, 0)
> +#define HR_VIDMBRCFG_P1MBR_SHIFT	2
> +#define HR_VIDMBRCFG_P1MBR_MASK		GENMASK(3, 2)
> +#define HR_VIDMBRCFG_P2MBR_SHIFT	4
> +#define HR_VIDMBRCFG_P2MBR_MASK		GENMASK(5, 4)
> +#define HR_VIDMBRCFG_P3MBR_SHIFT	6
> +#define HR_VIDMBRCFG_P3MBR_MASK		GENMASK(7, 6)
> +
> +#define HR_FEABITS0			(0xac * 2)
> +#define HR_FEABITS0_FDBBINS_SHIFT	4
> +#define HR_FEABITS0_FDBBINS_MASK	GENMASK(7, 4)
> +#define HR_FEABITS0_PCNT_SHIFT		8
> +#define HR_FEABITS0_PCNT_MASK		GENMASK(11, 8)
> +#define HR_FEABITS0_MCNT_SHIFT		12
> +#define HR_FEABITS0_MCNT_MASK		GENMASK(15, 12)
> +
> +#define TR_QTRACK			(0xb1 * 2)
> +#define TR_TGDVER			(0xb3 * 2)
> +#define TR_TGDVER_REV_MIN_MASK		GENMASK(7, 0)
> +#define TR_TGDVER_REV_MIN_SHIFT		0
> +#define TR_TGDVER_REV_MAJ_MASK		GENMASK(15, 8)
> +#define TR_TGDVER_REV_MAJ_SHIFT		8
> +#define TR_TGDSEL			(0xb4 * 2)
> +#define TR_TGDSEL_TDGSEL_MASK		GENMASK(1, 0)
> +#define TR_TGDSEL_TDGSEL_SHIFT		0
> +#define TR_TGDCTRL			(0xb5 * 2)
> +#define TR_TGDCTRL_GATE_EN		BIT(0)
> +#define TR_TGDCTRL_CYC_SNAP		BIT(4)
> +#define TR_TGDCTRL_SNAP_EST		BIT(5)
> +#define TR_TGDCTRL_ADMINGATESTATES_MASK	GENMASK(15, 8)
> +#define TR_TGDCTRL_ADMINGATESTATES_SHIFT	8
> +#define TR_TGDSTAT0			(0xb6 * 2)
> +#define TR_TGDSTAT1			(0xb7 * 2)
> +#define TR_ESTWRL			(0xb8 * 2)
> +#define TR_ESTWRH			(0xb9 * 2)
> +#define TR_ESTCMD			(0xba * 2)
> +#define TR_ESTCMD_ESTSEC_MASK		GENMASK(2, 0)
> +#define TR_ESTCMD_ESTSEC_SHIFT		0
> +#define TR_ESTCMD_ESTARM		BIT(4)
> +#define TR_ESTCMD_ESTSWCFG		BIT(5)
> +#define TR_EETWRL			(0xbb * 2)
> +#define TR_EETWRH			(0xbc * 2)
> +#define TR_EETCMD			(0xbd * 2)
> +#define TR_EETCMD_EETSEC_MASK		GEMASK(2, 0)
> +#define TR_EETCMD_EETSEC_SHIFT		0
> +#define TR_EETCMD_EETARM		BIT(4)
> +#define TR_CTWRL			(0xbe * 2)
> +#define TR_CTWRH			(0xbf * 2)
> +#define TR_LCNSL			(0xc1 * 2)
> +#define TR_LCNSH			(0xc2 * 2)
> +#define TR_LCS				(0xc3 * 2)
> +#define TR_GCLDAT			(0xc4 * 2)
> +#define TR_GCLDAT_GCLWRGATES_MASK	GENMASK(7, 0)
> +#define TR_GCLDAT_GCLWRGATES_SHIFT	0
> +#define TR_GCLDAT_GCLWRLAST		BIT(8)
> +#define TR_GCLDAT_GCLOVRI		BIT(9)
> +#define TR_GCLTIL			(0xc5 * 2)
> +#define TR_GCLTIH			(0xc6 * 2)
> +#define TR_GCLCMD			(0xc7 * 2)
> +#define TR_GCLCMD_GCLWRADR_MASK		GENMASK(7, 0)
> +#define TR_GCLCMD_GCLWRADR_SHIFT	0
> +#define TR_GCLCMD_INIT_GATE_STATES_MASK	GENMASK(15, 8)
> +#define TR_GCLCMD_INIT_GATE_STATES_SHIFT	8
> +
> +struct hellcreek_counter {
> +	u8 offset;
> +	const char *name;
> +};
> +
> +struct hellcreek;
> +
> +struct hellcreek_port {
> +	struct hellcreek *hellcreek;
> +	struct list_head vlan_list;
> +	struct mutex vlan_lock;	/* VLAN list mutex */
> +	int port;
> +	int vlan_filtering;	/* Is VLAN filtering activated */
> +	u16 ptcfg;		/* ptcfg shadow */
> +	u64 *counter_values;
> +};
> +
> +struct hellcreek_fdb_entry {
> +	size_t idx;
> +	unsigned char mac[ETH_ALEN];
> +	u8 portmask;
> +	u8 age;
> +	u8 is_obt;
> +	u8 pass_blocked;
> +	u8 is_static;
> +	u8 reprio_tc;
> +	u8 reprio_en;
> +};
> +
> +struct hellcreek_vlan {
> +	struct list_head list;
> +	int pvid;
> +	int untagged;
> +	u16 vid;
> +};
> +
> +struct hellcreek {
> +	const struct hellcreek_platform_data *pdata;
> +	struct device *dev;
> +	struct dsa_switch *ds;
> +	struct hellcreek_port *ports;
> +	struct mutex reg_lock;	/* Switch IP register lock */
> +	void __iomem *base;
> +	u8 *vidmbrcfg;		/* vidmbrcfg shadow */
> +	size_t fdb_entries;
> +};
> +
> +#endif /* _HELLCREEK_H_ */
> diff --git a/include/linux/platform_data/hirschmann-hellcreek.h b/include/linux/platform_data/hirschmann-hellcreek.h
> new file mode 100644
> index 000000000000..388846766bb2
> --- /dev/null
> +++ b/include/linux/platform_data/hirschmann-hellcreek.h
> @@ -0,0 +1,23 @@
> +/* SPDX-License-Identifier: (GPL-2.0 or MIT) */
> +/*
> + * Hirschmann Hellcreek TSN switch platform data.
> + *
> + * Copyright (C) 2020 Linutronix GmbH
> + * Author Kurt Kanzenbach <kurt@linutronix.de>
> + */
> +
> +#ifndef _HIRSCHMANN_HELLCREEK_H_
> +#define _HIRSCHMANN_HELLCREEK_H_
> +
> +#include <linux/types.h>
> +
> +struct hellcreek_platform_data {
> +	int num_ports;		/* Amount of switch ports */
> +	int is_100_mbits;	/* Is it configured to 100 or 1000 mbit/s */
> +	int qbv_support;	/* Qbv support on front TSN ports */
> +	int qbv_on_cpu_port;	/* Qbv support on the CPU port */
> +	int qbu_support;	/* Qbu support on front TSN ports */
> +	u16 module_id;		/* Module identificaton */
> +};
> +
> +#endif /* _HIRSCHMANN_HELLCREEK_H_ */
> -- 
> 2.20.1
> 

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-04 11:29 ` [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping Kurt Kanzenbach
@ 2020-10-04 14:30   ` Vladimir Oltean
  2020-10-06  6:27     ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-04 14:30 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Sun, Oct 04, 2020 at 01:29:08PM +0200, Kurt Kanzenbach wrote:
> From: Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>
> 
> The switch has the ability to take hardware generated time stamps per port for
> PTPv2 event messages in Rx and Tx direction. That is useful for achieving needed
> time synchronization precision for TSN devices/switches. So add support for it.
> 
> There are two directions:
> 
>  * RX
> 
>    The switch has a single register per port to capture a timestamp. That
>    mechanism is not used due to correlation problems. If the software processing
>    is too slow and a PTPv2 event message is received before the previous one has
>    been processed, false timestamps will be captured. Therefore, the switch can
>    do "inline" timestamping which means it can insert the nanoseconds part of
>    the timestamp directly into the PTPv2 event message. The reserved field (4
>    bytes) is leveraged for that. This might not be in accordance with (older)
>    PTP standards, but is the only way to get reliable results.
> 
>  * TX
> 
>    In Tx direction there is no correlation problem, because the software and the
>    driver has to ensure that only one event message is "on the fly". However,
>    the switch provides also a mechanism to check whether a timestamp is
>    lost. That can only happen when a timestamp is read and at this point another
>    message is timestamped. So, that lost bit is checked just in case to indicate
>    to the user that the driver or the software is somewhat buggy.
> 
> Signed-off-by: Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>
> Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
> Acked-by: Richard Cochran <richardcochran@gmail.com>
> ---
>  drivers/net/dsa/hirschmann/Makefile           |   1 +
>  drivers/net/dsa/hirschmann/hellcreek.c        |  15 +
>  drivers/net/dsa/hirschmann/hellcreek.h        |  25 +
>  .../net/dsa/hirschmann/hellcreek_hwtstamp.c   | 479 ++++++++++++++++++
>  .../net/dsa/hirschmann/hellcreek_hwtstamp.h   |  58 +++
>  drivers/net/dsa/hirschmann/hellcreek_ptp.c    |  48 +-
>  drivers/net/dsa/hirschmann/hellcreek_ptp.h    |   4 +
>  7 files changed, 616 insertions(+), 14 deletions(-)
>  create mode 100644 drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c
>  create mode 100644 drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h
> 
> diff --git a/drivers/net/dsa/hirschmann/Makefile b/drivers/net/dsa/hirschmann/Makefile
> index 39de02a03640..f4075c2998b5 100644
> --- a/drivers/net/dsa/hirschmann/Makefile
> +++ b/drivers/net/dsa/hirschmann/Makefile
> @@ -2,3 +2,4 @@
>  obj-$(CONFIG_NET_DSA_HIRSCHMANN_HELLCREEK)	+= hellcreek_sw.o
>  hellcreek_sw-objs := hellcreek.o
>  hellcreek_sw-objs += hellcreek_ptp.o
> +hellcreek_sw-objs += hellcreek_hwtstamp.o
> diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
> index 3e1040039a96..e9ec9dcca0d8 100644
> --- a/drivers/net/dsa/hirschmann/hellcreek.c
> +++ b/drivers/net/dsa/hirschmann/hellcreek.c
> @@ -25,6 +25,7 @@
>  
>  #include "hellcreek.h"
>  #include "hellcreek_ptp.h"
> +#include "hellcreek_hwtstamp.h"
>  
>  static const struct hellcreek_counter hellcreek_counter[] = {
>  	{ 0x00, "RxFiltered", },
> @@ -1191,6 +1192,11 @@ static const struct dsa_switch_ops hellcreek_ds_ops = {
>  	.port_bridge_leave   = hellcreek_port_bridge_leave,
>  	.port_stp_state_set  = hellcreek_port_stp_state_set,
>  	.phylink_validate    = hellcreek_phylink_validate,
> +	.port_hwtstamp_set   = hellcreek_port_hwtstamp_set,
> +	.port_hwtstamp_get   = hellcreek_port_hwtstamp_get,
> +	.port_txtstamp	     = hellcreek_port_txtstamp,
> +	.port_rxtstamp	     = hellcreek_port_rxtstamp,
> +	.get_ts_info	     = hellcreek_get_ts_info,
>  };
>  
>  static int hellcreek_probe(struct platform_device *pdev)
> @@ -1302,10 +1308,18 @@ static int hellcreek_probe(struct platform_device *pdev)
>  		goto err_ptp_setup;
>  	}
>  
> +	ret = hellcreek_hwtstamp_setup(hellcreek);
> +	if (ret) {
> +		dev_err(dev, "Failed to setup hardware timestamping!\n");
> +		goto err_tstamp_setup;
> +	}
> +
>  	platform_set_drvdata(pdev, hellcreek);
>  
>  	return 0;
>  
> +err_tstamp_setup:
> +	hellcreek_ptp_free(hellcreek);
>  err_ptp_setup:
>  	dsa_unregister_switch(hellcreek->ds);
>  
> @@ -1316,6 +1330,7 @@ static int hellcreek_remove(struct platform_device *pdev)
>  {
>  	struct hellcreek *hellcreek = platform_get_drvdata(pdev);
>  
> +	hellcreek_hwtstamp_free(hellcreek);
>  	hellcreek_ptp_free(hellcreek);
>  	dsa_unregister_switch(hellcreek->ds);
>  	platform_set_drvdata(pdev, NULL);
> diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h
> index 0c95577b81ad..0b42c15736ce 100644
> --- a/drivers/net/dsa/hirschmann/hellcreek.h
> +++ b/drivers/net/dsa/hirschmann/hellcreek.h
> @@ -213,6 +213,28 @@ struct hellcreek_counter {
>  
>  struct hellcreek;
>  
> +/* State flags for hellcreek_port_hwtstamp::state */
> +enum {
> +	HELLCREEK_HWTSTAMP_ENABLED,
> +	HELLCREEK_HWTSTAMP_TX_IN_PROGRESS,
> +};
> +
> +/* A structure to hold hardware timestamping information per port */
> +struct hellcreek_port_hwtstamp {
> +	/* Timestamping state */
> +	unsigned long state;
> +
> +	/* Resources for receive timestamping */
> +	struct sk_buff_head rx_queue; /* For synchronization messages */
> +
> +	/* Resources for transmit timestamping */
> +	unsigned long tx_tstamp_start;
> +	struct sk_buff *tx_skb;
> +
> +	/* Current timestamp configuration */
> +	struct hwtstamp_config tstamp_config;
> +};
> +
>  struct hellcreek_port {
>  	struct hellcreek *hellcreek;
>  	struct list_head vlan_list;
> @@ -221,6 +243,9 @@ struct hellcreek_port {
>  	int vlan_filtering;	/* Is VLAN filtering activated */
>  	u16 ptcfg;		/* ptcfg shadow */
>  	u64 *counter_values;
> +
> +	/* Per-port timestamping resources */
> +	struct hellcreek_port_hwtstamp port_hwtstamp;
>  };
>  
>  struct hellcreek_fdb_entry {
> diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c
> new file mode 100644
> index 000000000000..69dd9a2e8bb6
> --- /dev/null
> +++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c
> @@ -0,0 +1,479 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> +/*
> + * DSA driver for:
> + * Hirschmann Hellcreek TSN switch.
> + *
> + * Copyright (C) 2019,2020 Hochschule Offenburg
> + * Copyright (C) 2019,2020 Linutronix GmbH
> + * Authors: Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>
> + *	    Kurt Kanzenbach <kurt@linutronix.de>
> + */
> +
> +#include <linux/ptp_classify.h>
> +
> +#include "hellcreek.h"
> +#include "hellcreek_hwtstamp.h"
> +#include "hellcreek_ptp.h"
> +
> +int hellcreek_get_ts_info(struct dsa_switch *ds, int port,
> +			  struct ethtool_ts_info *info)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +
> +	info->phc_index = hellcreek->ptp_clock ?
> +		ptp_clock_index(hellcreek->ptp_clock) : -1;
> +	info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
> +		SOF_TIMESTAMPING_RX_HARDWARE |
> +		SOF_TIMESTAMPING_RAW_HARDWARE;
> +
> +	/* enabled tx timestamping */
> +	info->tx_types = BIT(HWTSTAMP_TX_ON);
> +
> +	/* L2 & L4 PTPv2 event rx messages are timestamped */
> +	info->rx_filters = BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
> +
> +	return 0;
> +}
> +
> +/* Enabling/disabling TX and RX HW timestamping for different PTP messages is
> + * not available in the switch. Thus, this function only serves as a check if
> + * the user requested what is actually available or not
> + */

Correct me if I'm wrong, but to the user it makes zero difference
whether the hardware takes timestamps or not. What matters is whether
the skb will be delivered to the stack with a hardware timestamp or not,
so you should definitely accept a hwtstamp_config with TX and RX
timestamping disabled.

> +static int hellcreek_set_hwtstamp_config(struct hellcreek *hellcreek, int port,
> +					 struct hwtstamp_config *config)
> +{
> +	struct hellcreek_port_hwtstamp *ps =
> +		&hellcreek->ports[port].port_hwtstamp;
> +	bool tx_tstamp_enable = false;
> +	bool rx_tstamp_enable = false;
> +
> +	/* Interaction with the timestamp hardware is prevented here.  It is
> +	 * enabled when this config function ends successfully
> +	 */
> +	clear_bit_unlock(HELLCREEK_HWTSTAMP_ENABLED, &ps->state);
> +
> +	/* Reserved for future extensions */
> +	if (config->flags)
> +		return -EINVAL;
> +
> +	switch (config->tx_type) {
> +	case HWTSTAMP_TX_ON:
> +		tx_tstamp_enable = true;
> +		break;
> +
> +	/* TX HW timestamping can't be disabled on the switch */
> +	case HWTSTAMP_TX_OFF:
> +		config->tx_type = HWTSTAMP_TX_ON;
> +		break;
> +
> +	default:
> +		return -ERANGE;
> +	}
> +
> +	switch (config->rx_filter) {
> +	/* RX HW timestamping can't be disabled on the switch */
> +	case HWTSTAMP_FILTER_NONE:
> +		config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
> +		break;
> +
> +	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
> +	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
> +	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
> +	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
> +	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
> +	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
> +	case HWTSTAMP_FILTER_PTP_V2_EVENT:
> +	case HWTSTAMP_FILTER_PTP_V2_SYNC:
> +	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
> +		config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
> +		rx_tstamp_enable = true;
> +		break;
> +
> +	/* RX HW timestamping can't be enabled for all messages on the switch */
> +	case HWTSTAMP_FILTER_ALL:
> +		config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
> +		break;
> +
> +	default:
> +		return -ERANGE;
> +	}
> +
> +	if (!tx_tstamp_enable)
> +		return -ERANGE;
> +
> +	if (!rx_tstamp_enable)
> +		return -ERANGE;
> +
> +	/* If this point is reached, then the requested hwtstamp config is
> +	 * compatible with the hwtstamp offered by the switch.  Therefore,
> +	 * enable the interaction with the HW timestamping
> +	 */
> +	set_bit(HELLCREEK_HWTSTAMP_ENABLED, &ps->state);
> +
> +	return 0;
> +}
> +
> +int hellcreek_port_hwtstamp_set(struct dsa_switch *ds, int port,
> +				struct ifreq *ifr)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port_hwtstamp *ps;
> +	struct hwtstamp_config config;
> +	int err;
> +
> +	ps = &hellcreek->ports[port].port_hwtstamp;
> +
> +	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
> +		return -EFAULT;
> +
> +	err = hellcreek_set_hwtstamp_config(hellcreek, port, &config);
> +	if (err)
> +		return err;
> +
> +	/* Save the chosen configuration to be returned later */
> +	memcpy(&ps->tstamp_config, &config, sizeof(config));
> +
> +	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
> +		-EFAULT : 0;
> +}
> +
> +int hellcreek_port_hwtstamp_get(struct dsa_switch *ds, int port,
> +				struct ifreq *ifr)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port_hwtstamp *ps;
> +	struct hwtstamp_config *config;
> +
> +	ps = &hellcreek->ports[port].port_hwtstamp;
> +	config = &ps->tstamp_config;
> +
> +	return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
> +		-EFAULT : 0;
> +}
> +
> +/* Returns a pointer to the PTP header if the caller should time stamp, or NULL
> + * if the caller should not.
> + */
> +static struct ptp_header *hellcreek_should_tstamp(struct hellcreek *hellcreek,
> +						  int port, struct sk_buff *skb,
> +						  unsigned int type)
> +{
> +	struct hellcreek_port_hwtstamp *ps =
> +		&hellcreek->ports[port].port_hwtstamp;
> +	struct ptp_header *hdr;
> +
> +	hdr = ptp_parse_header(skb, type);
> +	if (!hdr)
> +		return NULL;
> +
> +	if (!test_bit(HELLCREEK_HWTSTAMP_ENABLED, &ps->state))
> +		return NULL;
> +
> +	return hdr;
> +}
> +
> +static u64 hellcreek_get_reserved_field(const struct ptp_header *hdr)
> +{
> +	return be32_to_cpu(hdr->reserved2);
> +}
> +
> +static void hellcreek_clear_reserved_field(struct ptp_header *hdr)
> +{
> +	hdr->reserved2 = 0;
> +}
> +
> +static int hellcreek_ptp_hwtstamp_available(struct hellcreek *hellcreek,
> +					    unsigned int ts_reg)
> +{
> +	u16 status;
> +
> +	status = hellcreek_ptp_read(hellcreek, ts_reg);
> +
> +	if (status & PR_TS_STATUS_TS_LOST)
> +		dev_err(hellcreek->dev,
> +			"Tx time stamp lost! This should never happen!\n");
> +
> +	/* If hwtstamp is not available, this means the previous hwtstamp was
> +	 * successfully read, and the one we need is not yet available
> +	 */
> +	return (status & PR_TS_STATUS_TS_AVAIL) ? 1 : 0;
> +}
> +
> +/* Get nanoseconds timestamp from timestamping unit */
> +static u64 hellcreek_ptp_hwtstamp_read(struct hellcreek *hellcreek,
> +				       unsigned int ts_reg)
> +{
> +	u16 nsl, nsh;
> +
> +	nsh = hellcreek_ptp_read(hellcreek, ts_reg);
> +	nsh = hellcreek_ptp_read(hellcreek, ts_reg);
> +	nsh = hellcreek_ptp_read(hellcreek, ts_reg);
> +	nsh = hellcreek_ptp_read(hellcreek, ts_reg);
> +	nsl = hellcreek_ptp_read(hellcreek, ts_reg);
> +
> +	return (u64)nsl | ((u64)nsh << 16);
> +}
> +
> +static int hellcreek_txtstamp_work(struct hellcreek *hellcreek,
> +				   struct hellcreek_port_hwtstamp *ps, int port)
> +{
> +	struct skb_shared_hwtstamps shhwtstamps;
> +	unsigned int status_reg, data_reg;
> +	struct sk_buff *tmp_skb;
> +	int ts_status;
> +	u64 ns = 0;
> +
> +	if (!ps->tx_skb)
> +		return 0;
> +
> +	switch (port) {
> +	case 2:
> +		status_reg = PR_TS_TX_P1_STATUS_C;
> +		data_reg   = PR_TS_TX_P1_DATA_C;
> +		break;
> +	case 3:
> +		status_reg = PR_TS_TX_P2_STATUS_C;
> +		data_reg   = PR_TS_TX_P2_DATA_C;
> +		break;
> +	default:
> +		dev_err(hellcreek->dev, "Wrong port for timestamping!\n");
> +		return 0;
> +	}
> +
> +	ts_status = hellcreek_ptp_hwtstamp_available(hellcreek, status_reg);
> +
> +	/* Not available yet? */
> +	if (ts_status == 0) {
> +		/* Check whether the operation of reading the tx timestamp has
> +		 * exceeded its allowed period
> +		 */
> +		if (time_is_before_jiffies(ps->tx_tstamp_start +
> +					   TX_TSTAMP_TIMEOUT)) {
> +			dev_err(hellcreek->dev,
> +				"Timeout while waiting for Tx timestamp!\n");
> +			goto free_and_clear_skb;
> +		}
> +
> +		/* The timestamp should be available quickly, while getting it
> +		 * in high priority. Restart the work
> +		 */
> +		return 1;
> +	}
> +
> +	mutex_lock(&hellcreek->ptp_lock);
> +	ns  = hellcreek_ptp_hwtstamp_read(hellcreek, data_reg);
> +	ns += hellcreek_ptp_gettime_seconds(hellcreek, ns);
> +	mutex_unlock(&hellcreek->ptp_lock);
> +
> +	/* Now we have the timestamp in nanoseconds, store it in the correct
> +	 * structure in order to send it to the user
> +	 */
> +	memset(&shhwtstamps, 0, sizeof(shhwtstamps));
> +	shhwtstamps.hwtstamp = ns_to_ktime(ns);
> +
> +	tmp_skb = ps->tx_skb;
> +	ps->tx_skb = NULL;
> +
> +	/* skb_complete_tx_timestamp() frees up the client to make another
> +	 * timestampable transmit.  We have to be ready for it by clearing the
> +	 * ps->tx_skb "flag" beforehand
> +	 */
> +	clear_bit_unlock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS, &ps->state);
> +
> +	/* Deliver a clone of the original outgoing tx_skb with tx hwtstamp */
> +	skb_complete_tx_timestamp(tmp_skb, &shhwtstamps);
> +
> +	return 0;
> +
> +free_and_clear_skb:
> +	dev_kfree_skb_any(ps->tx_skb);
> +	ps->tx_skb = NULL;
> +	clear_bit_unlock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS, &ps->state);
> +
> +	return 0;
> +}
> +
> +static void hellcreek_get_rxts(struct hellcreek *hellcreek,
> +			       struct hellcreek_port_hwtstamp *ps,
> +			       struct sk_buff *skb, struct sk_buff_head *rxq,
> +			       int port)
> +{
> +	struct skb_shared_hwtstamps *shwt;
> +	struct sk_buff_head received;
> +	unsigned long flags;
> +
> +	/* The latched timestamp belongs to one of the received frames. */
> +	__skb_queue_head_init(&received);
> +
> +	/* Lock & disable interrupts */
> +	spin_lock_irqsave(&rxq->lock, flags);
> +
> +	/* Add the reception queue "rxq" to the "received" queue an reintialize
> +	 * "rxq".  From now on, we deal with "received" not with "rxq"
> +	 */
> +	skb_queue_splice_tail_init(rxq, &received);
> +
> +	spin_unlock_irqrestore(&rxq->lock, flags);
> +
> +	for (; skb; skb = __skb_dequeue(&received)) {
> +		struct ptp_header *hdr;
> +		unsigned int type;
> +		u64 ns;
> +
> +		/* Get nanoseconds from ptp packet */
> +		type = SKB_PTP_TYPE(skb);
> +		hdr  = ptp_parse_header(skb, type);
> +		ns   = hellcreek_get_reserved_field(hdr);
> +		hellcreek_clear_reserved_field(hdr);
> +
> +		/* Add seconds part */
> +		mutex_lock(&hellcreek->ptp_lock);
> +		ns += hellcreek_ptp_gettime_seconds(hellcreek, ns);
> +		mutex_unlock(&hellcreek->ptp_lock);
> +
> +		/* Save time stamp */
> +		shwt = skb_hwtstamps(skb);
> +		memset(shwt, 0, sizeof(*shwt));
> +		shwt->hwtstamp = ns_to_ktime(ns);
> +		netif_rx_ni(skb);
> +	}
> +}
> +
> +static void hellcreek_rxtstamp_work(struct hellcreek *hellcreek,
> +				    struct hellcreek_port_hwtstamp *ps,
> +				    int port)
> +{
> +	struct sk_buff *skb;
> +
> +	skb = skb_dequeue(&ps->rx_queue);
> +	if (skb)
> +		hellcreek_get_rxts(hellcreek, ps, skb, &ps->rx_queue, port);
> +}
> +
> +long hellcreek_hwtstamp_work(struct ptp_clock_info *ptp)
> +{
> +	struct hellcreek *hellcreek = ptp_to_hellcreek(ptp);
> +	struct dsa_switch *ds = hellcreek->ds;
> +	int i, restart = 0;
> +
> +	for (i = 0; i < ds->num_ports; i++) {
> +		struct hellcreek_port_hwtstamp *ps;
> +
> +		if (!dsa_is_user_port(ds, i))
> +			continue;
> +
> +		ps = &hellcreek->ports[i].port_hwtstamp;
> +
> +		if (test_bit(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS, &ps->state))
> +			restart |= hellcreek_txtstamp_work(hellcreek, ps, i);
> +
> +		hellcreek_rxtstamp_work(hellcreek, ps, i);
> +	}
> +
> +	return restart ? 1 : -1;
> +}
> +
> +bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
> +			     struct sk_buff *clone, unsigned int type)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port_hwtstamp *ps;
> +	struct ptp_header *hdr;
> +
> +	ps = &hellcreek->ports[port].port_hwtstamp;
> +
> +	/* Check if the driver is expected to do HW timestamping */
> +	if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP))
> +		return false;
> +
> +	/* Make sure the message is a PTP message that needs to be timestamped
> +	 * and the interaction with the HW timestamping is enabled. If not, stop
> +	 * here
> +	 */
> +	hdr = hellcreek_should_tstamp(hellcreek, port, clone, type);
> +	if (!hdr)
> +		return false;
> +
> +	if (test_and_set_bit_lock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS,
> +				  &ps->state))
> +		return false;
> +
> +	ps->tx_skb = clone;
> +
> +	/* store the number of ticks occurred since system start-up till this
> +	 * moment
> +	 */
> +	ps->tx_tstamp_start = jiffies;
> +
> +	ptp_schedule_worker(hellcreek->ptp_clock, 0);
> +
> +	return true;
> +}
> +
> +bool hellcreek_port_rxtstamp(struct dsa_switch *ds, int port,
> +			     struct sk_buff *skb, unsigned int type)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port_hwtstamp *ps;
> +	struct ptp_header *hdr;
> +
> +	ps = &hellcreek->ports[port].port_hwtstamp;
> +
> +	/* This check only fails if the user did not initialize hardware
> +	 * timestamping beforehand.
> +	 */
> +	if (ps->tstamp_config.rx_filter != HWTSTAMP_FILTER_PTP_V2_EVENT)
> +		return false;
> +
> +	/* Make sure the message is a PTP message that needs to be timestamped
> +	 * and the interaction with the HW timestamping is enabled. If not, stop
> +	 * here
> +	 */
> +	hdr = hellcreek_should_tstamp(hellcreek, port, skb, type);
> +	if (!hdr)
> +		return false;
> +
> +	SKB_PTP_TYPE(skb) = type;
> +
> +	skb_queue_tail(&ps->rx_queue, skb);
> +
> +	ptp_schedule_worker(hellcreek->ptp_clock, 0);
> +
> +	return true;
> +}
> +
> +static void hellcreek_hwtstamp_port_setup(struct hellcreek *hellcreek, int port)
> +{
> +	struct hellcreek_port_hwtstamp *ps =
> +		&hellcreek->ports[port].port_hwtstamp;
> +
> +	skb_queue_head_init(&ps->rx_queue);
> +}
> +
> +int hellcreek_hwtstamp_setup(struct hellcreek *hellcreek)
> +{
> +	struct dsa_switch *ds = hellcreek->ds;
> +	int i;
> +
> +	/* Initialize timestamping ports. */
> +	for (i = 0; i < ds->num_ports; ++i) {
> +		if (!dsa_is_user_port(ds, i))
> +			continue;
> +
> +		hellcreek_hwtstamp_port_setup(hellcreek, i);
> +	}
> +
> +	/* Select the synchronized clock as the source timekeeper for the
> +	 * timestamps and enable inline timestamping.
> +	 */
> +	hellcreek_ptp_write(hellcreek, PR_SETTINGS_C_TS_SRC_TK_MASK |
> +			    PR_SETTINGS_C_RES3TS,
> +			    PR_SETTINGS_C);
> +
> +	return 0;
> +}
> +
> +void hellcreek_hwtstamp_free(struct hellcreek *hellcreek)
> +{
> +	/* Nothing todo */
> +}
> diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h
> new file mode 100644
> index 000000000000..c0745ffa1ebb
> --- /dev/null
> +++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h
> @@ -0,0 +1,58 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
> +/*
> + * DSA driver for:
> + * Hirschmann Hellcreek TSN switch.
> + *
> + * Copyright (C) 2019,2020 Hochschule Offenburg
> + * Copyright (C) 2019,2020 Linutronix GmbH
> + * Authors: Kurt Kanzenbach <kurt@linutronix.de>
> + *	    Kamil Alkhouri <kamil.alkhouri@hs-offenburg.de>
> + */
> +
> +#ifndef _HELLCREEK_HWTSTAMP_H_
> +#define _HELLCREEK_HWTSTAMP_H_
> +
> +#include <net/dsa.h>
> +#include "hellcreek.h"
> +
> +/* Timestamp Register */
> +#define PR_TS_RX_P1_STATUS_C	(0x1d * 2)
> +#define PR_TS_RX_P1_DATA_C	(0x1e * 2)
> +#define PR_TS_TX_P1_STATUS_C	(0x1f * 2)
> +#define PR_TS_TX_P1_DATA_C	(0x20 * 2)
> +#define PR_TS_RX_P2_STATUS_C	(0x25 * 2)
> +#define PR_TS_RX_P2_DATA_C	(0x26 * 2)
> +#define PR_TS_TX_P2_STATUS_C	(0x27 * 2)
> +#define PR_TS_TX_P2_DATA_C	(0x28 * 2)
> +
> +#define PR_TS_STATUS_TS_AVAIL	BIT(2)
> +#define PR_TS_STATUS_TS_LOST	BIT(3)
> +
> +#define SKB_PTP_TYPE(__skb) (*(unsigned int *)((__skb)->cb))
> +
> +/* TX_TSTAMP_TIMEOUT: This limits the time spent polling for a TX
> + * timestamp. When working properly, hardware will produce a timestamp
> + * within 1ms. Software may enounter delays, so the timeout is set
> + * accordingly.
> + */
> +#define TX_TSTAMP_TIMEOUT	msecs_to_jiffies(40)
> +
> +int hellcreek_port_hwtstamp_set(struct dsa_switch *ds, int port,
> +				struct ifreq *ifr);
> +int hellcreek_port_hwtstamp_get(struct dsa_switch *ds, int port,
> +				struct ifreq *ifr);
> +
> +bool hellcreek_port_rxtstamp(struct dsa_switch *ds, int port,
> +			     struct sk_buff *clone, unsigned int type);
> +bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port,
> +			     struct sk_buff *clone, unsigned int type);
> +
> +int hellcreek_get_ts_info(struct dsa_switch *ds, int port,
> +			  struct ethtool_ts_info *info);
> +
> +long hellcreek_hwtstamp_work(struct ptp_clock_info *ptp);
> +
> +int hellcreek_hwtstamp_setup(struct hellcreek *chip);
> +void hellcreek_hwtstamp_free(struct hellcreek *chip);
> +
> +#endif /* _HELLCREEK_HWTSTAMP_H_ */
> diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.c b/drivers/net/dsa/hirschmann/hellcreek_ptp.c
> index 856fcb9ba3c6..12ad956abd5c 100644
> --- a/drivers/net/dsa/hirschmann/hellcreek_ptp.c
> +++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.c
> @@ -12,14 +12,15 @@
>  #include <linux/ptp_clock_kernel.h>
>  #include "hellcreek.h"
>  #include "hellcreek_ptp.h"
> +#include "hellcreek_hwtstamp.h"
>  
> -static u16 hellcreek_ptp_read(struct hellcreek *hellcreek, unsigned int offset)
> +u16 hellcreek_ptp_read(struct hellcreek *hellcreek, unsigned int offset)
>  {
>  	return readw(hellcreek->ptp_base + offset);
>  }
>  
> -static void hellcreek_ptp_write(struct hellcreek *hellcreek, u16 data,
> -				unsigned int offset)
> +void hellcreek_ptp_write(struct hellcreek *hellcreek, u16 data,
> +			 unsigned int offset)
>  {
>  	writew(data, hellcreek->ptp_base + offset);
>  }
> @@ -61,6 +62,24 @@ static u64 __hellcreek_ptp_gettime(struct hellcreek *hellcreek)
>  	return ns;
>  }
>  
> +/* Retrieve the seconds parts in nanoseconds for a packet timestamped with @ns.
> + * There has to be a check whether an overflow occurred between the packet
> + * arrival and now. If so use the correct seconds (-1) for calculating the
> + * packet arrival time.
> + */
> +u64 hellcreek_ptp_gettime_seconds(struct hellcreek *hellcreek, u64 ns)
> +{
> +	u64 s;
> +
> +	__hellcreek_ptp_gettime(hellcreek);
> +	if (hellcreek->last_ts > ns)
> +		s = hellcreek->seconds * NSEC_PER_SEC;
> +	else
> +		s = (hellcreek->seconds - 1) * NSEC_PER_SEC;
> +
> +	return s;
> +}
> +
>  static int hellcreek_ptp_gettime(struct ptp_clock_info *ptp,
>  				 struct timespec64 *ts)
>  {
> @@ -238,17 +257,18 @@ int hellcreek_ptp_setup(struct hellcreek *hellcreek)
>  	 * accumulator_overflow_rate shall not exceed 62.5 MHz (which adjusts
>  	 * the nominal frequency by 6.25%)
>  	 */
> -	hellcreek->ptp_clock_info.max_adj   = 62500000;
> -	hellcreek->ptp_clock_info.n_alarm   = 0;
> -	hellcreek->ptp_clock_info.n_pins    = 0;
> -	hellcreek->ptp_clock_info.n_ext_ts  = 0;
> -	hellcreek->ptp_clock_info.n_per_out = 0;
> -	hellcreek->ptp_clock_info.pps	    = 0;
> -	hellcreek->ptp_clock_info.adjfine   = hellcreek_ptp_adjfine;
> -	hellcreek->ptp_clock_info.adjtime   = hellcreek_ptp_adjtime;
> -	hellcreek->ptp_clock_info.gettime64 = hellcreek_ptp_gettime;
> -	hellcreek->ptp_clock_info.settime64 = hellcreek_ptp_settime;
> -	hellcreek->ptp_clock_info.enable    = hellcreek_ptp_enable;
> +	hellcreek->ptp_clock_info.max_adj     = 62500000;
> +	hellcreek->ptp_clock_info.n_alarm     = 0;
> +	hellcreek->ptp_clock_info.n_pins      = 0;
> +	hellcreek->ptp_clock_info.n_ext_ts    = 0;
> +	hellcreek->ptp_clock_info.n_per_out   = 0;
> +	hellcreek->ptp_clock_info.pps	      = 0;
> +	hellcreek->ptp_clock_info.adjfine     = hellcreek_ptp_adjfine;
> +	hellcreek->ptp_clock_info.adjtime     = hellcreek_ptp_adjtime;
> +	hellcreek->ptp_clock_info.gettime64   = hellcreek_ptp_gettime;
> +	hellcreek->ptp_clock_info.settime64   = hellcreek_ptp_settime;
> +	hellcreek->ptp_clock_info.enable      = hellcreek_ptp_enable;
> +	hellcreek->ptp_clock_info.do_aux_work = hellcreek_hwtstamp_work;
>  
>  	hellcreek->ptp_clock = ptp_clock_register(&hellcreek->ptp_clock_info,
>  						  hellcreek->dev);
> diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.h b/drivers/net/dsa/hirschmann/hellcreek_ptp.h
> index 2dd8aaa532d0..e0eca1f4a494 100644
> --- a/drivers/net/dsa/hirschmann/hellcreek_ptp.h
> +++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.h
> @@ -59,6 +59,10 @@
>  
>  int hellcreek_ptp_setup(struct hellcreek *hellcreek);
>  void hellcreek_ptp_free(struct hellcreek *hellcreek);
> +u16 hellcreek_ptp_read(struct hellcreek *hellcreek, unsigned int offset);
> +void hellcreek_ptp_write(struct hellcreek *hellcreek, u16 data,
> +			 unsigned int offset);
> +u64 hellcreek_ptp_gettime_seconds(struct hellcreek *hellcreek, u64 ns);
>  
>  #define ptp_to_hellcreek(ptp)					\
>  	container_of(ptp, struct hellcreek, ptp_clock_info)
> -- 
> 2.20.1
> 

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

* Re: [PATCH net-next v6 1/7] net: dsa: Add tag handling for Hirschmann Hellcreek switches
  2020-10-04 11:56   ` Vladimir Oltean
@ 2020-10-05  6:14     ` Kurt Kanzenbach
  0 siblings, 0 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-05  6:14 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 665 bytes --]

On Sun Oct 04 2020, Vladimir Oltean wrote:
> On Sun, Oct 04, 2020 at 01:29:05PM +0200, Kurt Kanzenbach wrote:
>> +static const struct dsa_device_ops hellcreek_netdev_ops = {
>> +	.name	  = "hellcreek",
>> +	.proto	  = DSA_TAG_PROTO_HELLCREEK,
>> +	.xmit	  = hellcreek_xmit,
>> +	.rcv	  = hellcreek_rcv,
>> +	.overhead = HELLCREEK_TAG_LEN,
>
> After the changes in "Generic adjustment for flow dissector in DSA":
> https://patchwork.ozlabs.org/project/netdev/list/?series=204347&state=*
> you might want to set ".tail_tag = true" (see patch 07/15), either now
> or later.
>
> Either way,
>
> Reviewed-by: Vladimir Oltean <olteanv@gmail.com>

Added it.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-04 12:56   ` Vladimir Oltean
@ 2020-10-06  6:09     ` Kurt Kanzenbach
  2020-10-06  9:20       ` Vladimir Oltean
  0 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-06  6:09 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 7460 bytes --]

Hi Vladimir,

thanks for the review.

On Sun Oct 04 2020, Vladimir Oltean wrote:
> On Sun, Oct 04, 2020 at 01:29:06PM +0200, Kurt Kanzenbach wrote:
>> +static int hellcreek_vlan_del(struct dsa_switch *ds, int port,
>> +			      const struct switchdev_obj_port_vlan *vlan)
>> +{
>> +	struct hellcreek *hellcreek = ds->priv;
>> +	u16 vid;
>> +
>> +	dev_dbg(hellcreek->dev, "Remove VLANs (%d -- %d) on port %d\n",
>> +		vlan->vid_begin, vlan->vid_end, port);
>> +
>> +	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
>> +		mutex_lock(&hellcreek->ports[port].vlan_lock);
>> +		if (hellcreek->ports[port].vlan_filtering)
>> +			hellcreek_unapply_vlan(hellcreek, port, vid);
>
> I don't think this works.
>
> ip link add br0 type bridge vlan_filtering 1
> ip link set swp0 master br0
> bridge vlan add dev swp0 vid 100
> ip link set br0 type bridge vlan_filtering 0
> bridge vlan del dev swp0 vid 100
> ip link set br0 type bridge vlan_filtering 1
>
> The expectation would be that swp0 blocks vid 100 now, but with your
> scheme it doesn't (it is not unapplied, and not unqueued either, because
> it was never queued in the first place).

Yes, that's correct. So, I think we have to queue not only the addition
of VLANs, but rather the "action" itself such as add or del. And then
apply all pending actions whenever vlan_filtering is set.

>> +static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,
>> +				      struct net_device *br)
>> +{
>> +	struct hellcreek *hellcreek = ds->priv;
>> +	int i;
>> +
>> +	dev_dbg(hellcreek->dev, "Port %d joins a bridge\n", port);
>> +
>> +	/* Configure port's vid to all other ports as egress untagged */
>> +	for (i = 0; i < ds->num_ports; ++i) {
>> +		if (!dsa_is_user_port(ds, i))
>> +			continue;
>> +
>> +		if (i == port)
>> +			continue;
>> +
>> +		hellcreek_apply_vlan(hellcreek, i, port, false, true);
>> +	}
>
> I think this is buggy when joining a VLAN filtering bridge. Your ports
> will pass frames with VID=2 with no problem, even without the user
> specifying 'bridge vlan add dev swp0 vid 2', and that's an issue. My
> understanding is that VLANs 1, 2, 3 stop having any sort of special
> meaning when the upper bridge has vlan_filtering=1.

Yes, that understanding is correct. So, what happens is when a port is
joining a VLAN filtering bridge is:

|root@tsn:~# ip link add name br0 type bridge
|root@tsn:~# ip link set dev br0 type bridge vlan_filtering 1
|root@tsn:~# ip link set dev lan0 master br0
|[  209.375055] br0: port 1(lan0) entered blocking state
|[  209.380073] br0: port 1(lan0) entered disabled state
|[  209.385340] hellcreek ff240000.switch: Port 2 joins a bridge
|[  209.391584] hellcreek ff240000.switch: Apply VLAN: port=3 vid=2 pvid=0 untagged=1
|[  209.399439] device lan0 entered promiscuous mode
|[  209.404043] device eth0 entered promiscuous mode
|[  209.409204] hellcreek ff240000.switch: Enable VLAN filtering on port 2
|[  209.415716] hellcreek ff240000.switch: Unapply VLAN: port=2 vid=2
|[  209.421840] hellcreek ff240000.switch: Unapply VLAN: port=0 vid=2
|[  209.428170] hellcreek ff240000.switch: Apply queued VLANs: port2
|[  209.434158] hellcreek ff240000.switch: Apply VLAN: port=2 vid=0 pvid=0 untagged=0
|[  209.441649] hellcreek ff240000.switch: Clear queued VLANs: port2
|[  209.447920] hellcreek ff240000.switch: Apply queued VLANs: port0
|[  209.453910] hellcreek ff240000.switch: Apply VLAN: port=0 vid=0 pvid=0 untagged=0
|[  209.461402] hellcreek ff240000.switch: Clear queued VLANs: port0
|[  209.467620] hellcreek ff240000.switch: VLAN prepare for port 2
|[  209.473476] hellcreek ff240000.switch: VLAN prepare for port 0
|[  209.479534] hellcreek ff240000.switch: Add VLANs (1 -- 1) on port 2, untagged, PVID
|[  209.487164] hellcreek ff240000.switch: Apply VLAN: port=2 vid=1 pvid=1 untagged=1
|[  209.494659] hellcreek ff240000.switch: Add VLANs (1 -- 1) on port 0, untagged, no PVID
|[  209.502794] hellcreek ff240000.switch: Apply VLAN: port=0 vid=1 pvid=0 untagged=1
|root@tsn:~# bridge vlan show
|port    vlan ids
|lan0     1 PVID Egress Untagged
|
|br0      1 PVID Egress Untagged

... which looks correct to me. The VLAN 2 is unapplied as expected. Or?

>
> And how do you deal with the case where swp1 and swp2 are bridged and
> have the VLAN 3 installed via 'bridge vlan', but swp3 isn't bridged?
> Will swp1/swp2 communicate with swp3? If yes, that's a problem.

There is no swp3. Currently there are only two ports and either they are
bridged or not.

>> +static int __hellcreek_fdb_del(struct hellcreek *hellcreek,
>> +			       const struct hellcreek_fdb_entry *entry)
>> +{
>> +	dev_dbg(hellcreek->dev, "Delete FDB entry: MAC=%pM!\n", entry->mac);
>> +
>
> Do these dev_dbg statements bring much value at all, even to you?

Yes, they do. See the log snippet above.

>> +/* Default setup for DSA: VLAN <X>: CPU and Port <X> egress untagged. */
>> +static int hellcreek_setup_vlan_membership(struct dsa_switch *ds, int port,
>> +					   bool enabled)
>
> This function always returns zero, so it should be void.

Yes. I noticed that as well and wanted to fix it before sending. Sorry, my bad.

>> +static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port,
>> +				    bool vlan_filtering)
>> +{
>> +	struct hellcreek *hellcreek = ds->priv;
>> +
>> +	dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n",
>> +		vlan_filtering ? "Enable" : "Disable", port);
>> +
>> +	/* Configure port to drop packages with not known vids */
>> +	hellcreek_setup_ingressflt(hellcreek, port, vlan_filtering);
>> +
>> +	/* Drop DSA vlan membership config. The user can now do it. */
>> +	hellcreek_setup_vlan_membership(ds, port, !vlan_filtering);
>> +
>> +	/* Apply saved vlan configurations while not filtering for port <X>. */
>> +	hellcreek_apply_vlan_filtering(hellcreek, port, vlan_filtering);
>> +
>> +	/* Do the same for the cpu port. */
>> +	hellcreek_apply_vlan_filtering(hellcreek, CPU_PORT, vlan_filtering);
>
> I think we should create a DSA_NOTIFIER_VLAN_FILTERING so you wouldn't
> have to do this, but not now.

OK.

>> +static int hellcreek_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct hellcreek *hellcreek;
>> +	struct resource *res;
>> +	int ret, i;
>> +
>> +	hellcreek = devm_kzalloc(dev, sizeof(*hellcreek), GFP_KERNEL);
>> +	if (!hellcreek)
>> +		return -ENOMEM;
>> +
>> +	hellcreek->vidmbrcfg = devm_kcalloc(dev, 4096,
>
> VLAN_N_VID

Thanks!

>> +static const struct hellcreek_platform_data de1soc_r1_pdata = {
>> +	.num_ports	 = 4,
>> +	.is_100_mbits	 = 1,
>> +	.qbv_support	 = 1,
>> +	.qbv_on_cpu_port = 1,
>
> Why does this matter?

Because Qbv on the CPU port is a feature and not all switch variants
have that. It will matter as soon as TAPRIO is implemented.

>> +#include <linux/bitops.h>
>> +#include <linux/kernel.h>
>> +#include <linux/device.h>
>> +#include <linux/ptp_clock_kernel.h>
>> +#include <linux/timecounter.h>
>> +#include <linux/mutex.h>
>
> Could you sort alphabetically?

Sure.

>
>> +#include <linux/platform_data/hirschmann-hellcreek.h>
>> +#include <net/dsa.h>
>> +
>> +/* Ports:
>> + *  - 0: CPU
>> + *  - 1: Tunnel
>> + *  - 2: TSN front port 1
>> + *  - 3: TSN front port 2
>> + *  - ...
>> + */
>> +#define CPU_PORT			0
>> +#define TUNNEL_PORT			1
>
> What's a tunnel port exactly?

AFAIK it's a debugging port for mirroring or looping traffic. Anyhow,
that is not a regular port and cannot be treated as such.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-04 14:30   ` Vladimir Oltean
@ 2020-10-06  6:27     ` Kurt Kanzenbach
  2020-10-06  7:28       ` Vladimir Oltean
  0 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-06  6:27 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 773 bytes --]

On Sun Oct 04 2020, Vladimir Oltean wrote:
> On Sun, Oct 04, 2020 at 01:29:08PM +0200, Kurt Kanzenbach wrote:
>> +/* Enabling/disabling TX and RX HW timestamping for different PTP messages is
>> + * not available in the switch. Thus, this function only serves as a check if
>> + * the user requested what is actually available or not
>> + */
>
> Correct me if I'm wrong, but to the user it makes zero difference
> whether the hardware takes timestamps or not.

Why not? I think it makes a difference to the user b/o the precision.

> What matters is whether the skb will be delivered to the stack with a
> hardware timestamp or not, so you should definitely accept a
> hwtstamp_config with TX and RX timestamping disabled.
>

Sorry, I cannot follow you here.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-06  6:27     ` Kurt Kanzenbach
@ 2020-10-06  7:28       ` Vladimir Oltean
  2020-10-06 13:30         ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-06  7:28 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Tue, Oct 06, 2020 at 08:27:42AM +0200, Kurt Kanzenbach wrote:
> On Sun Oct 04 2020, Vladimir Oltean wrote:
> > On Sun, Oct 04, 2020 at 01:29:08PM +0200, Kurt Kanzenbach wrote:
> >> +/* Enabling/disabling TX and RX HW timestamping for different PTP messages is
> >> + * not available in the switch. Thus, this function only serves as a check if
> >> + * the user requested what is actually available or not
> >> + */
> >
> > Correct me if I'm wrong, but to the user it makes zero difference
> > whether the hardware takes timestamps or not.
> 
> Why not? I think it makes a difference to the user b/o the precision.
> 
> > What matters is whether the skb will be delivered to the stack with a
> > hardware timestamp or not, so you should definitely accept a
> > hwtstamp_config with TX and RX timestamping disabled.
> >
> 
> Sorry, I cannot follow you here.

What I meant to say is that there is no reason you should refuse the
disabling of hardware timestamping. Even if that operation does not
really prevent the hardware from taking the timestamps, you simply
ignore the timestamps in the driver.

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06  6:09     ` Kurt Kanzenbach
@ 2020-10-06  9:20       ` Vladimir Oltean
  2020-10-06 10:13         ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-06  9:20 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Tue, Oct 06, 2020 at 08:09:39AM +0200, Kurt Kanzenbach wrote:
> On Sun Oct 04 2020, Vladimir Oltean wrote:
> > I don't think this works.
> >
> > ip link add br0 type bridge vlan_filtering 1
> > ip link set swp0 master br0
> > bridge vlan add dev swp0 vid 100
> > ip link set br0 type bridge vlan_filtering 0
> > bridge vlan del dev swp0 vid 100
> > ip link set br0 type bridge vlan_filtering 1
> >
> > The expectation would be that swp0 blocks vid 100 now, but with your
> > scheme it doesn't (it is not unapplied, and not unqueued either, because
> > it was never queued in the first place).
> 
> Yes, that's correct. So, I think we have to queue not only the addition
> of VLANs, but rather the "action" itself such as add or del. And then
> apply all pending actions whenever vlan_filtering is set.

Please remind me why you have to queue a VLAN addition/removal and can't
do it straight away? Is it because of private VID 2 and 3, which need to
be deleted first then re-added from the bridge VLAN group?

> >> +static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,
> >> +				      struct net_device *br)
> >> +{
> >> +	struct hellcreek *hellcreek = ds->priv;
> >> +	int i;
> >> +
> >> +	dev_dbg(hellcreek->dev, "Port %d joins a bridge\n", port);
> >> +
> >> +	/* Configure port's vid to all other ports as egress untagged */
> >> +	for (i = 0; i < ds->num_ports; ++i) {
> >> +		if (!dsa_is_user_port(ds, i))
> >> +			continue;
> >> +
> >> +		if (i == port)
> >> +			continue;
> >> +
> >> +		hellcreek_apply_vlan(hellcreek, i, port, false, true);
> >> +	}
> >
> > I think this is buggy when joining a VLAN filtering bridge. Your ports
> > will pass frames with VID=2 with no problem, even without the user
> > specifying 'bridge vlan add dev swp0 vid 2', and that's an issue. My
> > understanding is that VLANs 1, 2, 3 stop having any sort of special
> > meaning when the upper bridge has vlan_filtering=1.
> 
> Yes, that understanding is correct. So, what happens is when a port is
> joining a VLAN filtering bridge is:
> 
> |root@tsn:~# ip link add name br0 type bridge
> |root@tsn:~# ip link set dev br0 type bridge vlan_filtering 1
> |root@tsn:~# ip link set dev lan0 master br0
> |[  209.375055] br0: port 1(lan0) entered blocking state
> |[  209.380073] br0: port 1(lan0) entered disabled state
> |[  209.385340] hellcreek ff240000.switch: Port 2 joins a bridge
> |[  209.391584] hellcreek ff240000.switch: Apply VLAN: port=3 vid=2 pvid=0 untagged=1
> |[  209.399439] device lan0 entered promiscuous mode
> |[  209.404043] device eth0 entered promiscuous mode
> |[  209.409204] hellcreek ff240000.switch: Enable VLAN filtering on port 2
> |[  209.415716] hellcreek ff240000.switch: Unapply VLAN: port=2 vid=2
> |[  209.421840] hellcreek ff240000.switch: Unapply VLAN: port=0 vid=2

Now I understand even less. If the entire purpose of
hellcreek_setup_vlan_membership is to isolate lan0 from lan1, then why
do you even bother to install vid 2 to port=3 (lan1) when joining a
bridge, be it vlan_filtering or not? In bridged mode, they don't need
a unique pvid, it only complicates the implementation. They can have the
pvid from the bridge VLAN group.

> |[  209.428170] hellcreek ff240000.switch: Apply queued VLANs: port2
> |[  209.434158] hellcreek ff240000.switch: Apply VLAN: port=2 vid=0 pvid=0 untagged=0
> |[  209.441649] hellcreek ff240000.switch: Clear queued VLANs: port2
> |[  209.447920] hellcreek ff240000.switch: Apply queued VLANs: port0
> |[  209.453910] hellcreek ff240000.switch: Apply VLAN: port=0 vid=0 pvid=0 untagged=0
> |[  209.461402] hellcreek ff240000.switch: Clear queued VLANs: port0
> |[  209.467620] hellcreek ff240000.switch: VLAN prepare for port 2
> |[  209.473476] hellcreek ff240000.switch: VLAN prepare for port 0
> |[  209.479534] hellcreek ff240000.switch: Add VLANs (1 -- 1) on port 2, untagged, PVID
> |[  209.487164] hellcreek ff240000.switch: Apply VLAN: port=2 vid=1 pvid=1 untagged=1
> |[  209.494659] hellcreek ff240000.switch: Add VLANs (1 -- 1) on port 0, untagged, no PVID
> |[  209.502794] hellcreek ff240000.switch: Apply VLAN: port=0 vid=1 pvid=0 untagged=1
> |root@tsn:~# bridge vlan show

This is by no means a good indicator for anything. It shows the bridge
VLAN groups, not the hardware database.

> |port    vlan ids
> |lan0     1 PVID Egress Untagged
> |
> |br0      1 PVID Egress Untagged
> 
> ... which looks correct to me. The VLAN 2 is unapplied as expected. Or?

Ok, it gets applied in .port_bridge_join and unapplied in .port_vlan_filtering,
which is a convoluted way of doing nothing.

> >
> > And how do you deal with the case where swp1 and swp2 are bridged and
> > have the VLAN 3 installed via 'bridge vlan', but swp3 isn't bridged?
> > Will swp1/swp2 communicate with swp3? If yes, that's a problem.
> 
> There is no swp3. Currently there are only two ports and either they are
> bridged or not.

So this answers my question of whether the tunnel port is a user port or
not, ok.

How about other hardware revisions? Is this going to be a 2-port switch
forever? Your solution will indeed work for 2 ports (as long as you
address the other feedback from v5 w.r.t. declaring the ports as "always
filtering" and rejecting invalid 8021q uppers, which I don't see here),
but it will not scale for 3 ports, due to the fact that the bridge can
install a VLAN on a lan2 port, without knowing that it is in fact the
private pvid of lan1 or lan0.

> >> +static int __hellcreek_fdb_del(struct hellcreek *hellcreek,
> >> +			       const struct hellcreek_fdb_entry *entry)
> >> +{
> >> +	dev_dbg(hellcreek->dev, "Delete FDB entry: MAC=%pM!\n", entry->mac);
> >> +
> >
> > Do these dev_dbg statements bring much value at all, even to you?
> 
> Yes, they do. See the log snippet above.
> 

If you want to dump the hardware database you can look at the devlink
regions that Andrew added very recently. Much more reliable than
following the order of operations in the log.

> >> +static const struct hellcreek_platform_data de1soc_r1_pdata = {
> >> +	.num_ports	 = 4,
> >> +	.is_100_mbits	 = 1,
> >> +	.qbv_support	 = 1,
> >> +	.qbv_on_cpu_port = 1,
> >
> > Why does this matter?
> 
> Because Qbv on the CPU port is a feature and not all switch variants
> have that. It will matter as soon as TAPRIO is implemented.

How do you plan to install a tc-taprio qdisc on the CPU port?

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06  9:20       ` Vladimir Oltean
@ 2020-10-06 10:13         ` Kurt Kanzenbach
  2020-10-06 11:32           ` Vladimir Oltean
  2020-10-08 11:49           ` Vladimir Oltean
  0 siblings, 2 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-06 10:13 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 8326 bytes --]

On Tue Oct 06 2020, Vladimir Oltean wrote:
> On Tue, Oct 06, 2020 at 08:09:39AM +0200, Kurt Kanzenbach wrote:
>> On Sun Oct 04 2020, Vladimir Oltean wrote:
>> > I don't think this works.
>> >
>> > ip link add br0 type bridge vlan_filtering 1
>> > ip link set swp0 master br0
>> > bridge vlan add dev swp0 vid 100
>> > ip link set br0 type bridge vlan_filtering 0
>> > bridge vlan del dev swp0 vid 100
>> > ip link set br0 type bridge vlan_filtering 1
>> >
>> > The expectation would be that swp0 blocks vid 100 now, but with your
>> > scheme it doesn't (it is not unapplied, and not unqueued either, because
>> > it was never queued in the first place).
>> 
>> Yes, that's correct. So, I think we have to queue not only the addition
>> of VLANs, but rather the "action" itself such as add or del. And then
>> apply all pending actions whenever vlan_filtering is set.
>
> Please remind me why you have to queue a VLAN addition/removal and can't
> do it straight away? Is it because of private VID 2 and 3, which need to
> be deleted first then re-added from the bridge VLAN group?

It's because of the private VLANs 2 and 3 which shouldn't be tampered
with. Isn't it? You said:

> If you need caching of VLANs installed by the bridge and/or by the 8021q
> module, then you can add those to a list, and restore them in the
> .port_vlan_filtering callback by yourself. You can look at how sja1105
> does that.
[...]
> If your driver makes private use of VLAN tags beyond what the upper
> layers ask for, then it should keep track of them.

That's what I did.

At the end of the day the driver needs to port separation
somehow. Otherwise it doesn't match the DSA model, right? Again there is
no port forwarding matrix which would make things easy. It has to be
solved in software.

If the private VLAN stuff isn't working, because all of the different
corner cases, then what's the alternative?

>
>> >> +static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,
>> >> +				      struct net_device *br)
>> >> +{
>> >> +	struct hellcreek *hellcreek = ds->priv;
>> >> +	int i;
>> >> +
>> >> +	dev_dbg(hellcreek->dev, "Port %d joins a bridge\n", port);
>> >> +
>> >> +	/* Configure port's vid to all other ports as egress untagged */
>> >> +	for (i = 0; i < ds->num_ports; ++i) {
>> >> +		if (!dsa_is_user_port(ds, i))
>> >> +			continue;
>> >> +
>> >> +		if (i == port)
>> >> +			continue;
>> >> +
>> >> +		hellcreek_apply_vlan(hellcreek, i, port, false, true);
>> >> +	}
>> >
>> > I think this is buggy when joining a VLAN filtering bridge. Your ports
>> > will pass frames with VID=2 with no problem, even without the user
>> > specifying 'bridge vlan add dev swp0 vid 2', and that's an issue. My
>> > understanding is that VLANs 1, 2, 3 stop having any sort of special
>> > meaning when the upper bridge has vlan_filtering=1.
>> 
>> Yes, that understanding is correct. So, what happens is when a port is
>> joining a VLAN filtering bridge is:
>> 
>> |root@tsn:~# ip link add name br0 type bridge
>> |root@tsn:~# ip link set dev br0 type bridge vlan_filtering 1
>> |root@tsn:~# ip link set dev lan0 master br0
>> |[  209.375055] br0: port 1(lan0) entered blocking state
>> |[  209.380073] br0: port 1(lan0) entered disabled state
>> |[  209.385340] hellcreek ff240000.switch: Port 2 joins a bridge
>> |[  209.391584] hellcreek ff240000.switch: Apply VLAN: port=3 vid=2 pvid=0 untagged=1
>> |[  209.399439] device lan0 entered promiscuous mode
>> |[  209.404043] device eth0 entered promiscuous mode
>> |[  209.409204] hellcreek ff240000.switch: Enable VLAN filtering on port 2
>> |[  209.415716] hellcreek ff240000.switch: Unapply VLAN: port=2 vid=2
>> |[  209.421840] hellcreek ff240000.switch: Unapply VLAN: port=0 vid=2
>
> Now I understand even less. If the entire purpose of
> hellcreek_setup_vlan_membership is to isolate lan0 from lan1

Yes.

> , then why do you even bother to install vid 2 to port=3 (lan1) when
> joining a bridge, be it vlan_filtering or not?

So, that traffic is actually switched between the ports.

> In bridged mode, they don't need a unique pvid, it only complicates
> the implementation. They can have the pvid from the bridge VLAN group.

Meaning rely on the fact that VLAN 1 is programmed automatically? Maybe
just unapply the private VLAN in bridge_join()?

>
>> |[  209.428170] hellcreek ff240000.switch: Apply queued VLANs: port2
>> |[  209.434158] hellcreek ff240000.switch: Apply VLAN: port=2 vid=0 pvid=0 untagged=0
>> |[  209.441649] hellcreek ff240000.switch: Clear queued VLANs: port2
>> |[  209.447920] hellcreek ff240000.switch: Apply queued VLANs: port0
>> |[  209.453910] hellcreek ff240000.switch: Apply VLAN: port=0 vid=0 pvid=0 untagged=0
>> |[  209.461402] hellcreek ff240000.switch: Clear queued VLANs: port0
>> |[  209.467620] hellcreek ff240000.switch: VLAN prepare for port 2
>> |[  209.473476] hellcreek ff240000.switch: VLAN prepare for port 0
>> |[  209.479534] hellcreek ff240000.switch: Add VLANs (1 -- 1) on port 2, untagged, PVID
>> |[  209.487164] hellcreek ff240000.switch: Apply VLAN: port=2 vid=1 pvid=1 untagged=1
>> |[  209.494659] hellcreek ff240000.switch: Add VLANs (1 -- 1) on port 0, untagged, no PVID
>> |[  209.502794] hellcreek ff240000.switch: Apply VLAN: port=0 vid=1 pvid=0 untagged=1
>> |root@tsn:~# bridge vlan show
>
> This is by no means a good indicator for anything. It shows the bridge
> VLAN groups, not the hardware database.
>
>> |port    vlan ids
>> |lan0     1 PVID Egress Untagged
>> |
>> |br0      1 PVID Egress Untagged
>> 
>> ... which looks correct to me. The VLAN 2 is unapplied as expected. Or?
>
> Ok, it gets applied in .port_bridge_join and unapplied in .port_vlan_filtering,
> which is a convoluted way of doing nothing.
>
>> >
>> > And how do you deal with the case where swp1 and swp2 are bridged and
>> > have the VLAN 3 installed via 'bridge vlan', but swp3 isn't bridged?
>> > Will swp1/swp2 communicate with swp3? If yes, that's a problem.
>> 
>> There is no swp3. Currently there are only two ports and either they are
>> bridged or not.
>
> So this answers my question of whether the tunnel port is a user port or
> not, ok.
>
> How about other hardware revisions? Is this going to be a 2-port switch
> forever?

At the moment, yes. It's meant to be used for switched endpoints. More
port devices may come in the future.

> Your solution will indeed work for 2 ports (as long as you
> address the other feedback from v5 w.r.t. declaring the ports as "always
> filtering" and rejecting invalid 8021q uppers, which I don't see
> here),

I've checked that property with ethtool and it's set to the value you
suggested. And yes, the same VLAN on top of single ports will break
separation with the current solution.

> but it will not scale for 3 ports, due to the fact that the bridge can
> install a VLAN on a lan2 port, without knowing that it is in fact the
> private pvid of lan1 or lan0.

Yes, that's also a limitation of the VLAN approach.

>
>> >> +static int __hellcreek_fdb_del(struct hellcreek *hellcreek,
>> >> +			       const struct hellcreek_fdb_entry *entry)
>> >> +{
>> >> +	dev_dbg(hellcreek->dev, "Delete FDB entry: MAC=%pM!\n", entry->mac);
>> >> +
>> >
>> > Do these dev_dbg statements bring much value at all, even to you?
>> 
>> Yes, they do. See the log snippet above.
>> 
>
> If you want to dump the hardware database you can look at the devlink
> regions that Andrew added very recently. Much more reliable than
> following the order of operations in the log.

I saw the patches and it's really useful. However, I won't implement any
new features to this drivers unless that port separation problem is
sorted out.

>
>> >> +static const struct hellcreek_platform_data de1soc_r1_pdata = {
>> >> +	.num_ports	 = 4,
>> >> +	.is_100_mbits	 = 1,
>> >> +	.qbv_support	 = 1,
>> >> +	.qbv_on_cpu_port = 1,
>> >
>> > Why does this matter?
>> 
>> Because Qbv on the CPU port is a feature and not all switch variants
>> have that. It will matter as soon as TAPRIO is implemented.
>
> How do you plan to install a tc-taprio qdisc on the CPU port?

That's an issue to be sorted out.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06 10:13         ` Kurt Kanzenbach
@ 2020-10-06 11:32           ` Vladimir Oltean
  2020-10-06 12:37             ` Vladimir Oltean
  2020-10-06 13:23             ` Kurt Kanzenbach
  2020-10-08 11:49           ` Vladimir Oltean
  1 sibling, 2 replies; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-06 11:32 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Tue, Oct 06, 2020 at 12:13:04PM +0200, Kurt Kanzenbach wrote:
> On Tue Oct 06 2020, Vladimir Oltean wrote:
> > On Tue, Oct 06, 2020 at 08:09:39AM +0200, Kurt Kanzenbach wrote:
> >> On Sun Oct 04 2020, Vladimir Oltean wrote:
> >> > I don't think this works.
> >> >
> >> > ip link add br0 type bridge vlan_filtering 1
> >> > ip link set swp0 master br0
> >> > bridge vlan add dev swp0 vid 100
> >> > ip link set br0 type bridge vlan_filtering 0
> >> > bridge vlan del dev swp0 vid 100
> >> > ip link set br0 type bridge vlan_filtering 1
> >> >
> >> > The expectation would be that swp0 blocks vid 100 now, but with your
> >> > scheme it doesn't (it is not unapplied, and not unqueued either, because
> >> > it was never queued in the first place).
> >> 
> >> Yes, that's correct. So, I think we have to queue not only the addition
> >> of VLANs, but rather the "action" itself such as add or del. And then
> >> apply all pending actions whenever vlan_filtering is set.
> >
> > Please remind me why you have to queue a VLAN addition/removal and can't
> > do it straight away? Is it because of private VID 2 and 3, which need to
> > be deleted first then re-added from the bridge VLAN group?
> 
> It's because of the private VLANs 2 and 3 which shouldn't be tampered
> with. Isn't it? You said:
> 
> > If you need caching of VLANs installed by the bridge and/or by the 8021q
> > module, then you can add those to a list, and restore them in the
> > .port_vlan_filtering callback by yourself. You can look at how sja1105
> > does that.
> [...]
> > If your driver makes private use of VLAN tags beyond what the upper
> > layers ask for, then it should keep track of them.
> 
> That's what I did.

Yes, that's what I said, and it's not wrong because there's a big IF there.
But first of all, whatever you do has to work, no matter how you do it.

DSA can at any moment call your .port_vlan_add method either from the
bridge or from the 8021q module. And you need to make sure that you:

- offer the correct services to these layers. Meaning:
  (a) a bridge with vlan_filtering=0 does not expect its offloading
      ports to filter (drop) by VLAN ID. The only thing that changed
      after the configure_vlan_while_not_filtering patch was that now,
      DSA drivers are supposed to make sure that the VLAN database can
      accept .port_vlan_add calls that were made during the time that
      vlan_filtering was 0. These VLANs are supposed to make no
      difference to the data path until vlan_filtering is switched to 1.
  (b) a bridge with vlan_filtering=1 with offloading expects that VLANs
      from its VLAN group are tagged according to their flags, and
      forwarded to the other ports that are members of that VLAN group,
      and VLANs from outside its VLAN group are dropped in hardware.
  (c) 8021q uppers receive traffic tagged with their VLAN ID

- still keep port separation where that's needed (i.e. in standalone
  mode). Ports that are not under a bridge do not perform autonomous L2
  forwarding on their own.

Because port separation is only a concern in standalone mode, I expect
that you only call hellcreek_setup_vlan_membership when entering
standalone mode.

So:
- neither the bridge nor the 8021q module cannot offload a VLAN on a
  port that is the private pvid of any other standalone port. Maybe this
  would not even be visible if you would configure those private pvids
  as 4095, 4094, etc, but you should definitely enfore the restriction.
- IF you let the bridge or 8021q module use a private pvid of a
  standalone port during the time that said port did not need it, then
  you should restore that private pvid when the bridge or 8021q upper is
  removed. This is the part that seems to be causing problems.
- in standalone mode, you can't let 8021q uppers request the same VLAN
  from different ports, as that would break separation.

I am thinking:
If you _don't_ ever let the private pvids of the standalone ports
overlap with the valid range for the bridge and 8021q module, then you
don't need to care whether the bridge or 8021q module could delete a
private pvid of yours (because you wouldn't let them install it in the
first place). So you solve half the problem.

Otherwise said:
If you reject VLANs 4095 and 4094 in the .port_vlan_prepare callback,
you'll be left with 4094 usable VLANs for the bridge on each port, or
4094 VLANs usable for the 8021q module in total (but mutually exclusive
either on one port or the other). So you lose exactly 2 VLANs, and you
simplify the driver implementation.

- The .port_vlan_prepare will check whether the VLAN is 4095 or 4094,
  and if it is, refuse it.

- The .port_vlan_add will always install the VLAN to the hardware
  database, no queuing if there's no reason for it (and I can't see any.
  Your hardware seems to be sane enough to not drop a VLAN-tagged frame,
  and forward it correctly on egress, as long as you call
  hellcreek_setup_ingressflt with enable=false, am I right? or does the
  VLAN still need to be installed into the egress port?).

- The .port_vlan_del will always delete the VLAN from the hardware.

- The .port_bridge_join will:
  (a) disable the VLAN ingress filtering that you need for standalone
      mode. Let the bridge re-enable it if it needs.
  (b) delete VLAN 4094 or 4095 from the port's database. It bothers you
      in bridged mode.

- The .port_bridge_leave will:
  (a) re-enable the VLAN ingress filtering for standalone mode.
  (b) reinstall VLAN 4094 or 4095 into the port's database. You need it
      for isolation in standalone mode.

Am I missing something? The rules are relatively simple and intuitive
(until they aren't!), I'm not trying to impose a certain implementation,
sorry if that's what you understood, I'm just trying to make sure that
the rules are observed in the simplest way possible.

You'll also need something along the lines of this patch, that's what I
was hoping to see from you:

----------------------[ cut here ]----------------------
From 151271ebeebe520ff997bdc08a3e776fbefce17c Mon Sep 17 00:00:00 2001
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Date: Tue, 6 Oct 2020 14:06:54 +0300
Subject: [PATCH] net: dsa: give drivers the chance to veto certain upper
 devices

Some switches rely on unique pvids to ensure port separation in
standalone mode, because they don't have a port forwarding matrix
configurable in hardware. So, setups like a group of 2 uppers with the
same VLAN, swp0.100 and swp1.100, will cause traffic tagged with VLAN
100 to be autonomously forwarded between these switch ports, in spite
of there being no bridge between swp0 and swp1.

These drivers need to prevent this from happening. They need to have
VLAN filtering enabled in standalone mode (so they'll drop frames tagged
with unknown VLANs) and they can only accept an 8021q upper on a port as
long as it isn't installed on any other port too. So give them the
chance to veto bad user requests.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 include/net/dsa.h |  6 ++++++
 net/dsa/slave.c   | 12 ++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index c0185660881c..17e4bb9170e7 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -534,6 +534,12 @@ struct dsa_switch_ops {
 	void	(*get_regs)(struct dsa_switch *ds, int port,
 			    struct ethtool_regs *regs, void *p);
 
+	/*
+	 * Upper device tracking.
+	 */
+	int	(*port_prechangeupper)(struct dsa_switch *ds, int port,
+				       struct netdev_notifier_changeupper_info *info);
+
 	/*
 	 * Bridge integration
 	 */
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index e7c1d62fde99..919dbc1bcf6c 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -2006,10 +2006,22 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
 	switch (event) {
 	case NETDEV_PRECHANGEUPPER: {
 		struct netdev_notifier_changeupper_info *info = ptr;
+		struct dsa_switch *ds;
+		struct dsa_port *dp;
+		int err;
 
 		if (!dsa_slave_dev_check(dev))
 			return dsa_prevent_bridging_8021q_upper(dev, ptr);
 
+		dp = dsa_slave_to_port(dev);
+		ds = dp->ds;
+
+		if (ds->ops->port_prechangeupper) {
+			err = ds->ops->port_prechangeupper(ds, dp->index, ptr);
+			if (err)
+				return err;
+		}
+
 		if (is_vlan_dev(info->upper_dev))
 			return dsa_slave_check_8021q_upper(dev, ptr);
 		break;
-- 
2.25.1

----------------------[ cut here ]----------------------

And then you'll implement this callback and reject 8021q uppers (see the
dsa_slave_check_8021q_upper function for how) with equal VLANs on
another port. Maybe that's one place where you can keep a VLAN list. But
that's an implementation detail which should be best left to you to
figure out.

> At the end of the day the driver needs to port separation
> somehow. Otherwise it doesn't match the DSA model, right? Again there is
> no port forwarding matrix which would make things easy. It has to be
> solved in software.
> 
> If the private VLAN stuff isn't working, because all of the different
> corner cases, then what's the alternative?

Did I say it can't work? I am just commenting on the code.

> > In bridged mode, they don't need a unique pvid, it only complicates
> > the implementation. They can have the pvid from the bridge VLAN group.
> 
> Meaning rely on the fact that VLAN 1 is programmed automatically? Maybe
> just unapply the private VLAN in bridge_join()?

Yes.

> I've checked that property with ethtool and it's set to the value you
> suggested.

Ok, so you don't need to change anything on the ethtool features side in
that case.

Thanks,
-Vladimir

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06 11:32           ` Vladimir Oltean
@ 2020-10-06 12:37             ` Vladimir Oltean
  2020-10-06 13:23             ` Kurt Kanzenbach
  1 sibling, 0 replies; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-06 12:37 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Tue, Oct 06, 2020 at 02:32:37PM +0300, Vladimir Oltean wrote:
> - The .port_vlan_add will always install the VLAN to the hardware
>   database, no queuing if there's no reason for it (and I can't see any.
>   Your hardware seems to be sane enough to not drop a VLAN-tagged frame,
>   and forward it correctly on egress, as long as you call
>   hellcreek_setup_ingressflt with enable=false, am I right? or does the
>   VLAN still need to be installed into the egress port?).

I don't know if this goes without saying or not, but of course, if you
can't enforce correct behavior with a vlan_filtering=0 bridge (i.e.
"ingressflt" will only help the VLAN-tagged frames to be accepted on
ingress, but they will be nonetheless dropped on egress due to no valid
destinations), then you should reject that setting in the 2 places where
vlan_filtering can be enabled:

(a) in .port_prechangeupper, you should make sure that if the upper is a
    bridge, then br_vlan_enabled() must be true.
(b) in .port_vlan_filtering, you should reject enabled=false from the
    switchdev_trans_ph_prepare(trans) state.

Again, this isn't about implementing every possible combination, just
about making sure that the user isn't led into believing that a certain
setting works when in reality it doesn't.

> @@ -2006,10 +2006,22 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
>  	switch (event) {
>  	case NETDEV_PRECHANGEUPPER: {
>  		struct netdev_notifier_changeupper_info *info = ptr;
> +		struct dsa_switch *ds;
> +		struct dsa_port *dp;
> +		int err;
>  
>  		if (!dsa_slave_dev_check(dev))
>  			return dsa_prevent_bridging_8021q_upper(dev, ptr);
>  
> +		dp = dsa_slave_to_port(dev);
> +		ds = dp->ds;
> +
> +		if (ds->ops->port_prechangeupper) {
> +			err = ds->ops->port_prechangeupper(ds, dp->index, ptr);
> +			if (err)
> +				return err;

Correction: this should return notifier_from_errno(err).

> +		}
> +
>  		if (is_vlan_dev(info->upper_dev))
>  			return dsa_slave_check_8021q_upper(dev, ptr);
>  		break;
> -- 
> 2.25.1

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06 11:32           ` Vladimir Oltean
  2020-10-06 12:37             ` Vladimir Oltean
@ 2020-10-06 13:23             ` Kurt Kanzenbach
  2020-10-06 13:42               ` Vladimir Oltean
  2020-10-06 13:56               ` Vladimir Oltean
  1 sibling, 2 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-06 13:23 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 8162 bytes --]

On Tue Oct 06 2020, Vladimir Oltean wrote:
> Yes, that's what I said, and it's not wrong because there's a big IF there.
> But first of all, whatever you do has to work, no matter how you do it.
>
> DSA can at any moment call your .port_vlan_add method either from the
> bridge or from the 8021q module. And you need to make sure that you:
>
> - offer the correct services to these layers. Meaning:
>   (a) a bridge with vlan_filtering=0 does not expect its offloading
>       ports to filter (drop) by VLAN ID. The only thing that changed
>       after the configure_vlan_while_not_filtering patch was that now,
>       DSA drivers are supposed to make sure that the VLAN database can
>       accept .port_vlan_add calls that were made during the time that
>       vlan_filtering was 0. These VLANs are supposed to make no
>       difference to the data path until vlan_filtering is switched to 1.

Does this mean that tagged traffic is forwarded no matter what? That
doesn't work with the current implementation, because the VLAN tags are
interpreted by default. There's a global flag to put the switch in VLAN
unaware mode. But it's global and not per bridge or port.

>   (b) a bridge with vlan_filtering=1 with offloading expects that VLANs
>       from its VLAN group are tagged according to their flags, and
>       forwarded to the other ports that are members of that VLAN group,
>       and VLANs from outside its VLAN group are dropped in hardware.
>   (c) 8021q uppers receive traffic tagged with their VLAN ID
>
> - still keep port separation where that's needed (i.e. in standalone
>   mode). Ports that are not under a bridge do not perform autonomous L2
>   forwarding on their own.
>
> Because port separation is only a concern in standalone mode, I expect
> that you only call hellcreek_setup_vlan_membership when entering
> standalone mode.
>
> So:
> - neither the bridge nor the 8021q module cannot offload a VLAN on a
>   port that is the private pvid of any other standalone port. Maybe this
>   would not even be visible if you would configure those private pvids
>   as 4095, 4094, etc, but you should definitely enfore the restriction.
> - IF you let the bridge or 8021q module use a private pvid of a
>   standalone port during the time that said port did not need it, then
>   you should restore that private pvid when the bridge or 8021q upper is
>   removed. This is the part that seems to be causing problems.
> - in standalone mode, you can't let 8021q uppers request the same VLAN
>   from different ports, as that would break separation.
>
> I am thinking:
> If you _don't_ ever let the private pvids of the standalone ports
> overlap with the valid range for the bridge and 8021q module, then you
> don't need to care whether the bridge or 8021q module could delete a
> private pvid of yours (because you wouldn't let them install it in the
> first place). So you solve half the problem.

So you're saying private VLANs can be used but the user or the other
kernel modules shouldn't be allowed to use them to simplify the
implementation?  Makes sense to me.

>
> Otherwise said:
> If you reject VLANs 4095 and 4094 in the .port_vlan_prepare callback,
> you'll be left with 4094 usable VLANs for the bridge on each port, or
> 4094 VLANs usable for the 8021q module in total (but mutually exclusive
> either on one port or the other). So you lose exactly 2 VLANs, and you
> simplify the driver implementation.
>
> - The .port_vlan_prepare will check whether the VLAN is 4095 or 4094,
>   and if it is, refuse it.
>
> - The .port_vlan_add will always install the VLAN to the hardware
>   database, no queuing if there's no reason for it (and I can't see any.
>   Your hardware seems to be sane enough to not drop a VLAN-tagged frame,
>   and forward it correctly on egress, as long as you call
>   hellcreek_setup_ingressflt with enable=false, am I right? or does the
>   VLAN still need to be installed into the egress port?).

The egress port has to member to that VLAN.

>
> - The .port_vlan_del will always delete the VLAN from the hardware.
>
> - The .port_bridge_join will:
>   (a) disable the VLAN ingress filtering that you need for standalone
>       mode. Let the bridge re-enable it if it needs.
>   (b) delete VLAN 4094 or 4095 from the port's database. It bothers you
>       in bridged mode.
>
> - The .port_bridge_leave will:
>   (a) re-enable the VLAN ingress filtering for standalone mode.
>   (b) reinstall VLAN 4094 or 4095 into the port's database. You need it
>       for isolation in standalone mode.
>
> Am I missing something? The rules are relatively simple and intuitive
> (until they aren't!), I'm not trying to impose a certain implementation,
> sorry if that's what you understood, I'm just trying to make sure that
> the rules are observed in the simplest way possible.

And I'm trying to understand what the rules are... Thanks for detailed
explanation.

>
> You'll also need something along the lines of this patch, that's what I
> was hoping to see from you:
>
> ----------------------[ cut here ]----------------------
> From 151271ebeebe520ff997bdc08a3e776fbefce17c Mon Sep 17 00:00:00 2001
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> Date: Tue, 6 Oct 2020 14:06:54 +0300
> Subject: [PATCH] net: dsa: give drivers the chance to veto certain upper
>  devices
>
> Some switches rely on unique pvids to ensure port separation in
> standalone mode, because they don't have a port forwarding matrix
> configurable in hardware. So, setups like a group of 2 uppers with the
> same VLAN, swp0.100 and swp1.100, will cause traffic tagged with VLAN
> 100 to be autonomously forwarded between these switch ports, in spite
> of there being no bridge between swp0 and swp1.
>
> These drivers need to prevent this from happening. They need to have
> VLAN filtering enabled in standalone mode (so they'll drop frames tagged
> with unknown VLANs) and they can only accept an 8021q upper on a port as
> long as it isn't installed on any other port too. So give them the
> chance to veto bad user requests.
>
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
>  include/net/dsa.h |  6 ++++++
>  net/dsa/slave.c   | 12 ++++++++++++
>  2 files changed, 18 insertions(+)
>
> diff --git a/include/net/dsa.h b/include/net/dsa.h
> index c0185660881c..17e4bb9170e7 100644
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -534,6 +534,12 @@ struct dsa_switch_ops {
>  	void	(*get_regs)(struct dsa_switch *ds, int port,
>  			    struct ethtool_regs *regs, void *p);
>  
> +	/*
> +	 * Upper device tracking.
> +	 */
> +	int	(*port_prechangeupper)(struct dsa_switch *ds, int port,
> +				       struct netdev_notifier_changeupper_info *info);
> +
>  	/*
>  	 * Bridge integration
>  	 */
> diff --git a/net/dsa/slave.c b/net/dsa/slave.c
> index e7c1d62fde99..919dbc1bcf6c 100644
> --- a/net/dsa/slave.c
> +++ b/net/dsa/slave.c
> @@ -2006,10 +2006,22 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
>  	switch (event) {
>  	case NETDEV_PRECHANGEUPPER: {
>  		struct netdev_notifier_changeupper_info *info = ptr;
> +		struct dsa_switch *ds;
> +		struct dsa_port *dp;
> +		int err;
>  
>  		if (!dsa_slave_dev_check(dev))
>  			return dsa_prevent_bridging_8021q_upper(dev, ptr);
>  
> +		dp = dsa_slave_to_port(dev);
> +		ds = dp->ds;
> +
> +		if (ds->ops->port_prechangeupper) {
> +			err = ds->ops->port_prechangeupper(ds, dp->index, ptr);
> +			if (err)
> +				return err;
> +		}
> +
>  		if (is_vlan_dev(info->upper_dev))
>  			return dsa_slave_check_8021q_upper(dev, ptr);
>  		break;
> -- 
> 2.25.1
>
> ----------------------[ cut here ]----------------------
>
> And then you'll implement this callback and reject 8021q uppers (see the
> dsa_slave_check_8021q_upper function for how) with equal VLANs on
> another port. Maybe that's one place where you can keep a VLAN list. But
> that's an implementation detail which should be best left to you to
> figure out.

OK.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-06  7:28       ` Vladimir Oltean
@ 2020-10-06 13:30         ` Kurt Kanzenbach
  2020-10-06 13:32           ` Vladimir Oltean
  0 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-06 13:30 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 1408 bytes --]

On Tue Oct 06 2020, Vladimir Oltean wrote:
> On Tue, Oct 06, 2020 at 08:27:42AM +0200, Kurt Kanzenbach wrote:
>> On Sun Oct 04 2020, Vladimir Oltean wrote:
>> > On Sun, Oct 04, 2020 at 01:29:08PM +0200, Kurt Kanzenbach wrote:
>> >> +/* Enabling/disabling TX and RX HW timestamping for different PTP messages is
>> >> + * not available in the switch. Thus, this function only serves as a check if
>> >> + * the user requested what is actually available or not
>> >> + */
>> >
>> > Correct me if I'm wrong, but to the user it makes zero difference
>> > whether the hardware takes timestamps or not.
>> 
>> Why not? I think it makes a difference to the user b/o the precision.
>> 
>> > What matters is whether the skb will be delivered to the stack with a
>> > hardware timestamp or not, so you should definitely accept a
>> > hwtstamp_config with TX and RX timestamping disabled.
>> >
>> 
>> Sorry, I cannot follow you here.
>
> What I meant to say is that there is no reason you should refuse the
> disabling of hardware timestamping. Even if that operation does not
> really prevent the hardware from taking the timestamps, you simply
> ignore the timestamps in the driver.

That's the point. The user (or anybody else) cannot disable hardware
stamping, because it is always performed. So, why should it be allowed
to disable it even when it cannot be disabled?

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-06 13:30         ` Kurt Kanzenbach
@ 2020-10-06 13:32           ` Vladimir Oltean
  2020-10-06 13:56             ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-06 13:32 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Tue, Oct 06, 2020 at 03:30:36PM +0200, Kurt Kanzenbach wrote:
> That's the point. The user (or anybody else) cannot disable hardware
> stamping, because it is always performed. So, why should it be allowed
> to disable it even when it cannot be disabled?

Because your driver's user can attach a PTP PHY to your switch port, and
the network stack doesn't support multiple TX timestamps attached to the
same skb. They'll want the TX timestamp from the PHY and not from your
switch.

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06 13:23             ` Kurt Kanzenbach
@ 2020-10-06 13:42               ` Vladimir Oltean
  2020-10-06 14:05                 ` Kurt Kanzenbach
  2020-10-06 13:56               ` Vladimir Oltean
  1 sibling, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-06 13:42 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Tue, Oct 06, 2020 at 03:23:36PM +0200, Kurt Kanzenbach wrote:
> On Tue Oct 06 2020, Vladimir Oltean wrote:
> Does this mean that tagged traffic is forwarded no matter what?

Precisely. The bridge VLAN table should be irrelevant to the acceptance
or forwarding decision of the packet if vlan_filtering is 0.

> That doesn't work with the current implementation, because the VLAN
> tags are interpreted by default. There's a global flag to put the
> switch in VLAN unaware mode. But it's global and not per bridge or
> port.

Oh, there is? Maybe you can use it then.

JUST FOR CONTEXT, for sja1105 and felix/ocelot, this is the mode that
they're operating in, when a bridge with vlan_filtering=0 is configured
as an upper.

In sja1105, I don't even have the VLAN awareness flag that you have. So
I need to change the VLAN TPID from 0x8100 to 0xdadb, and the switch
will think that VLAN-tagged frames aren't VLAN. So all frames are tagged
internally by the switch with the port-based VLAN ID and PCP, when in
vlan_filtering=0.
And because my knob is global and not per bridge either, I just set
ds->vlan_filtering_is_global = true and let DSA handle the rest.

As for ocelot/felix, those switches have 2 knobs:
- VLAN awareness: does the ingress port derive the classified VLAN from
  the packet's 802.1Q header? If yes, the VLAN ID and PCP are taken from
  the packet. If not, they are taken from the port-based default.
- VLAN ingress filtering: does the ingress port drop a VLAN-tagged frame
  if the classified VLAN is not installed in its ingress filter?

As you may guess, even for ocelot/felix, when we have a bridge with
vlan_filtering=0, we are still configuring it as:
VLAN awareness = disabled
VLAN ingress filtering = enabled

Because the classified VLAN is not derived from the packet, it will
always be equal to the pvid of the port, which is installed privately by
the driver. So no packet drops due to VLAN, regardless of VLAN ID.

> So you're saying private VLANs can be used but the user or the other
> kernel modules shouldn't be allowed to use them to simplify the
> implementation?  Makes sense to me.

Yes.
And because the user is more likely to install VLAN 2 and 3 than 4095
and 4094, maybe you could use private VLANs from the end of the range,
just to make this restriction less obvious (or maybe not at all).

> The egress port has to member to that VLAN.

Same as ocelot/felix. This is the reason why we make it VLAN-unaware.
There's no point in disabling just VLAN ingress filtering if the end
result is still going to be a drop, albeit due to a different reason (no
destinations).

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-06 13:32           ` Vladimir Oltean
@ 2020-10-06 13:56             ` Kurt Kanzenbach
  2020-10-06 14:01               ` Vladimir Oltean
  0 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-06 13:56 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 623 bytes --]

On Tue Oct 06 2020, Vladimir Oltean wrote:
> On Tue, Oct 06, 2020 at 03:30:36PM +0200, Kurt Kanzenbach wrote:
>> That's the point. The user (or anybody else) cannot disable hardware
>> stamping, because it is always performed. So, why should it be allowed
>> to disable it even when it cannot be disabled?
>
> Because your driver's user can attach a PTP PHY to your switch port, and
> the network stack doesn't support multiple TX timestamps attached to the
> same skb. They'll want the TX timestamp from the PHY and not from your
> switch.

Yeah, sure. That use case makes sense. What's the problem exactly?

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06 13:23             ` Kurt Kanzenbach
  2020-10-06 13:42               ` Vladimir Oltean
@ 2020-10-06 13:56               ` Vladimir Oltean
  2020-10-06 14:13                 ` Kurt Kanzenbach
  2020-10-11 12:29                 ` Kurt Kanzenbach
  1 sibling, 2 replies; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-06 13:56 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Tue, Oct 06, 2020 at 03:23:36PM +0200, Kurt Kanzenbach wrote:
> So you're saying private VLANs can be used but the user or the other
> kernel modules shouldn't be allowed to use them to simplify the
> implementation?  Makes sense to me.

It would be interesting to see if you could simply turn off VLAN
awareness in standalone mode, and still use unique pvids per port.
Then you would have no further restriction for VLAN-tagged traffic with
unknown VLANs or with 8021q uppers having the same VLAN ID on multiple
ports.

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-06 13:56             ` Kurt Kanzenbach
@ 2020-10-06 14:01               ` Vladimir Oltean
  2020-10-07 10:39                 ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-06 14:01 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Tue, Oct 06, 2020 at 03:56:31PM +0200, Kurt Kanzenbach wrote:
> On Tue Oct 06 2020, Vladimir Oltean wrote:
> > On Tue, Oct 06, 2020 at 03:30:36PM +0200, Kurt Kanzenbach wrote:
> >> That's the point. The user (or anybody else) cannot disable hardware
> >> stamping, because it is always performed. So, why should it be allowed
> >> to disable it even when it cannot be disabled?
> >
> > Because your driver's user can attach a PTP PHY to your switch port, and
> > the network stack doesn't support multiple TX timestamps attached to the
> > same skb. They'll want the TX timestamp from the PHY and not from your
> > switch.
> 
> Yeah, sure. That use case makes sense. What's the problem exactly?

The SO_TIMESTAMPING / SO_TIMESTAMPNS cmsg socket API simply doesn't have
any sort of identification for a hardware TX timestamp (where it came
from). So when you'll poll for TX timestamps, you'll receive a TX
timestamp from the PHY and another one from the switch, and those will
be in a race with one another, so you won't know which one is which.

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06 13:42               ` Vladimir Oltean
@ 2020-10-06 14:05                 ` Kurt Kanzenbach
  2020-10-06 14:10                   ` Vladimir Oltean
  0 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-06 14:05 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 3005 bytes --]

On Tue Oct 06 2020, Vladimir Oltean wrote:
> On Tue, Oct 06, 2020 at 03:23:36PM +0200, Kurt Kanzenbach wrote:
>> On Tue Oct 06 2020, Vladimir Oltean wrote:
>> Does this mean that tagged traffic is forwarded no matter what?
>
> Precisely. The bridge VLAN table should be irrelevant to the acceptance
> or forwarding decision of the packet if vlan_filtering is 0.

I see.

>
>> That doesn't work with the current implementation, because the VLAN
>> tags are interpreted by default. There's a global flag to put the
>> switch in VLAN unaware mode. But it's global and not per bridge or
>> port.
>
> Oh, there is? Maybe you can use it then.
>
> JUST FOR CONTEXT, for sja1105 and felix/ocelot, this is the mode that
> they're operating in, when a bridge with vlan_filtering=0 is configured
> as an upper.
>
> In sja1105, I don't even have the VLAN awareness flag that you have. So
> I need to change the VLAN TPID from 0x8100 to 0xdadb, and the switch
> will think that VLAN-tagged frames aren't VLAN. So all frames are tagged
> internally by the switch with the port-based VLAN ID and PCP, when in
> vlan_filtering=0.
> And because my knob is global and not per bridge either, I just set
> ds->vlan_filtering_is_global = true and let DSA handle the rest.

What's that flag doing? ...

	/* Disallow bridge core from requesting different VLAN awareness
	 * settings on ports if not hardware-supported
	 */
	bool			vlan_filtering_is_global;

OK, that's what I need for the bridging part.

>
> As for ocelot/felix, those switches have 2 knobs:
> - VLAN awareness: does the ingress port derive the classified VLAN from
>   the packet's 802.1Q header? If yes, the VLAN ID and PCP are taken from
>   the packet. If not, they are taken from the port-based default.
> - VLAN ingress filtering: does the ingress port drop a VLAN-tagged frame
>   if the classified VLAN is not installed in its ingress filter?
>
> As you may guess, even for ocelot/felix, when we have a bridge with
> vlan_filtering=0, we are still configuring it as:
> VLAN awareness = disabled
> VLAN ingress filtering = enabled
>
> Because the classified VLAN is not derived from the packet, it will
> always be equal to the pvid of the port, which is installed privately by
> the driver. So no packet drops due to VLAN, regardless of VLAN ID.
>
>> So you're saying private VLANs can be used but the user or the other
>> kernel modules shouldn't be allowed to use them to simplify the
>> implementation?  Makes sense to me.
>
> Yes.
> And because the user is more likely to install VLAN 2 and 3 than 4095
> and 4094, maybe you could use private VLANs from the end of the range,
> just to make this restriction less obvious (or maybe not at all).
>
>> The egress port has to member to that VLAN.
>
> Same as ocelot/felix. This is the reason why we make it VLAN-unaware.
> There's no point in disabling just VLAN ingress filtering if the end
> result is still going to be a drop, albeit due to a different reason (no
> destinations).

OK.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06 14:05                 ` Kurt Kanzenbach
@ 2020-10-06 14:10                   ` Vladimir Oltean
  0 siblings, 0 replies; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-06 14:10 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Tue, Oct 06, 2020 at 04:05:11PM +0200, Kurt Kanzenbach wrote:
> > And because my knob is global and not per bridge either, I just set
> > ds->vlan_filtering_is_global = true and let DSA handle the rest.
>
> What's that flag doing? ...
>
> 	/* Disallow bridge core from requesting different VLAN awareness
> 	 * settings on ports if not hardware-supported
> 	 */
> 	bool			vlan_filtering_is_global;
>
> OK, that's what I need for the bridging part.

Yes, with the mention that not all checks for 8021q uppers may be in
place properly today. But we are in a better position of adding those
checks now, since we have a good place to start in
dsa_slave_netdevice_event. If you find scenarios that should be rejected
when this flag is set, but aren't, let me know and we'll fix that.
Nonetheless, if you go this route, then yes, you should set that flag.

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06 13:56               ` Vladimir Oltean
@ 2020-10-06 14:13                 ` Kurt Kanzenbach
  2020-10-11 12:29                 ` Kurt Kanzenbach
  1 sibling, 0 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-06 14:13 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 615 bytes --]

On Tue Oct 06 2020, Vladimir Oltean wrote:
> On Tue, Oct 06, 2020 at 03:23:36PM +0200, Kurt Kanzenbach wrote:
>> So you're saying private VLANs can be used but the user or the other
>> kernel modules shouldn't be allowed to use them to simplify the
>> implementation?  Makes sense to me.
>
> It would be interesting to see if you could simply turn off VLAN
> awareness in standalone mode, and still use unique pvids per port.
> Then you would have no further restriction for VLAN-tagged traffic with
> unknown VLANs or with 8021q uppers having the same VLAN ID on multiple
> ports.

True. That needs to be tested.


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-06 14:01               ` Vladimir Oltean
@ 2020-10-07 10:39                 ` Kurt Kanzenbach
  2020-10-07 10:54                   ` Vladimir Oltean
  0 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-07 10:39 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 1720 bytes --]

On Tue Oct 06 2020, Vladimir Oltean wrote:
> On Tue, Oct 06, 2020 at 03:56:31PM +0200, Kurt Kanzenbach wrote:
>> On Tue Oct 06 2020, Vladimir Oltean wrote:
>> > On Tue, Oct 06, 2020 at 03:30:36PM +0200, Kurt Kanzenbach wrote:
>> >> That's the point. The user (or anybody else) cannot disable hardware
>> >> stamping, because it is always performed. So, why should it be allowed
>> >> to disable it even when it cannot be disabled?
>> >
>> > Because your driver's user can attach a PTP PHY to your switch port, and
>> > the network stack doesn't support multiple TX timestamps attached to the
>> > same skb. They'll want the TX timestamp from the PHY and not from your
>> > switch.
>> 
>> Yeah, sure. That use case makes sense. What's the problem exactly?
>
> The SO_TIMESTAMPING / SO_TIMESTAMPNS cmsg socket API simply doesn't have
> any sort of identification for a hardware TX timestamp (where it came
> from).

This is sounds like a problem. For instance the hellcreek switch has
actually three ptp hardware clocks and the time stamping can be
configured to use either one of them. How would the user space
distinguish what time stamp is taken by which clock? This is not a
problem at the moment, because currently only the synchronized clock is
exported to user space. See change log of this patch.

> So when you'll poll for TX timestamps, you'll receive a TX
> timestamp from the PHY and another one from the switch, and those will
> be in a race with one another, so you won't know which one is which.

OK. So what happens if the driver will accept to disable hardware
timestamping? Is there anything else that needs to be implemented? Are
there (good) examples?

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-07 10:39                 ` Kurt Kanzenbach
@ 2020-10-07 10:54                   ` Vladimir Oltean
  2020-10-08  8:34                     ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-07 10:54 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Wed, Oct 07, 2020 at 12:39:49PM +0200, Kurt Kanzenbach wrote:
> On Tue Oct 06 2020, Vladimir Oltean wrote:
> > On Tue, Oct 06, 2020 at 03:56:31PM +0200, Kurt Kanzenbach wrote:
> >> Yeah, sure. That use case makes sense. What's the problem exactly?
> >
> > The SO_TIMESTAMPING / SO_TIMESTAMPNS cmsg socket API simply doesn't have
> > any sort of identification for a hardware TX timestamp (where it came
> > from).
> 
> This is sounds like a problem.

Yeah, tell me about it.

> For instance the hellcreek switch has actually three ptp hardware
> clocks and the time stamping can be configured to use either one of
> them.

The sja1105 also has a corrected and an uncorrected PTP clock that can
take timestamps. Initially I had thought I'd be going to spend some time
figuring out multi-PHC support, but now I don't see any practical reason
to use the uncorrected PHC for anything.

> How would the user space distinguish what time stamp is taken by
> which clock? This is not a problem at the moment, because currently
> only the synchronized clock is exported to user space. See change log
> of this patch.

It wouldn't, of course. You'd need to add the plumbing for that.

> > So when you'll poll for TX timestamps, you'll receive a TX
> > timestamp from the PHY and another one from the switch, and those will
> > be in a race with one another, so you won't know which one is which.
> 
> OK. So what happens if the driver will accept to disable hardware
> timestamping? Is there anything else that needs to be implemented? Are
> there (good) examples?

It needs to not call skb_complete_tx_timestamp() and friends.

For PHY timestamping, it also needs to invoke the correct methods for RX
and for TX, where the PHY timestamping hooks will get called. I don't
think that DSA is compatible yet with PHY timestamping, but it is
probably a trivial modification. Please read
Documentation/networking/timestamping.rst, we try to keep it fairly
comprehensive.

Thanks,
-Vladimir

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-07 10:54                   ` Vladimir Oltean
@ 2020-10-08  8:34                     ` Kurt Kanzenbach
  2020-10-08  9:44                       ` Vladimir Oltean
  0 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-08  8:34 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 2027 bytes --]

On Wed Oct 07 2020, Vladimir Oltean wrote:
> On Wed, Oct 07, 2020 at 12:39:49PM +0200, Kurt Kanzenbach wrote:
>> For instance the hellcreek switch has actually three ptp hardware
>> clocks and the time stamping can be configured to use either one of
>> them.
>
> The sja1105 also has a corrected and an uncorrected PTP clock that can
> take timestamps. Initially I had thought I'd be going to spend some time
> figuring out multi-PHC support, but now I don't see any practical reason
> to use the uncorrected PHC for anything.

Just out of curiosity: How do you implement 802.1AS then? My
understanding is that the free-running clock has to be used for the
calculation of the peer delays and such meaning there should be a way to
get access to both PHCs or having some form of cross timestamping
available.

The hellcreek switch can take cross snapshots of all three ptp clocks in
hardware for that purpose.

>> > So when you'll poll for TX timestamps, you'll receive a TX
>> > timestamp from the PHY and another one from the switch, and those will
>> > be in a race with one another, so you won't know which one is which.
>> 
>> OK. So what happens if the driver will accept to disable hardware
>> timestamping? Is there anything else that needs to be implemented? Are
>> there (good) examples?
>
> It needs to not call skb_complete_tx_timestamp() and friends.
>
> For PHY timestamping, it also needs to invoke the correct methods for RX
> and for TX, where the PHY timestamping hooks will get called. I don't
> think that DSA is compatible yet with PHY timestamping, but it is
> probably a trivial modification.

Hmm? If DSA doesn't support PHY timestamping how are other DSA drivers
dealing with it then? I'm getting really confused.

Furthermore, there is no hellcreek hardware available with timestamping
capable PHYs. How am I supposed to even test this?

For now, until there is hardware available, PHY timestamping is not
supported with the hellcreek switch.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-08  8:34                     ` Kurt Kanzenbach
@ 2020-10-08  9:44                       ` Vladimir Oltean
  2020-10-08 10:01                         ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-08  9:44 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Thu, Oct 08, 2020 at 10:34:11AM +0200, Kurt Kanzenbach wrote:
> On Wed Oct 07 2020, Vladimir Oltean wrote:
> > On Wed, Oct 07, 2020 at 12:39:49PM +0200, Kurt Kanzenbach wrote:
> >> For instance the hellcreek switch has actually three ptp hardware
> >> clocks and the time stamping can be configured to use either one of
> >> them.
> >
> > The sja1105 also has a corrected and an uncorrected PTP clock that can
> > take timestamps. Initially I had thought I'd be going to spend some time
> > figuring out multi-PHC support, but now I don't see any practical reason
> > to use the uncorrected PHC for anything.
> 
> Just out of curiosity: How do you implement 802.1AS then? My
> understanding is that the free-running clock has to be used for the

Has to be? I couldn't find that wording in IEEE 802.1AS-2011.

> calculation of the peer delays and such meaning there should be a way to
> get access to both PHCs or having some form of cross timestamping
> available.
> 
> The hellcreek switch can take cross snapshots of all three ptp clocks in
> hardware for that purpose.

Well, at the end of the day, all the other TSN offloads (tc-taprio,
tc-gate) will still have to use the synchronized PTP clock, so what
we're doing is we're simply letting that clock be synchronized by ptp4l.

> >> > So when you'll poll for TX timestamps, you'll receive a TX
> >> > timestamp from the PHY and another one from the switch, and those will
> >> > be in a race with one another, so you won't know which one is which.
> >> 
> >> OK. So what happens if the driver will accept to disable hardware
> >> timestamping? Is there anything else that needs to be implemented? Are
> >> there (good) examples?
> >
> > It needs to not call skb_complete_tx_timestamp() and friends.
> >
> > For PHY timestamping, it also needs to invoke the correct methods for RX
> > and for TX, where the PHY timestamping hooks will get called. I don't
> > think that DSA is compatible yet with PHY timestamping, but it is
> > probably a trivial modification.
> 
> Hmm? If DSA doesn't support PHY timestamping how are other DSA drivers
> dealing with it then? I'm getting really confused.

They aren't dealing with it, of course.

> Furthermore, there is no hellcreek hardware available with timestamping
> capable PHYs. How am I supposed to even test this?
> 
> For now, until there is hardware available, PHY timestamping is not
> supported with the hellcreek switch.

I was just pointing out that this is something you'll certainly have to
change if somebody will want PHY timestamping.

Even without hardware, you _could_ probably test that DSA is doing the
right thing by simply adding the PTP timestamping ops to a PHY driver
that you own, and inject dummy timestamps. The expectation becomes that
user space gets those dummy timestamps, and not the ones emitted by your
switch.

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-08  9:44                       ` Vladimir Oltean
@ 2020-10-08 10:01                         ` Kurt Kanzenbach
  2020-10-08 12:55                           ` Kamil Alkhouri
  0 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-08 10:01 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 3629 bytes --]

On Thu Oct 08 2020, Vladimir Oltean wrote:
> On Thu, Oct 08, 2020 at 10:34:11AM +0200, Kurt Kanzenbach wrote:
>> On Wed Oct 07 2020, Vladimir Oltean wrote:
>> > On Wed, Oct 07, 2020 at 12:39:49PM +0200, Kurt Kanzenbach wrote:
>> >> For instance the hellcreek switch has actually three ptp hardware
>> >> clocks and the time stamping can be configured to use either one of
>> >> them.
>> >
>> > The sja1105 also has a corrected and an uncorrected PTP clock that can
>> > take timestamps. Initially I had thought I'd be going to spend some time
>> > figuring out multi-PHC support, but now I don't see any practical reason
>> > to use the uncorrected PHC for anything.
>> 
>> Just out of curiosity: How do you implement 802.1AS then? My
>> understanding is that the free-running clock has to be used for the
>
> Has to be? I couldn't find that wording in IEEE 802.1AS-2011.

It doesn't has to be, it *should* be. That's at least the outcome we had
after lots of discussions. Actually Kamil (on Cc) is the expert on this
topic.

>
>> calculation of the peer delays and such meaning there should be a way to
>> get access to both PHCs or having some form of cross timestamping
>> available.
>> 
>> The hellcreek switch can take cross snapshots of all three ptp clocks in
>> hardware for that purpose.
>
> Well, at the end of the day, all the other TSN offloads (tc-taprio,
> tc-gate) will still have to use the synchronized PTP clock, so what
> we're doing is we're simply letting that clock be synchronized by
> ptp4l.

Yes, the synchronized clock is of course needed for the traffic
scheduling and so on. This is what we do here in this code as well. Only
the synchronized one is exported to user space and used. However, the
multi PHCs issue should be addressed as well at some point.

>
>> >> > So when you'll poll for TX timestamps, you'll receive a TX
>> >> > timestamp from the PHY and another one from the switch, and those will
>> >> > be in a race with one another, so you won't know which one is which.
>> >> 
>> >> OK. So what happens if the driver will accept to disable hardware
>> >> timestamping? Is there anything else that needs to be implemented? Are
>> >> there (good) examples?
>> >
>> > It needs to not call skb_complete_tx_timestamp() and friends.
>> >
>> > For PHY timestamping, it also needs to invoke the correct methods for RX
>> > and for TX, where the PHY timestamping hooks will get called. I don't
>> > think that DSA is compatible yet with PHY timestamping, but it is
>> > probably a trivial modification.
>> 
>> Hmm? If DSA doesn't support PHY timestamping how are other DSA drivers
>> dealing with it then? I'm getting really confused.
>
> They aren't dealing with it, of course.
>
>> Furthermore, there is no hellcreek hardware available with timestamping
>> capable PHYs. How am I supposed to even test this?
>> 
>> For now, until there is hardware available, PHY timestamping is not
>> supported with the hellcreek switch.
>
> I was just pointing out that this is something you'll certainly have to
> change if somebody will want PHY timestamping.

Understood.

>
> Even without hardware, you _could_ probably test that DSA is doing the
> right thing by simply adding the PTP timestamping ops to a PHY driver
> that you own, and inject dummy timestamps. The expectation becomes that
> user space gets those dummy timestamps, and not the ones emitted by your
> switch.

Of course it can be mocked. Whenever somebody wants to do PHY
timestamping with a hellcreek switch this issue can be re-visited.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06 10:13         ` Kurt Kanzenbach
  2020-10-06 11:32           ` Vladimir Oltean
@ 2020-10-08 11:49           ` Vladimir Oltean
  2020-10-09  5:58             ` Kurt Kanzenbach
  1 sibling, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-08 11:49 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Tue, Oct 06, 2020 at 12:13:04PM +0200, Kurt Kanzenbach wrote:
> >> >> +static const struct hellcreek_platform_data de1soc_r1_pdata = {
> >> >> +	.num_ports	 = 4,
> >> >> +	.is_100_mbits	 = 1,
> >> >> +	.qbv_support	 = 1,
> >> >> +	.qbv_on_cpu_port = 1,
> >> >
> >> > Why does this matter?
> >> 
> >> Because Qbv on the CPU port is a feature and not all switch variants
> >> have that. It will matter as soon as TAPRIO is implemented.
> >
> > How do you plan to install a tc-taprio qdisc on the CPU port?
> 
> That's an issue to be sorted out.

Do you have a compelling use case for tc-taprio on the CPU port though?
I've been waiting for someone to put one on the table.
If it's just "nice to have", I don't think that DSA will change just to
accomodate that. The fact that the CPU port doesn't have a net device is
already pretty much the established behavior.

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-08 10:01                         ` Kurt Kanzenbach
@ 2020-10-08 12:55                           ` Kamil Alkhouri
  2020-10-08 15:09                             ` Vladimir Oltean
  0 siblings, 1 reply; 51+ messages in thread
From: Kamil Alkhouri @ 2020-10-08 12:55 UTC (permalink / raw)
  To: Kurt Kanzenbach, Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, ilias.apalodimas

Hello dears,

On Thu, 2020-10-08 at 12:01 +0200, Kurt Kanzenbach wrote:
> On Thu Oct 08 2020, Vladimir Oltean wrote:
> > On Thu, Oct 08, 2020 at 10:34:11AM +0200, Kurt Kanzenbach wrote:
> > > On Wed Oct 07 2020, Vladimir Oltean wrote:
> > > > On Wed, Oct 07, 2020 at 12:39:49PM +0200, Kurt Kanzenbach
> > > > wrote:
> > > > > For instance the hellcreek switch has actually three ptp
> > > > > hardware
> > > > > clocks and the time stamping can be configured to use either
> > > > > one of
> > > > > them.
> > > > 
> > > > The sja1105 also has a corrected and an uncorrected PTP clock
> > > > that can
> > > > take timestamps. Initially I had thought I'd be going to spend
> > > > some time
> > > > figuring out multi-PHC support, but now I don't see any
> > > > practical reason
> > > > to use the uncorrected PHC for anything.
> > > 
> > > Just out of curiosity: How do you implement 802.1AS then? My
> > > understanding is that the free-running clock has to be used for
> > > the
> > 
> > Has to be? I couldn't find that wording in IEEE 802.1AS-2011.
> 
> It doesn't has to be, it *should* be. That's at least the outcome we
> had
> after lots of discussions. Actually Kamil (on Cc) is the expert on
> this
> topic.

According to 802.1AS-2011 (10.1.1): "The LocalClock entity is a free-
running clock (see 3.3) that provides a common time to the time-aware
system, relative to an arbitrary epoch.", "... All timestamps are taken
relative to the LocalClock entity". The same statement holds true for
802.1AS-2020 (10.1.2.1).

> > > calculation of the peer delays and such meaning there should be a
> > > way to
> > > get access to both PHCs or having some form of cross timestamping
> > > available.
> > > 
> > > The hellcreek switch can take cross snapshots of all three ptp
> > > clocks in
> > > hardware for that purpose.
> > 
> > Well, at the end of the day, all the other TSN offloads (tc-taprio,
> > tc-gate) will still have to use the synchronized PTP clock, so what
> > we're doing is we're simply letting that clock be synchronized by
> > ptp4l.
> 
> Yes, the synchronized clock is of course needed for the traffic
> scheduling and so on. This is what we do here in this code as well.
> Only
> the synchronized one is exported to user space and used. However, the
> multi PHCs issue should be addressed as well at some point.
> 
> > > > > > So when you'll poll for TX timestamps, you'll receive a TX
> > > > > > timestamp from the PHY and another one from the switch, and
> > > > > > those will
> > > > > > be in a race with one another, so you won't know which one
> > > > > > is which.
> > > > > 
> > > > > OK. So what happens if the driver will accept to disable
> > > > > hardware
> > > > > timestamping? Is there anything else that needs to be
> > > > > implemented? Are
> > > > > there (good) examples?
> > > > 
> > > > It needs to not call skb_complete_tx_timestamp() and friends.
> > > > 
> > > > For PHY timestamping, it also needs to invoke the correct
> > > > methods for RX
> > > > and for TX, where the PHY timestamping hooks will get called. I
> > > > don't
> > > > think that DSA is compatible yet with PHY timestamping, but it
> > > > is
> > > > probably a trivial modification.
> > > 
> > > Hmm? If DSA doesn't support PHY timestamping how are other DSA
> > > drivers
> > > dealing with it then? I'm getting really confused.
> > 
> > They aren't dealing with it, of course.
> > 
> > > Furthermore, there is no hellcreek hardware available with
> > > timestamping
> > > capable PHYs. How am I supposed to even test this?
> > > 
> > > For now, until there is hardware available, PHY timestamping is
> > > not
> > > supported with the hellcreek switch.
> > 
> > I was just pointing out that this is something you'll certainly
> > have to
> > change if somebody will want PHY timestamping.
> 
> Understood.
> 
> > Even without hardware, you _could_ probably test that DSA is doing
> > the
> > right thing by simply adding the PTP timestamping ops to a PHY
> > driver
> > that you own, and inject dummy timestamps. The expectation becomes
> > that
> > user space gets those dummy timestamps, and not the ones emitted by
> > your
> > switch.
> 
> Of course it can be mocked. Whenever somebody wants to do PHY
> timestamping with a hellcreek switch this issue can be re-visited.
> 
> Thanks,
> Kurt


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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-08 12:55                           ` Kamil Alkhouri
@ 2020-10-08 15:09                             ` Vladimir Oltean
  2020-10-12 12:53                               ` Kamil Alkhouri
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-08 15:09 UTC (permalink / raw)
  To: Kamil Alkhouri
  Cc: Kurt Kanzenbach, Andrew Lunn, Vivien Didelot, Florian Fainelli,
	David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, ilias.apalodimas

Hi Kamil,

On Thu, Oct 08, 2020 at 02:55:57PM +0200, Kamil Alkhouri wrote:
> Hello dears,
> 
> On Thu, 2020-10-08 at 12:01 +0200, Kurt Kanzenbach wrote:
> > On Thu Oct 08 2020, Vladimir Oltean wrote:
> > > On Thu, Oct 08, 2020 at 10:34:11AM +0200, Kurt Kanzenbach wrote:
> > > > On Wed Oct 07 2020, Vladimir Oltean wrote:
> > > > > On Wed, Oct 07, 2020 at 12:39:49PM +0200, Kurt Kanzenbach
> > > > > wrote:
> > > > > > For instance the hellcreek switch has actually three ptp
> > > > > > hardware
> > > > > > clocks and the time stamping can be configured to use either
> > > > > > one of
> > > > > > them.
> > > > > 
> > > > > The sja1105 also has a corrected and an uncorrected PTP clock
> > > > > that can
> > > > > take timestamps. Initially I had thought I'd be going to spend
> > > > > some time
> > > > > figuring out multi-PHC support, but now I don't see any
> > > > > practical reason
> > > > > to use the uncorrected PHC for anything.
> > > > 
> > > > Just out of curiosity: How do you implement 802.1AS then? My
> > > > understanding is that the free-running clock has to be used for
> > > > the
> > > 
> > > Has to be? I couldn't find that wording in IEEE 802.1AS-2011.
> > 
> > It doesn't has to be, it *should* be. That's at least the outcome we
> > had
> > after lots of discussions. Actually Kamil (on Cc) is the expert on
> > this
> > topic.
> 
> According to 802.1AS-2011 (10.1.1): "The LocalClock entity is a free-
> running clock (see 3.3) that provides a common time to the time-aware
> system, relative to an arbitrary epoch.", "... All timestamps are taken
> relative to the LocalClock entity". The same statement holds true for
> 802.1AS-2020 (10.1.2.1).

Nice having you part of the discussion.

IEEE 802.1AS-rev draft 8.0, clause F.3 PTP options:

	The physical adjustment of the frequency of the LocalClock
	entity (i.e., physical syntonization) is allowed but not
	required.

In fact, even if that wasn't explicitly written, I am having a hard time
understanding how the "B.1.1 Frequency accuracy" requirement for the
LocalClock could be satisfied as long as it is kept free-running.
Otherwise said, what should I do as a system designer if the
LocalClock's frequency is not within +/- 100 ppm offset to the TAI
frequency, and I'm not allowed to correct it.

By the way, how would you see the split between an unsynchronized and a
synchronized PHC be implemented in the Linux kernel?

Thanks,
-Vladimir

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-08 11:49           ` Vladimir Oltean
@ 2020-10-09  5:58             ` Kurt Kanzenbach
  0 siblings, 0 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-09  5:58 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 1575 bytes --]

On Thu Oct 08 2020, Vladimir Oltean wrote:
> On Tue, Oct 06, 2020 at 12:13:04PM +0200, Kurt Kanzenbach wrote:
>> >> >> +static const struct hellcreek_platform_data de1soc_r1_pdata = {
>> >> >> +	.num_ports	 = 4,
>> >> >> +	.is_100_mbits	 = 1,
>> >> >> +	.qbv_support	 = 1,
>> >> >> +	.qbv_on_cpu_port = 1,
>> >> >
>> >> > Why does this matter?
>> >> 
>> >> Because Qbv on the CPU port is a feature and not all switch variants
>> >> have that. It will matter as soon as TAPRIO is implemented.
>> >
>> > How do you plan to install a tc-taprio qdisc on the CPU port?
>> 
>> That's an issue to be sorted out.
>
> Do you have a compelling use case for tc-taprio on the CPU port though?
> I've been waiting for someone to put one on the table.

Yes, we do. This feature is a must for switched endpoints. Imagine one
port is connected to a PLC with tight cycle times and the other port is
connected to the out side world doing best effort traffic. Under no
circumstances should the ingressing best effort traffic interfere with
the incoming real time traffic. Using strict priorities is not enough as
a best effort frame still might block the wire for a certain period of
time. Therefore, this feature exists in the hardware and Qbv is needed
on the CPU port.

> If it's just "nice to have", I don't think that DSA will change just to
> accomodate that. The fact that the CPU port doesn't have a net device is
> already pretty much the established behavior.

Yes, I know that. Anyhow we'll have to find a solution to that problem.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-06 13:56               ` Vladimir Oltean
  2020-10-06 14:13                 ` Kurt Kanzenbach
@ 2020-10-11 12:29                 ` Kurt Kanzenbach
  2020-10-11 15:30                   ` Vladimir Oltean
  1 sibling, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-11 12:29 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 1046 bytes --]

On Tue Oct 06 2020, Vladimir Oltean wrote:
> On Tue, Oct 06, 2020 at 03:23:36PM +0200, Kurt Kanzenbach wrote:
>> So you're saying private VLANs can be used but the user or the other
>> kernel modules shouldn't be allowed to use them to simplify the
>> implementation?  Makes sense to me.
>
> It would be interesting to see if you could simply turn off VLAN
> awareness in standalone mode, and still use unique pvids per port.

That doesn't work, just tested. When VLAN awareness is disabled,
everything is switched regardless of VLAN tags and table. Therefore, the
implementation could look like this:

 * bridge without filtering:
   * vlan_awareness=0
   * drop private vlans
 * bridge with vlan filtering:
   * vlan_awareness=1
   * drop private vlans
 * standalone:
   * vlan_awareness=1
   * use private vlans
   * forbid other users to use private vlans to allow
     configure_vlans_while_not_filtering behavior in .vlan_prepare()
   * forbid use of lan0.<X> and lan1.<X> in .port_prechangeupper()

So, this should work, or?

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-11 12:29                 ` Kurt Kanzenbach
@ 2020-10-11 15:30                   ` Vladimir Oltean
  2020-10-12  5:37                     ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-11 15:30 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Sun, Oct 11, 2020 at 02:29:08PM +0200, Kurt Kanzenbach wrote:
> On Tue Oct 06 2020, Vladimir Oltean wrote:
> > It would be interesting to see if you could simply turn off VLAN
> > awareness in standalone mode, and still use unique pvids per port.
>
> That doesn't work, just tested. When VLAN awareness is disabled,
> everything is switched regardless of VLAN tags and table.

That's strange, do you happen to know where things are going wrong?
I would expect:
- port VLAN awareness is disabled, so any packet is classified to the
  port-based VLAN
- the port-based VLAN is a private VLAN whose membership includes only
  that port, plus the CPU port
- the switch does not forward packets towards a port that is not member
  of the packets' classified VLAN
When VLAN awareness is disabled, are you able to cause packet drops by
deleting the pvid of the ingress port? Therefore, can you confirm that
lan1 is not a member of lan0's pvid, but the switch still forwards the
packets to it?

> Therefore, the implementation could look like this:
>
>  * bridge without filtering:
>    * vlan_awareness=0
>    * drop private vlans
>  * bridge with vlan filtering:
>    * vlan_awareness=1
>    * drop private vlans
>  * standalone:
>    * vlan_awareness=1
>    * use private vlans
>    * forbid other users to use private vlans to allow
>      configure_vlans_while_not_filtering behavior in .vlan_prepare()
>    * forbid use of lan0.<X> and lan1.<X> in .port_prechangeupper()
>
> So, this should work, or?

Yes, this is an alternative that could work.

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-11 15:30                   ` Vladimir Oltean
@ 2020-10-12  5:37                     ` Kurt Kanzenbach
  2020-10-16 12:11                       ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-12  5:37 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 1156 bytes --]

On Sun Oct 11 2020, Vladimir Oltean wrote:
> On Sun, Oct 11, 2020 at 02:29:08PM +0200, Kurt Kanzenbach wrote:
>> On Tue Oct 06 2020, Vladimir Oltean wrote:
>> > It would be interesting to see if you could simply turn off VLAN
>> > awareness in standalone mode, and still use unique pvids per port.
>>
>> That doesn't work, just tested. When VLAN awareness is disabled,
>> everything is switched regardless of VLAN tags and table.
>
> That's strange, do you happen to know where things are going wrong?

No I don't. I'll clarify with the hardware engineer.

> I would expect:
> - port VLAN awareness is disabled, so any packet is classified to the
>   port-based VLAN
> - the port-based VLAN is a private VLAN whose membership includes only
>   that port, plus the CPU port
> - the switch does not forward packets towards a port that is not member
>   of the packets' classified VLAN

Me, too.

> When VLAN awareness is disabled, are you able to cause packet drops by
> deleting the pvid of the ingress port? Therefore, can you confirm that
> lan1 is not a member of lan0's pvid, but the switch still forwards the
> packets to it?

Will test.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-08 15:09                             ` Vladimir Oltean
@ 2020-10-12 12:53                               ` Kamil Alkhouri
  2020-10-12 21:42                                 ` Richard Cochran
  0 siblings, 1 reply; 51+ messages in thread
From: Kamil Alkhouri @ 2020-10-12 12:53 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Kurt Kanzenbach, Andrew Lunn, Vivien Didelot, Florian Fainelli,
	David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, ilias.apalodimas

Hi Vladimir,

On Thu, 2020-10-08 at 18:09 +0300, Vladimir Oltean wrote:
> Hi Kamil,
> 
> On Thu, Oct 08, 2020 at 02:55:57PM +0200, Kamil Alkhouri wrote:
> > Hello dears,
> > 
> > On Thu, 2020-10-08 at 12:01 +0200, Kurt Kanzenbach wrote:
> > > On Thu Oct 08 2020, Vladimir Oltean wrote:
> > > > On Thu, Oct 08, 2020 at 10:34:11AM +0200, Kurt Kanzenbach
> > > > wrote:
> > > > > On Wed Oct 07 2020, Vladimir Oltean wrote:
> > > > > > On Wed, Oct 07, 2020 at 12:39:49PM +0200, Kurt Kanzenbach
> > > > > > wrote:
> > > > > > > For instance the hellcreek switch has actually three ptp
> > > > > > > hardware
> > > > > > > clocks and the time stamping can be configured to use
> > > > > > > either
> > > > > > > one of
> > > > > > > them.
> > > > > > 
> > > > > > The sja1105 also has a corrected and an uncorrected PTP
> > > > > > clock
> > > > > > that can
> > > > > > take timestamps. Initially I had thought I'd be going to
> > > > > > spend
> > > > > > some time
> > > > > > figuring out multi-PHC support, but now I don't see any
> > > > > > practical reason
> > > > > > to use the uncorrected PHC for anything.
> > > > > 
> > > > > Just out of curiosity: How do you implement 802.1AS then? My
> > > > > understanding is that the free-running clock has to be used
> > > > > for
> > > > > the
> > > > 
> > > > Has to be? I couldn't find that wording in IEEE 802.1AS-2011.
> > > 
> > > It doesn't has to be, it *should* be. That's at least the outcome
> > > we
> > > had
> > > after lots of discussions. Actually Kamil (on Cc) is the expert
> > > on
> > > this
> > > topic.
> > 
> > According to 802.1AS-2011 (10.1.1): "The LocalClock entity is a
> > free-
> > running clock (see 3.3) that provides a common time to the time-
> > aware
> > system, relative to an arbitrary epoch.", "... All timestamps are
> > taken
> > relative to the LocalClock entity". The same statement holds true
> > for
> > 802.1AS-2020 (10.1.2.1).
> 
> Nice having you part of the discussion.
> 
> IEEE 802.1AS-rev draft 8.0, clause F.3 PTP options:
> 
> 	The physical adjustment of the frequency of the LocalClock
> 	entity (i.e., physical syntonization) is allowed but not
> 	required.

what about phase adjustment?
I believe logical syntonization is a main part of 802.1AS-Rev and it is
actually mandatory (7.5.g). Even though physical syntonization is
allowed, the standard clearly states that it is slow and prone to gain
peaking effects (7.3.3). Therefore, it makes sense to use a free-
running clock to get the most benefit of AS-Rev when it comes to the
transport of synchronization information. 

> 
> In fact, even if that wasn't explicitly written, I am having a hard
> time
> understanding how the "B.1.1 Frequency accuracy" requirement for the
> LocalClock could be satisfied as long as it is kept free-running.
> Otherwise said, what should I do as a system designer if the
> LocalClock's frequency is not within +/- 100 ppm offset to the TAI
> frequency, and I'm not allowed to correct it.

B.1.1 defines the frequency accuracy of the local clock relative to TAI
and not to grandmaster. In my opinion, this is a physical requirement
of the quartz oscillator used to drive the time and it should be
fulfilled for all local clocks even for the ones in non-slave devices.

> 
> By the way, how would you see the split between an unsynchronized and
> a
> synchronized PHC be implemented in the Linux kernel?

I'm not an expert in kernel implementation but we have plans to discuss
possible approaches in the near future.

> 
> Thanks,
> -Vladimir

Thanks,
Kamil


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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-12 12:53                               ` Kamil Alkhouri
@ 2020-10-12 21:42                                 ` Richard Cochran
  2020-10-14  9:57                                   ` Vladimir Oltean
  0 siblings, 1 reply; 51+ messages in thread
From: Richard Cochran @ 2020-10-12 21:42 UTC (permalink / raw)
  To: Kamil Alkhouri
  Cc: Vladimir Oltean, Kurt Kanzenbach, Andrew Lunn, Vivien Didelot,
	Florian Fainelli, David S. Miller, Jakub Kicinski, netdev,
	Rob Herring, devicetree, Sebastian Andrzej Siewior,
	ilias.apalodimas

On Mon, Oct 12, 2020 at 02:53:58PM +0200, Kamil Alkhouri wrote:
> > By the way, how would you see the split between an unsynchronized and
> > a
> > synchronized PHC be implemented in the Linux kernel?

If you want, you can run your PHC using the linuxptp "free_running"
option.  Then, you can use the TIME_STATUS_NP management request to
use the remote time signal in your application.

> I'm not an expert in kernel implementation but we have plans to discuss
> possible approaches in the near future.

I don't see any need for kernel changes in this area.

Thanks,
Richard

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-12 21:42                                 ` Richard Cochran
@ 2020-10-14  9:57                                   ` Vladimir Oltean
  2020-10-14 11:01                                     ` Richard Cochran
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-14  9:57 UTC (permalink / raw)
  To: Richard Cochran
  Cc: Kamil Alkhouri, Kurt Kanzenbach, Andrew Lunn, Vivien Didelot,
	Florian Fainelli, David S. Miller, Jakub Kicinski, netdev,
	Rob Herring, devicetree, Sebastian Andrzej Siewior,
	ilias.apalodimas

On Mon, Oct 12, 2020 at 02:42:54PM -0700, Richard Cochran wrote:
> If you want, you can run your PHC using the linuxptp "free_running"
> option.  Then, you can use the TIME_STATUS_NP management request to
> use the remote time signal in your application.

I was expecting some sort of reaction to this from Kamil or Kurt.

I don't think that 'using the remote time signal in an application' is
all that needs to be done with the gPTP time, at least for a switch with
the hardware features that hellcreek has. Ultimately it should be fed
back into the hardware, such that the scheduler based on 802.1Q clause
8.6.8.4 "Enhancements for scheduled traffic" has some time scale based
on which it can run. Running tc-taprio offload on top of an
unsynchronized clock is not something productive.

So the discussion is about how to have the cake and eat it at the same
time. Silicon vendors eager to follow the latest trends in standards are
implementing hybrid PTP clocks, where an unsynchronizable version of the
clock delivers MAC timestamps to the application stack, and a
synchronizable wrapper over that same clock is what gets fed into the
offloading engines, like the ones behind the tc-taprio and tc-gate
offload. Some of these vendors perform cross-timestamping (they deliver
a timestamp from the MAC with 2, or 3, or 4, timestamps, depending on
how many PHCs that MAC has wired to it), some don't, and just deliver a
single timestamp from a configurable source.

The operating system is supposed to ??? in order to synchronize the
synchronizable clock to the virtual time retrieved via TIME_STATUS_NP
that you're talking about. The question is what to replace that ???
with, of course.

> > I'm not an expert in kernel implementation but we have plans to discuss
> > possible approaches in the near future.
>
> I don't see any need for kernel changes in this area.

I'm not an expert in kernel implementation either, but perhaps in the
light of this, you can revisit the idea that kernel changes will not be
needed (or explain more, if you still think they aren't).

Since IEEE 60802 keeps talking about multiple time domains to be used
with 802.1AS-rev (a 'universal clock domain' and a 'working clock
domain'), a decision needs to be taken somewhere about which time base
you're going to use as a source for synchronizing your tc-taprio clock.
That decision should best be taken at the application level, so in my
opinion this is an argument that the application should have explicit
access to the unsynchronizable and to the synchronizable versions of the
PTP clock.

In the Linux kernel API, a network interface can have at most one PHC.

--------------

DISCLAIMER
Yes, I know full well that everyone can write a standard, but not
everyone can implement one. At the end of the day, I'm not trying to
make an argument whether the end result is worth making all these
changes. I'm only here to learn what other people are doing, how, and
most importantly, why.

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-14  9:57                                   ` Vladimir Oltean
@ 2020-10-14 11:01                                     ` Richard Cochran
  2020-10-14 11:37                                       ` Kurt Kanzenbach
  0 siblings, 1 reply; 51+ messages in thread
From: Richard Cochran @ 2020-10-14 11:01 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Kamil Alkhouri, Kurt Kanzenbach, Andrew Lunn, Vivien Didelot,
	Florian Fainelli, David S. Miller, Jakub Kicinski, netdev,
	Rob Herring, devicetree, Sebastian Andrzej Siewior,
	ilias.apalodimas

On Wed, Oct 14, 2020 at 12:57:47PM +0300, Vladimir Oltean wrote:
> So the discussion is about how to have the cake and eat it at the same
> time.

And I wish for a pony.  With sparkles.  And a unicorn.  And a rainbow.

> Silicon vendors eager to follow the latest trends in standards are
> implementing hybrid PTP clocks, where an unsynchronizable version of the
> clock delivers MAC timestamps to the application stack, and a
> synchronizable wrapper over that same clock is what gets fed into the
> offloading engines, like the ones behind the tc-taprio and tc-gate
> offload. Some of these vendors perform cross-timestamping (they deliver
> a timestamp from the MAC with 2, or 3, or 4, timestamps, depending on
> how many PHCs that MAC has wired to it), some don't, and just deliver a
> single timestamp from a configurable source.

Sounds like it will be nearly impossible to make a single tc-taprio
framework that fits all the hardware variants.

> The operating system is supposed to ??? in order to synchronize the
> synchronizable clock to the virtual time retrieved via TIME_STATUS_NP
> that you're talking about. The question is what to replace that ???
> with, of course.

You have a choice.  Either you synchronize the local PHC to the global
TAI time base or not.  If you do synchronize the PHC, then everything
(like the globally scheduled time slots) just works.  If you decide to
follow the nonsensical idea (following 802.1-AS) and leave the PHC
free running, then you will have a difficult time scheduling those
time windows.

So it is all up to you.

> I'm not an expert in kernel implementation either, but perhaps in the
> light of this, you can revisit the idea that kernel changes will not be
> needed (or explain more, if you still think they aren't).

I am not opposed to kernel changes, but there must be:

- A clear statement of the background context, and
- an explanation of the issue to solved, and
- a realistic solution that will support the wide variety of HW. 

> DISCLAIMER
> Yes, I know full well that everyone can write a standard, but not
> everyone can implement one. At the end of the day, I'm not trying to
> make an argument whether the end result is worth making all these
> changes.

+1

That is the question.  You can easily solve this issue by simply
synchronizing the PHC to the global time base.

Thanks,
Richard

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

* Re: [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping
  2020-10-14 11:01                                     ` Richard Cochran
@ 2020-10-14 11:37                                       ` Kurt Kanzenbach
  0 siblings, 0 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-14 11:37 UTC (permalink / raw)
  To: Richard Cochran, Vladimir Oltean
  Cc: Kamil Alkhouri, Andrew Lunn, Vivien Didelot, Florian Fainelli,
	David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 2706 bytes --]

On Wed Oct 14 2020, Richard Cochran wrote:
> On Wed, Oct 14, 2020 at 12:57:47PM +0300, Vladimir Oltean wrote:
>> So the discussion is about how to have the cake and eat it at the same
>> time.
>
> And I wish for a pony.  With sparkles.  And a unicorn.  And a rainbow.
>
>> Silicon vendors eager to follow the latest trends in standards are
>> implementing hybrid PTP clocks, where an unsynchronizable version of the
>> clock delivers MAC timestamps to the application stack, and a
>> synchronizable wrapper over that same clock is what gets fed into the
>> offloading engines, like the ones behind the tc-taprio and tc-gate
>> offload. Some of these vendors perform cross-timestamping (they deliver
>> a timestamp from the MAC with 2, or 3, or 4, timestamps, depending on
>> how many PHCs that MAC has wired to it), some don't, and just deliver a
>> single timestamp from a configurable source.
>
> Sounds like it will be nearly impossible to make a single tc-taprio
> framework that fits all the hardware variants.

Why? All the gate operations work on the synchronized clock. I assume
all Qbv capable switches have a synchronized clock?

It's just that some switches have multiple PHCs instead of a single
one. It seems to be quite common to have a free-running as well as a
synchronized clock. In order for a better(?) or more accurate(?) ptp
implementation they expose not a single but rather multiple timestamps
from all PHCs (-> cross-timestamping) to user space for the ptp event
messages. That's at least my very limited understanding.

>
>> The operating system is supposed to ??? in order to synchronize the
>> synchronizable clock to the virtual time retrieved via TIME_STATUS_NP
>> that you're talking about. The question is what to replace that ???
>> with, of course.
>
> You have a choice.  Either you synchronize the local PHC to the global
> TAI time base or not.  If you do synchronize the PHC, then everything
> (like the globally scheduled time slots) just works.  If you decide to
> follow the nonsensical idea (following 802.1-AS) and leave the PHC
> free running, then you will have a difficult time scheduling those
> time windows.
>
> So it is all up to you.
>
>> I'm not an expert in kernel implementation either, but perhaps in the
>> light of this, you can revisit the idea that kernel changes will not be
>> needed (or explain more, if you still think they aren't).
>
> I am not opposed to kernel changes, but there must be:
>
> - A clear statement of the background context, and
> - an explanation of the issue to solved, and
> - a realistic solution that will support the wide variety of HW. 

Agreed.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-12  5:37                     ` Kurt Kanzenbach
@ 2020-10-16 12:11                       ` Kurt Kanzenbach
  2020-10-16 15:43                         ` Vladimir Oltean
  0 siblings, 1 reply; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-16 12:11 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 1499 bytes --]

On Mon Oct 12 2020, Kurt Kanzenbach wrote:
> On Sun Oct 11 2020, Vladimir Oltean wrote:
>> On Sun, Oct 11, 2020 at 02:29:08PM +0200, Kurt Kanzenbach wrote:
>>> On Tue Oct 06 2020, Vladimir Oltean wrote:
>>> > It would be interesting to see if you could simply turn off VLAN
>>> > awareness in standalone mode, and still use unique pvids per port.
>>>
>>> That doesn't work, just tested. When VLAN awareness is disabled,
>>> everything is switched regardless of VLAN tags and table.
>>
>> That's strange, do you happen to know where things are going wrong?
>
> No I don't. I'll clarify with the hardware engineer.

When VLAN awareness is disabled, the packet is still classified with the
pvid. But, later all rules regarding VLANs (except for the PCP field)
are ignored then. So, the programmed pvid doesn't matter in this case.

The only way to implement the non-filtering bridge behavior is this
flag. However, this has some more implications. For instance when
there's a non filtering bridge, then standalone mode doesn't work
anymore due to the VLAN unawareness. This is not a problem at the
moment, because there are only two ports. But, later when there are more
ports, then having two ports in a non-filtering bridge and one in
standalone mode doesn't work. That's another limitation that needs to be
considered when adding more ports later on.

Besides that problem everything else seem to work now in accordance to
the expected Linux behavior with roper restrictions in place.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-16 12:11                       ` Kurt Kanzenbach
@ 2020-10-16 15:43                         ` Vladimir Oltean
  2020-10-16 16:56                           ` Florian Fainelli
  0 siblings, 1 reply; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-16 15:43 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Fri, Oct 16, 2020 at 02:11:06PM +0200, Kurt Kanzenbach wrote:
> When VLAN awareness is disabled, the packet is still classified with the
> pvid. But, later all rules regarding VLANs (except for the PCP field)
> are ignored then. So, the programmed pvid doesn't matter in this case.

Ok, clear now.

> The only way to implement the non-filtering bridge behavior is this
> flag. However, this has some more implications. For instance when
> there's a non filtering bridge, then standalone mode doesn't work
> anymore due to the VLAN unawareness. This is not a problem at the
> moment, because there are only two ports. But, later when there are more
> ports, then having two ports in a non-filtering bridge and one in
> standalone mode doesn't work. That's another limitation that needs to be
> considered when adding more ports later on.

Well, then you have feedback to bring to the hardware engineers when
switches with more than 2 user ports will be instantiated.

> Besides that problem everything else seem to work now in accordance to
> the expected Linux behavior with roper restrictions in place.

Ok, that's great.

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-16 15:43                         ` Vladimir Oltean
@ 2020-10-16 16:56                           ` Florian Fainelli
  2020-10-17 10:06                             ` Kurt Kanzenbach
  2020-10-17 15:57                             ` Vladimir Oltean
  0 siblings, 2 replies; 51+ messages in thread
From: Florian Fainelli @ 2020-10-16 16:56 UTC (permalink / raw)
  To: Vladimir Oltean, Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, David S. Miller, Jakub Kicinski,
	netdev, Rob Herring, devicetree, Sebastian Andrzej Siewior,
	Richard Cochran, Kamil Alkhouri, ilias.apalodimas

On 10/16/20 8:43 AM, Vladimir Oltean wrote:
> On Fri, Oct 16, 2020 at 02:11:06PM +0200, Kurt Kanzenbach wrote:
>> When VLAN awareness is disabled, the packet is still classified with the
>> pvid. But, later all rules regarding VLANs (except for the PCP field)
>> are ignored then. So, the programmed pvid doesn't matter in this case.
> 
> Ok, clear now.
> 
>> The only way to implement the non-filtering bridge behavior is this
>> flag. However, this has some more implications. For instance when
>> there's a non filtering bridge, then standalone mode doesn't work
>> anymore due to the VLAN unawareness. This is not a problem at the
>> moment, because there are only two ports. But, later when there are more
>> ports, then having two ports in a non-filtering bridge and one in
>> standalone mode doesn't work. That's another limitation that needs to be
>> considered when adding more ports later on.
> 
> Well, then you have feedback to bring to the hardware engineers when
> switches with more than 2 user ports will be instantiated.
> 
>> Besides that problem everything else seem to work now in accordance to
>> the expected Linux behavior with roper restrictions in place.
> 
> Ok, that's great.

I probably missed parts of this long discussion, but for this generation
of switches, does that mean that you will only allow a bridge with
vlan_filtering=1 to be configured and also refuse toggling of
vlan_filtering at run time?
-- 
Florian

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-16 16:56                           ` Florian Fainelli
@ 2020-10-17 10:06                             ` Kurt Kanzenbach
  2020-10-17 15:57                             ` Vladimir Oltean
  1 sibling, 0 replies; 51+ messages in thread
From: Kurt Kanzenbach @ 2020-10-17 10:06 UTC (permalink / raw)
  To: Florian Fainelli, Vladimir Oltean
  Cc: Andrew Lunn, Vivien Didelot, David S. Miller, Jakub Kicinski,
	netdev, Rob Herring, devicetree, Sebastian Andrzej Siewior,
	Richard Cochran, Kamil Alkhouri, ilias.apalodimas

[-- Attachment #1: Type: text/plain, Size: 1205 bytes --]

On Fri Oct 16 2020, Florian Fainelli wrote:
> I probably missed parts of this long discussion, but for this generation
> of switches, does that mean that you will only allow a bridge with
> vlan_filtering=1 to be configured and also refuse toggling of
> vlan_filtering at run time?

Nope. To sum up the driver will use "private" VLANs for the port
separation. That will lead to certain restrictions. These are:

 * Private VLANs cannot be used by the user or other kernel modules (one
   per port)
 * Mixed vlan_filtering bridges are not supported, as there's only a
   global VLAN awareness flag (ds->vlan_filtering_is_global)
 * vlan_filtering=0 bridges and having standalone ports is not
   supported
 * Same VLANs on top of standalone ports such as lan0.100 and lan1.100
   will break port separation and are also not supported

Most of these restrictions are not important at the moment, because
there are only two user ports. Either they're in a bridge or not. The
"mixed" cases are interesting.

However, as Vladimir suggested, I'll point these limitation to the hw
engineers when they start to develop these switches with more than two
user ports.

Hope that summary makes it clear.

Thanks,
Kurt

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH net-next v6 2/7] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-10-16 16:56                           ` Florian Fainelli
  2020-10-17 10:06                             ` Kurt Kanzenbach
@ 2020-10-17 15:57                             ` Vladimir Oltean
  1 sibling, 0 replies; 51+ messages in thread
From: Vladimir Oltean @ 2020-10-17 15:57 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Kurt Kanzenbach, Andrew Lunn, Vivien Didelot, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

On Fri, Oct 16, 2020 at 09:56:22AM -0700, Florian Fainelli wrote:
> I probably missed parts of this long discussion, but for this generation
> of switches, does that mean that you will only allow a bridge with
> vlan_filtering=1 to be configured and also refuse toggling of
> vlan_filtering at run time?

No, the bridge vlan_filtering option will be supported as both on or off.
The limitation that you're probably thinking of is that
NETIF_F_HW_VLAN_CTAG_FILTER will be non-optionally forced on. Otherwise
stated, ports in standalone mode will drop VLAN-tagged traffic unless
they have an 8021q upper with that VLAN ID. Which is probably ok
considering that the network stack would drop them anyway in absence of
an 8021q upper, but they won't be visible in tcpdump either, as things
stand. Otherwise stated, standalone hellcreek ports cannot support
promiscuous mode.
I know that currently DSA sets up NETIF_F_HW_VLAN_CTAG_FILTER as
forced-on for everybody, but that limitation could be lifted for some
switches, but not for hellcreek. Just something to keep in mind.

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

end of thread, other threads:[~2020-10-17 15:57 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-04 11:29 [PATCH net-next v6 0/7] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
2020-10-04 11:29 ` [PATCH net-next v6 1/7] net: dsa: Add tag handling for Hirschmann Hellcreek switches Kurt Kanzenbach
2020-10-04 11:56   ` Vladimir Oltean
2020-10-05  6:14     ` Kurt Kanzenbach
2020-10-04 11:29 ` [PATCH net-next v6 2/7] net: dsa: Add DSA driver " Kurt Kanzenbach
2020-10-04 12:56   ` Vladimir Oltean
2020-10-06  6:09     ` Kurt Kanzenbach
2020-10-06  9:20       ` Vladimir Oltean
2020-10-06 10:13         ` Kurt Kanzenbach
2020-10-06 11:32           ` Vladimir Oltean
2020-10-06 12:37             ` Vladimir Oltean
2020-10-06 13:23             ` Kurt Kanzenbach
2020-10-06 13:42               ` Vladimir Oltean
2020-10-06 14:05                 ` Kurt Kanzenbach
2020-10-06 14:10                   ` Vladimir Oltean
2020-10-06 13:56               ` Vladimir Oltean
2020-10-06 14:13                 ` Kurt Kanzenbach
2020-10-11 12:29                 ` Kurt Kanzenbach
2020-10-11 15:30                   ` Vladimir Oltean
2020-10-12  5:37                     ` Kurt Kanzenbach
2020-10-16 12:11                       ` Kurt Kanzenbach
2020-10-16 15:43                         ` Vladimir Oltean
2020-10-16 16:56                           ` Florian Fainelli
2020-10-17 10:06                             ` Kurt Kanzenbach
2020-10-17 15:57                             ` Vladimir Oltean
2020-10-08 11:49           ` Vladimir Oltean
2020-10-09  5:58             ` Kurt Kanzenbach
2020-10-04 11:29 ` [PATCH net-next v6 3/7] net: dsa: hellcreek: Add PTP clock support Kurt Kanzenbach
2020-10-04 11:29 ` [PATCH net-next v6 4/7] net: dsa: hellcreek: Add support for hardware timestamping Kurt Kanzenbach
2020-10-04 14:30   ` Vladimir Oltean
2020-10-06  6:27     ` Kurt Kanzenbach
2020-10-06  7:28       ` Vladimir Oltean
2020-10-06 13:30         ` Kurt Kanzenbach
2020-10-06 13:32           ` Vladimir Oltean
2020-10-06 13:56             ` Kurt Kanzenbach
2020-10-06 14:01               ` Vladimir Oltean
2020-10-07 10:39                 ` Kurt Kanzenbach
2020-10-07 10:54                   ` Vladimir Oltean
2020-10-08  8:34                     ` Kurt Kanzenbach
2020-10-08  9:44                       ` Vladimir Oltean
2020-10-08 10:01                         ` Kurt Kanzenbach
2020-10-08 12:55                           ` Kamil Alkhouri
2020-10-08 15:09                             ` Vladimir Oltean
2020-10-12 12:53                               ` Kamil Alkhouri
2020-10-12 21:42                                 ` Richard Cochran
2020-10-14  9:57                                   ` Vladimir Oltean
2020-10-14 11:01                                     ` Richard Cochran
2020-10-14 11:37                                       ` Kurt Kanzenbach
2020-10-04 11:29 ` [PATCH net-next v6 5/7] net: dsa: hellcreek: Add PTP status LEDs Kurt Kanzenbach
2020-10-04 11:29 ` [PATCH net-next v6 6/7] dt-bindings: Add vendor prefix for Hirschmann Kurt Kanzenbach
2020-10-04 11:29 ` [PATCH net-next v6 7/7] dt-bindings: net: dsa: Add documentation for Hellcreek switches Kurt Kanzenbach

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).