Netdev Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH v3 0/8] Hirschmann Hellcreek DSA driver
@ 2020-08-20  8:11 Kurt Kanzenbach
  2020-08-20  8:11 ` [PATCH v3 1/8] net: dsa: Add tag handling for Hirschmann Hellcreek switches Kurt Kanzenbach
                   ` (8 more replies)
  0 siblings, 9 replies; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-20  8:11 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean, 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, hardware
timestamping and TAPRIO offloading.

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/

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 (6):
  net: dsa: Add tag handling for Hirschmann Hellcreek switches
  net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  net: dsa: hellcreek: Add TAPRIO offloading support
  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           |  125 ++
 .../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        | 1551 +++++++++++++++++
 drivers/net/dsa/hirschmann/hellcreek.h        |  301 ++++
 .../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 +
 include/net/dsa.h                             |    2 +
 net/dsa/Kconfig                               |    6 +
 net/dsa/Makefile                              |    1 +
 net/dsa/tag_hellcreek.c                       |  101 ++
 16 files changed, 3171 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 net/dsa/tag_hellcreek.c

-- 
2.20.1


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

* [PATCH v3 1/8] net: dsa: Add tag handling for Hirschmann Hellcreek switches
  2020-08-20  8:11 [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
@ 2020-08-20  8:11 ` Kurt Kanzenbach
  2020-08-20  8:11 ` [PATCH v3 2/8] net: dsa: Add DSA driver " Kurt Kanzenbach
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-20  8:11 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean, 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 75c8fac82017..5cf710580a28 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	[flat|nested] 44+ messages in thread

* [PATCH v3 2/8] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-08-20  8:11 [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
  2020-08-20  8:11 ` [PATCH v3 1/8] net: dsa: Add tag handling for Hirschmann Hellcreek switches Kurt Kanzenbach
@ 2020-08-20  8:11 ` Kurt Kanzenbach
  2020-08-24 22:44   ` Andrew Lunn
  2020-08-20  8:11 ` [PATCH v3 3/8] net: dsa: hellcreek: Add PTP clock support Kurt Kanzenbach
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-20  8:11 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean, 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 | 1170 ++++++++++++++++++++++++
 drivers/net/dsa/hirschmann/hellcreek.h |  244 +++++
 6 files changed, 1427 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

diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 468b3c4273c5..297dc27b92bc 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -58,6 +58,8 @@ source "drivers/net/dsa/qca/Kconfig"
 
 source "drivers/net/dsa/sja1105/Kconfig"
 
+source "drivers/net/dsa/hirschmann/Kconfig"
+
 config NET_DSA_QCA8K
 	tristate "Qualcomm Atheros QCA8K Ethernet switch family support"
 	depends on NET_DSA
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index 4a943ccc2ca4..a707ccc3a940 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -23,3 +23,4 @@ obj-y				+= mv88e6xxx/
 obj-y				+= ocelot/
 obj-y				+= qca/
 obj-y				+= sja1105/
+obj-y				+= hirschmann/
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..8759f7f1b645
--- /dev/null
+++ b/drivers/net/dsa/hirschmann/hellcreek.c
@@ -0,0 +1,1170 @@
+// 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/spinlock.h>
+#include <linux/delay.h>
+#include <linux/ktime.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_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;
+	unsigned long flags;
+	u16 val;
+
+	hellcreek_port = &hellcreek->ports[port];
+
+	dev_dbg(hellcreek->dev, "Enable port %d\n", port);
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	hellcreek_select_port(hellcreek, port);
+	val = hellcreek_port->ptcfg;
+	val |= HR_PTCFG_ADMIN_EN;
+	hellcreek_write(hellcreek, val, HR_PTCFG);
+	hellcreek_port->ptcfg = val;
+
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+	return 0;
+}
+
+static void hellcreek_port_disable(struct dsa_switch *ds, int port)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	struct hellcreek_port *hellcreek_port;
+	unsigned long flags;
+	u16 val;
+
+	hellcreek_port = &hellcreek->ports[port];
+
+	dev_dbg(hellcreek->dev, "Disable port %d\n", port);
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	hellcreek_select_port(hellcreek, port);
+	val = hellcreek_port->ptcfg;
+	val &= ~HR_PTCFG_ADMIN_EN;
+	hellcreek_write(hellcreek, val, HR_PTCFG);
+	hellcreek_port->ptcfg = val;
+
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+}
+
+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;
+	unsigned long flags;
+	int i;
+
+	hellcreek_port = &hellcreek->ports[port];
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+	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;
+
+		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];
+	}
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+}
+
+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_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;
+	unsigned long flags;
+	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");
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+		int shift, mask;
+		u16 val;
+
+		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;
+	}
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+}
+
+static int hellcreek_vlan_del(struct dsa_switch *ds, int port,
+			      const struct switchdev_obj_port_vlan *vlan)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	unsigned long flags;
+	u16 vid;
+
+	dev_dbg(hellcreek->dev, "Remove VLANs (%d -- %d) on port %d\n",
+		vlan->vid_begin, vlan->vid_end, port);
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
+		int shift, mask;
+		u16 val;
+
+		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;
+	}
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+	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;
+	unsigned long flags;
+	u16 val;
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	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;
+
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+	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;
+	u16 rx_vid = port;
+	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) {
+		const struct switchdev_obj_port_vlan vlan = {
+			.vid_begin = rx_vid,
+			.vid_end = rx_vid,
+			.flags = BRIDGE_VLAN_INFO_UNTAGGED,
+		};
+
+		if (!dsa_is_user_port(ds, i))
+			continue;
+
+		if (i == port)
+			continue;
+
+		hellcreek_vlan_add(ds, i, &vlan);
+	}
+
+	return 0;
+}
+
+static void hellcreek_port_bridge_leave(struct dsa_switch *ds, int port,
+					struct net_device *br)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	u16 rx_vid = port;
+	int i, err;
+
+	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) {
+		const struct switchdev_obj_port_vlan vlan = {
+			.vid_begin = rx_vid,
+			.vid_end = rx_vid,
+		};
+
+		if (!dsa_is_user_port(ds, i))
+			continue;
+
+		if (i == port)
+			continue;
+
+		err = hellcreek_vlan_del(ds, i, &vlan);
+		if (err) {
+			dev_err(hellcreek->dev, "Failed add vid %d to port %d\n",
+				rx_vid, i);
+			return;
+		}
+	}
+}
+
+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;
+	unsigned long flags;
+	int ret;
+
+	dev_dbg(hellcreek->dev, "Add FDB entry for MAC=%pM\n", addr);
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	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:
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+	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;
+	unsigned long flags;
+	int ret;
+
+	dev_dbg(hellcreek->dev, "Delete FDB entry for MAC=%pM\n", addr);
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	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:
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+	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;
+	unsigned long flags;
+	u16 entries;
+	size_t i;
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	/* 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);
+	}
+
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+	return 0;
+}
+
+/* Default setup for DSA:
+ *  VLAN 2: CPU and Port 1 egress untagged.
+ *  VLAN 3: CPU and Port 2 egress untagged.
+ */
+static int hellcreek_setup_vlan_membership(struct dsa_switch *ds, int port,
+					   bool enabled)
+{
+	int upstream = dsa_upstream_port(ds, port);
+	struct switchdev_obj_port_vlan vlan = {
+		.vid_begin = port,
+		.vid_end = port,
+	};
+	int err = 0;
+
+	/* 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 */
+	vlan.flags = BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_PVID;
+	if (enabled)
+		hellcreek_vlan_add(ds, port, &vlan);
+	else
+		err = hellcreek_vlan_del(ds, port, &vlan);
+	if (err) {
+		dev_err(ds->dev, "Failed to apply VID %d to port %d: %d\n",
+			port, port, err);
+		return err;
+	}
+
+	/* Apply vid to cpu port as well */
+	vlan.flags = BRIDGE_VLAN_INFO_UNTAGGED;
+	if (enabled)
+		hellcreek_vlan_add(ds, upstream, &vlan);
+	else
+		err = hellcreek_vlan_del(ds, upstream, &vlan);
+	if (err) {
+		dev_err(ds->dev, "Failed to apply VID %d to port %d: %d\n",
+			port, port, err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void hellcreek_setup_ingressflt(struct hellcreek *hellcreek, int port,
+				       bool enable)
+{
+	struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
+	unsigned long flags;
+	u16 ptcfg;
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	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;
+
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+}
+
+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);
+
+	return 0;
+}
+
+static int hellcreek_enable_ip_core(struct hellcreek *hellcreek)
+{
+	unsigned long flags;
+	int ret;
+	u16 val;
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	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);
+
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+	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];
+	unsigned long flags;
+	u16 ptcfg = 0;
+
+	ptcfg |= HR_PTCFG_LEARNING_EN | HR_PTCFG_ADMIN_EN;
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	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;
+
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+}
+
+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. A new mapping can be
+	 * configured by the user using tc.
+	 */
+
+	for (i = 0; i < 8; ++i) {
+		hellcreek_select_prio(hellcreek, i);
+		hellcreek_write(hellcreek,
+				i << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
+				HR_PRTCCFG);
+	}
+}
+
+static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+	__hellcreek_setup_tc_identity_mapping(hellcreek);
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+}
+
+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);
+
+	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. Currently only chips with 100Mbit/s full duplex
+	 * exist. So apply that mask.
+	 */
+	phylink_set(mask, 100baseT_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;
+
+	for (i = 0; i < 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->port	= i;
+	}
+
+	spin_lock_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     = 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 of_device_id hellcreek_of_match[] = {
+	{
+		.compatible = "hirschmann,hellcreek",
+	},
+	{ },
+};
+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..a08a10cb5ab7
--- /dev/null
+++ b/drivers/net/dsa/hirschmann/hellcreek.h
@@ -0,0 +1,244 @@
+/* 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/spinlock.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 NUM_PORTS			4
+
+#define HELLCREEK_MODULE_ID		0x4c30
+#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;
+	int port;
+	u16 ptcfg;		/* ptcfg shadow */
+	u64 *counter_values;
+};
+
+struct hellcreek_fdb_entry {
+	size_t idx;
+	unsigned char mac[6];
+	u8 portmask;
+	u8 age;
+	u8 is_obt;
+	u8 pass_blocked;
+	u8 is_static;
+	u8 reprio_tc;
+	u8 reprio_en;
+};
+
+struct hellcreek {
+	struct device *dev;
+	struct dsa_switch *ds;
+	struct hellcreek_port ports[4];
+	spinlock_t reg_lock;	/* Switch IP register lock */
+	void __iomem *base;
+	u8 *vidmbrcfg;		/* vidmbrcfg shadow */
+	size_t fdb_entries;
+};
+
+#endif /* _HELLCREEK_H_ */
-- 
2.20.1


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

* [PATCH v3 3/8] net: dsa: hellcreek: Add PTP clock support
  2020-08-20  8:11 [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
  2020-08-20  8:11 ` [PATCH v3 1/8] net: dsa: Add tag handling for Hirschmann Hellcreek switches Kurt Kanzenbach
  2020-08-20  8:11 ` [PATCH v3 2/8] net: dsa: Add DSA driver " Kurt Kanzenbach
@ 2020-08-20  8:11 ` Kurt Kanzenbach
  2020-08-20  8:11 ` [PATCH v3 4/8] net: dsa: hellcreek: Add support for hardware timestamping Kurt Kanzenbach
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-20  8:11 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean, 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     |  72 ++++++
 drivers/net/dsa/hirschmann/hellcreek.h     |   7 +
 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 8759f7f1b645..1ab2e7b27610 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.c
+++ b/drivers/net/dsa/hirschmann/hellcreek.c
@@ -25,6 +25,7 @@
 #include <net/dsa.h>
 
 #include "hellcreek.h"
+#include "hellcreek_ptp.h"
 
 static const struct hellcreek_counter hellcreek_counter[] = {
 	{ 0x00, "RxFiltered", },
@@ -965,6 +966,44 @@ static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
 	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
 }
 
+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,
+	};
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+	ret = __hellcreek_fdb_add(hellcreek, &ptp);
+	if (ret)
+		goto out;
+	ret = __hellcreek_fdb_add(hellcreek, &p2p);
+out:
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+	return ret;
+}
+
 static int hellcreek_setup(struct dsa_switch *ds)
 {
 	struct hellcreek *hellcreek = ds->priv;
@@ -1007,6 +1046,14 @@ static int hellcreek_setup(struct dsa_switch *ds)
 	/* Configure PCP <-> TC mapping */
 	hellcreek_setup_tc_identity_mapping(hellcreek);
 
+	/* 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;
 }
 
@@ -1087,6 +1134,7 @@ static int hellcreek_probe(struct platform_device *pdev)
 	}
 
 	spin_lock_init(&hellcreek->reg_lock);
+	spin_lock_init(&hellcreek->ptp_lock);
 
 	hellcreek->dev = dev;
 
@@ -1102,6 +1150,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");
@@ -1132,15 +1192,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 a08a10cb5ab7..2d4422fd2567 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.h
+++ b/drivers/net/dsa/hirschmann/hellcreek.h
@@ -234,10 +234,17 @@ struct hellcreek_fdb_entry {
 struct hellcreek {
 	struct device *dev;
 	struct dsa_switch *ds;
+	struct ptp_clock *ptp_clock;
+	struct ptp_clock_info ptp_clock_info;
 	struct hellcreek_port ports[4];
+	struct delayed_work overflow_work;
 	spinlock_t reg_lock;	/* Switch IP register lock */
+	spinlock_t 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..c606a26a130e
--- /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;
+
+	spin_lock(&hellcreek->ptp_lock);
+	ns = __hellcreek_ptp_gettime(hellcreek);
+	spin_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;
+
+	spin_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);
+
+	spin_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;
+
+	spin_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);
+
+	spin_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;
+
+	spin_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);
+
+	spin_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);
+
+	spin_lock(&hellcreek->ptp_lock);
+	__hellcreek_ptp_gettime(hellcreek);
+	spin_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	[flat|nested] 44+ messages in thread

* [PATCH v3 4/8] net: dsa: hellcreek: Add support for hardware timestamping
  2020-08-20  8:11 [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
                   ` (2 preceding siblings ...)
  2020-08-20  8:11 ` [PATCH v3 3/8] net: dsa: hellcreek: Add PTP clock support Kurt Kanzenbach
@ 2020-08-20  8:11 ` Kurt Kanzenbach
  2020-08-20  8:11 ` [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support Kurt Kanzenbach
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-20  8:11 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean, 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 1ab2e7b27610..745ca60342b4 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.c
+++ b/drivers/net/dsa/hirschmann/hellcreek.c
@@ -26,6 +26,7 @@
 
 #include "hellcreek.h"
 #include "hellcreek_ptp.h"
+#include "hellcreek_hwtstamp.h"
 
 static const struct hellcreek_counter hellcreek_counter[] = {
 	{ 0x00, "RxFiltered", },
@@ -1099,6 +1100,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)
@@ -1198,10 +1204,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);
 
@@ -1212,6 +1226,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 2d4422fd2567..1d3de72a48a5 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.h
+++ b/drivers/net/dsa/hirschmann/hellcreek.h
@@ -212,11 +212,36 @@ 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;
 	int port;
 	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..f3747a2072e4
--- /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;
+	}
+
+	spin_lock(&hellcreek->ptp_lock);
+	ns  = hellcreek_ptp_hwtstamp_read(hellcreek, data_reg);
+	ns += hellcreek_ptp_gettime_seconds(hellcreek, ns);
+	spin_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 */
+		spin_lock(&hellcreek->ptp_lock);
+		ns += hellcreek_ptp_gettime_seconds(hellcreek, ns);
+		spin_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 c606a26a130e..8c2cef2b60fb 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] 44+ messages in thread

* [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-20  8:11 [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
                   ` (3 preceding siblings ...)
  2020-08-20  8:11 ` [PATCH v3 4/8] net: dsa: hellcreek: Add support for hardware timestamping Kurt Kanzenbach
@ 2020-08-20  8:11 ` Kurt Kanzenbach
  2020-08-22 14:39   ` Vladimir Oltean
                     ` (2 more replies)
  2020-08-20  8:11 ` [PATCH v3 6/8] net: dsa: hellcreek: Add PTP status LEDs Kurt Kanzenbach
                   ` (3 subsequent siblings)
  8 siblings, 3 replies; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-20  8:11 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean, Kurt Kanzenbach

The switch has support for the 802.1Qbv Time Aware Shaper (TAS). Traffic
schedules may be configured individually on each front port. Each port has eight
egress queues. The traffic is mapped to a traffic class respectively via the PCP
field of a VLAN tagged frame.

The TAPRIO Qdisc already implements that. Therefore, this interface can simply
be reused. Add .port_setup_tc() accordingly.

The activation of a schedule on a port is split into two parts:

 * Programming the necessary gate control list (GCL)
 * Setup hrtimer for starting the schedule

The hardware supports starting a schedule up to eight seconds in the future. The
TAPRIO interface provides an absolute base time. Therefore, hrtimers are
leveraged.

Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
---
 drivers/net/dsa/hirschmann/hellcreek.c | 294 +++++++++++++++++++++++++
 drivers/net/dsa/hirschmann/hellcreek.h |  21 ++
 2 files changed, 315 insertions(+)

diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
index 745ca60342b4..e5b54f42c635 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.c
+++ b/drivers/net/dsa/hirschmann/hellcreek.c
@@ -22,7 +22,9 @@
 #include <linux/spinlock.h>
 #include <linux/delay.h>
 #include <linux/ktime.h>
+#include <linux/time.h>
 #include <net/dsa.h>
+#include <net/pkt_sched.h>
 
 #include "hellcreek.h"
 #include "hellcreek_ptp.h"
@@ -153,6 +155,15 @@ static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid,
 	hellcreek_write(hellcreek, val, HR_VIDCFG);
 }
 
+static void hellcreek_select_tgd(struct hellcreek *hellcreek, int port)
+{
+	u16 val = 0;
+
+	val |= port << TR_TGDSEL_TDGSEL_SHIFT;
+
+	hellcreek_write(hellcreek, val, TR_TGDSEL);
+}
+
 static int hellcreek_wait_until_ready(struct hellcreek *hellcreek)
 {
 	u16 val;
@@ -958,6 +969,24 @@ static void __hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
 	}
 }
 
+static void hellcreek_setup_tc_mapping(struct hellcreek *hellcreek,
+				       struct net_device *netdev)
+{
+	int i, j;
+
+	/* Setup mapping between traffic classes and port queues. */
+	for (i = 0; i < netdev_get_num_tc(netdev); ++i) {
+		for (j = 0; j < netdev->tc_to_txq[i].count; ++j) {
+			const int queue = j + netdev->tc_to_txq[i].offset;
+
+			hellcreek_select_prio(hellcreek, i);
+			hellcreek_write(hellcreek,
+					queue << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
+					HR_PRTCCFG);
+		}
+	}
+}
+
 static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
 {
 	unsigned long flags;
@@ -1081,6 +1110,267 @@ static void hellcreek_phylink_validate(struct dsa_switch *ds, int port,
 		   __ETHTOOL_LINK_MODE_MASK_NBITS);
 }
 
+static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port,
+				const struct hellcreek_schedule *schedule)
+{
+	size_t i;
+
+	for (i = 1; i <= schedule->num_entries; ++i) {
+		const struct hellcreek_gcl_entry *cur, *initial, *next;
+		u16 data;
+		u8 gates;
+
+		cur	= &schedule->entries[i - 1];
+		initial = &schedule->entries[0];
+		next	= &schedule->entries[i];
+
+		if (i == schedule->num_entries)
+			gates = initial->gate_states ^
+				cur->gate_states;
+		else
+			gates = next->gate_states ^
+				cur->gate_states;
+
+		data = gates;
+		if (cur->overrun_ignore)
+			data |= TR_GCLDAT_GCLOVRI;
+
+		if (i == schedule->num_entries)
+			data |= TR_GCLDAT_GCLWRLAST;
+
+		/* Gates states */
+		hellcreek_write(hellcreek, data, TR_GCLDAT);
+
+		/* Time intervall */
+		hellcreek_write(hellcreek,
+				cur->interval & 0x0000ffff,
+				TR_GCLTIL);
+		hellcreek_write(hellcreek,
+				(cur->interval & 0xffff0000) >> 16,
+				TR_GCLTIH);
+
+		/* Commit entry */
+		data = ((i - 1) << TR_GCLCMD_GCLWRADR_SHIFT) |
+			(initial->gate_states <<
+			 TR_GCLCMD_INIT_GATE_STATES_SHIFT);
+		hellcreek_write(hellcreek, data, TR_GCLCMD);
+	}
+}
+
+static void hellcreek_set_cycle_time(struct hellcreek *hellcreek,
+				     const struct hellcreek_schedule *schedule)
+{
+	u32 cycle_time = schedule->cycle_time;
+
+	hellcreek_write(hellcreek, cycle_time & 0x0000ffff, TR_CTWRL);
+	hellcreek_write(hellcreek, (cycle_time & 0xffff0000) >> 16, TR_CTWRH);
+}
+
+static void hellcreek_start_schedule(struct hellcreek *hellcreek,
+				     ktime_t start_time)
+{
+	struct timespec64 ts = ktime_to_timespec64(start_time);
+
+	/* Start can be only 8 seconds in the future */
+	ts.tv_sec %= 8;
+
+	/* Start schedule at this point of time */
+	hellcreek_write(hellcreek, ts.tv_nsec & 0x0000ffff, TR_ESTWRL);
+	hellcreek_write(hellcreek, (ts.tv_nsec & 0xffff0000) >> 16, TR_ESTWRH);
+
+	/* Arm timer, set seconds and switch schedule */
+	hellcreek_write(hellcreek, TR_ESTCMD_ESTARM | TR_ESTCMD_ESTSWCFG |
+		     ((ts.tv_sec & TR_ESTCMD_ESTSEC_MASK) <<
+		      TR_ESTCMD_ESTSEC_SHIFT), TR_ESTCMD);
+}
+
+static struct hellcreek_schedule *hellcreek_taprio_to_schedule(
+	const struct tc_taprio_qopt_offload *taprio)
+{
+	struct hellcreek_schedule *schedule;
+	size_t i;
+
+	/* Allocate some memory first */
+	schedule = kzalloc(sizeof(*schedule), GFP_KERNEL);
+	if (!schedule)
+		return ERR_PTR(-ENOMEM);
+	schedule->entries = kcalloc(taprio->num_entries,
+				    sizeof(*schedule->entries),
+				    GFP_KERNEL);
+	if (!schedule->entries) {
+		kfree(schedule);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	/* Construct hellcreek schedule */
+	schedule->num_entries = taprio->num_entries;
+	schedule->base_time   = taprio->base_time;
+
+	for (i = 0; i < taprio->num_entries; ++i) {
+		const struct tc_taprio_sched_entry *t = &taprio->entries[i];
+		struct hellcreek_gcl_entry *k = &schedule->entries[i];
+
+		k->interval	  = t->interval;
+		k->gate_states	  = t->gate_mask;
+		k->overrun_ignore = 0;
+
+		/* Update complete cycle time */
+		schedule->cycle_time += t->interval;
+	}
+
+	return schedule;
+}
+
+static enum hrtimer_restart hellcreek_set_schedule(struct hrtimer *timer)
+{
+	struct hellcreek_port *hellcreek_port =
+		hrtimer_to_hellcreek_port(timer);
+	struct hellcreek *hellcreek = hellcreek_port->hellcreek;
+	struct hellcreek_schedule *schedule;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	/* First select port */
+	hellcreek_select_tgd(hellcreek, hellcreek_port->port);
+
+	/* Set admin base time and switch schedule */
+	hellcreek_start_schedule(hellcreek,
+				 hellcreek_port->current_schedule->base_time);
+
+	schedule = hellcreek_port->current_schedule;
+	hellcreek_port->current_schedule = NULL;
+
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+	dev_dbg(hellcreek->dev, "ARMed EST timer for port %d\n",
+		hellcreek_port->port);
+
+	/* Free resources */
+	kfree(schedule->entries);
+	kfree(schedule);
+
+	return HRTIMER_NORESTART;
+}
+
+static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
+				       const struct tc_taprio_qopt_offload *taprio)
+{
+	struct net_device *netdev = dsa_to_port(ds, port)->slave;
+	struct hellcreek *hellcreek = ds->priv;
+	struct hellcreek_port *hellcreek_port;
+	struct hellcreek_schedule *schedule;
+	unsigned long flags;
+	ktime_t start;
+	u16 ctrl;
+
+	hellcreek_port = &hellcreek->ports[port];
+
+	/* Convert taprio data to hellcreek schedule */
+	schedule = hellcreek_taprio_to_schedule(taprio);
+	if (IS_ERR(schedule))
+		return PTR_ERR(schedule);
+
+	dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n",
+		port);
+
+	/* Cancel an in flight timer */
+	hrtimer_cancel(&hellcreek_port->cycle_start_timer);
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	if (hellcreek_port->current_schedule) {
+		kfree(hellcreek_port->current_schedule->entries);
+		kfree(hellcreek_port->current_schedule);
+	}
+
+	hellcreek_port->current_schedule = schedule;
+
+	/* First select port */
+	hellcreek_select_tgd(hellcreek, port);
+
+	/* Setup traffic class <-> queue mapping */
+	hellcreek_setup_tc_mapping(hellcreek, netdev);
+
+	/* Enable gating and set the admin state to forward everything in the
+	 * mean time
+	 */
+	ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN;
+	hellcreek_write(hellcreek, ctrl, TR_TGDCTRL);
+
+	/* Cancel pending schedule */
+	hellcreek_write(hellcreek, 0x00, TR_ESTCMD);
+
+	/* Setup a new schedule */
+	hellcreek_setup_gcl(hellcreek, port, schedule);
+
+	/* Configure cycle time */
+	hellcreek_set_cycle_time(hellcreek, schedule);
+
+	/* Setup timer for schedule switch: The IP core only allows to set a
+	 * cycle start timer 8 seconds in the future. This is why we setup the
+	 * hritmer to base_time - 5 seconds. Then, we have enough time to
+	 * activate IP core's EST timer.
+	 */
+	start = ktime_sub_ns(schedule->base_time, (u64)5 * NSEC_PER_SEC);
+	hrtimer_start_range_ns(&hellcreek_port->cycle_start_timer, start,
+			       NSEC_PER_SEC, HRTIMER_MODE_ABS);
+
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+	return 0;
+}
+
+static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port)
+{
+	struct hellcreek *hellcreek = ds->priv;
+	struct hellcreek_port *hellcreek_port;
+	unsigned long flags;
+
+	hellcreek_port = &hellcreek->ports[port];
+
+	dev_dbg(hellcreek->dev, "Remove traffic schedule on port %d\n", port);
+
+	/* First cancel timer */
+	hrtimer_cancel(&hellcreek_port->cycle_start_timer);
+
+	spin_lock_irqsave(&hellcreek->reg_lock, flags);
+
+	if (hellcreek_port->current_schedule) {
+		kfree(hellcreek_port->current_schedule->entries);
+		kfree(hellcreek_port->current_schedule);
+		hellcreek_port->current_schedule = NULL;
+	}
+
+	/* Then select port */
+	hellcreek_select_tgd(hellcreek, port);
+
+	/* Revert tc mapping */
+	__hellcreek_setup_tc_identity_mapping(hellcreek);
+
+	/* Disable gating and return to regular switching flow */
+	hellcreek_write(hellcreek, 0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT,
+			TR_TGDCTRL);
+
+	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
+
+	return 0;
+}
+
+static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port,
+				   enum tc_setup_type type, void *type_data)
+{
+	const struct tc_taprio_qopt_offload *taprio = type_data;
+
+	if (type != TC_SETUP_QDISC_TAPRIO)
+		return -EOPNOTSUPP;
+
+	if (taprio->enable)
+		return hellcreek_port_set_schedule(ds, port, taprio);
+
+	return hellcreek_port_del_schedule(ds, port);
+}
+
 static const struct dsa_switch_ops hellcreek_ds_ops = {
 	.get_tag_protocol    = hellcreek_get_tag_protocol,
 	.setup		     = hellcreek_setup,
@@ -1104,6 +1394,7 @@ static const struct dsa_switch_ops hellcreek_ds_ops = {
 	.port_hwtstamp_get   = hellcreek_port_hwtstamp_get,
 	.port_txtstamp	     = hellcreek_port_txtstamp,
 	.port_rxtstamp	     = hellcreek_port_rxtstamp,
+	.port_setup_tc	     = hellcreek_port_setup_tc,
 	.get_ts_info	     = hellcreek_get_ts_info,
 };
 
@@ -1135,6 +1426,9 @@ static int hellcreek_probe(struct platform_device *pdev)
 		if (!port->counter_values)
 			return -ENOMEM;
 
+		hrtimer_init(&port->cycle_start_timer, CLOCK_TAI,
+			     HRTIMER_MODE_ABS);
+		port->cycle_start_timer.function = hellcreek_set_schedule;
 		port->hellcreek = hellcreek;
 		port->port	= i;
 	}
diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h
index 1d3de72a48a5..d3d1a1144857 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/spinlock.h>
+#include <linux/hrtimer.h>
 #include <net/dsa.h>
 
 /* Ports:
@@ -210,6 +211,20 @@ struct hellcreek_counter {
 	const char *name;
 };
 
+struct hellcreek_gcl_entry {
+	u32 interval;
+	u8 gate_states;
+	bool overrun_ignore;
+};
+
+struct hellcreek_schedule {
+	struct hellcreek_gcl_entry *entries;
+	size_t num_entries;
+	ktime_t base_time;
+	u32 cycle_time;
+	int port;
+};
+
 struct hellcreek;
 
 /* State flags for hellcreek_port_hwtstamp::state */
@@ -236,6 +251,8 @@ struct hellcreek_port_hwtstamp {
 
 struct hellcreek_port {
 	struct hellcreek *hellcreek;
+	struct hellcreek_schedule *current_schedule;
+	struct hrtimer cycle_start_timer;
 	int port;
 	u16 ptcfg;		/* ptcfg shadow */
 	u64 *counter_values;
@@ -273,4 +290,8 @@ struct hellcreek {
 	size_t fdb_entries;
 };
 
+#define hrtimer_to_hellcreek_port(timer)		\
+	container_of(timer, struct hellcreek_port,	\
+		     cycle_start_timer)
+
 #endif /* _HELLCREEK_H_ */
-- 
2.20.1


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

* [PATCH v3 6/8] net: dsa: hellcreek: Add PTP status LEDs
  2020-08-20  8:11 [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
                   ` (4 preceding siblings ...)
  2020-08-20  8:11 ` [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support Kurt Kanzenbach
@ 2020-08-20  8:11 ` Kurt Kanzenbach
  2020-08-24 22:50   ` Andrew Lunn
  2020-08-20  8:11 ` [PATCH v3 7/8] dt-bindings: Add vendor prefix for Hirschmann Kurt Kanzenbach
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-20  8:11 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean, 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>
---
 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 d3d1a1144857..c83af1af8d8a 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/spinlock.h>
 #include <linux/hrtimer.h>
+#include <linux/leds.h>
 #include <net/dsa.h>
 
 /* Ports:
@@ -280,6 +281,8 @@ struct hellcreek {
 	struct ptp_clock_info ptp_clock_info;
 	struct hellcreek_port ports[4];
 	struct delayed_work overflow_work;
+	struct led_classdev led_is_gm;
+	struct led_classdev led_sync_good;
 	spinlock_t reg_lock;	/* Switch IP register lock */
 	spinlock_t ptp_lock;	/* PTP IP register lock */
 	void __iomem *base;
@@ -287,6 +290,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 8c2cef2b60fb..579a38d8ad15 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)
+{
+	spin_lock(&hellcreek->ptp_lock);
+
+	if (b)
+		hellcreek->status_out |= led;
+	else
+		hellcreek->status_out &= ~led;
+
+	hellcreek_ptp_write(hellcreek, hellcreek->status_out, STATUS_OUT);
+
+	spin_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	[flat|nested] 44+ messages in thread

* [PATCH v3 7/8] dt-bindings: Add vendor prefix for Hirschmann
  2020-08-20  8:11 [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
                   ` (5 preceding siblings ...)
  2020-08-20  8:11 ` [PATCH v3 6/8] net: dsa: hellcreek: Add PTP status LEDs Kurt Kanzenbach
@ 2020-08-20  8:11 ` Kurt Kanzenbach
  2020-08-20  8:11 ` [PATCH v3 8/8] dt-bindings: net: dsa: Add documentation for Hellcreek switches Kurt Kanzenbach
  2020-08-24 21:31 ` [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Jakub Kicinski
  8 siblings, 0 replies; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-20  8:11 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean, 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 2baee2c817c1..3ab8162d80c5 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	[flat|nested] 44+ messages in thread

* [PATCH v3 8/8] dt-bindings: net: dsa: Add documentation for Hellcreek switches
  2020-08-20  8:11 [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
                   ` (6 preceding siblings ...)
  2020-08-20  8:11 ` [PATCH v3 7/8] dt-bindings: Add vendor prefix for Hirschmann Kurt Kanzenbach
@ 2020-08-20  8:11 ` Kurt Kanzenbach
  2020-08-24 22:52   ` Andrew Lunn
  2020-08-25 22:28   ` Rob Herring
  2020-08-24 21:31 ` [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Jakub Kicinski
  8 siblings, 2 replies; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-20  8:11 UTC (permalink / raw)
  To: Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean, Kurt Kanzenbach

Add basic documentation and example.

Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
---
 .../bindings/net/dsa/hellcreek.yaml           | 125 ++++++++++++++++++
 1 file changed, 125 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..412f2e573540
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/hellcreek.yaml
@@ -0,0 +1,125 @@
+# 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
+
+  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@[0-9]+$":
+          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
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - ethernet-ports
+  - leds
+
+unevaluatedProperties: false
+
+examples:
+  - |
+        switch0: switch@ff240000 {
+            compatible = "hirschmann,hellcreek";
+            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	[flat|nested] 44+ messages in thread

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-20  8:11 ` [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support Kurt Kanzenbach
@ 2020-08-22 14:39   ` Vladimir Oltean
  2020-08-24  6:10     ` Kurt Kanzenbach
  2020-08-24 22:56   ` Vladimir Oltean
  2020-08-24 23:57   ` Vinicius Costa Gomes
  2 siblings, 1 reply; 44+ messages in thread
From: Vladimir Oltean @ 2020-08-22 14:39 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

Hi Kurt,

On Thu, Aug 20, 2020 at 10:11:15AM +0200, Kurt Kanzenbach wrote:
> The switch has support for the 802.1Qbv Time Aware Shaper (TAS). Traffic
> schedules may be configured individually on each front port. Each port has eight
> egress queues. The traffic is mapped to a traffic class respectively via the PCP
> field of a VLAN tagged frame.
> 
> The TAPRIO Qdisc already implements that. Therefore, this interface can simply
> be reused. Add .port_setup_tc() accordingly.
> 
> The activation of a schedule on a port is split into two parts:
> 
>  * Programming the necessary gate control list (GCL)
>  * Setup hrtimer for starting the schedule
> 
> The hardware supports starting a schedule up to eight seconds in the future. The
> TAPRIO interface provides an absolute base time. Therefore, hrtimers are
> leveraged.
> 
> Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
> ---
>  drivers/net/dsa/hirschmann/hellcreek.c | 294 +++++++++++++++++++++++++
>  drivers/net/dsa/hirschmann/hellcreek.h |  21 ++
>  2 files changed, 315 insertions(+)
> 
> diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
> index 745ca60342b4..e5b54f42c635 100644
> --- a/drivers/net/dsa/hirschmann/hellcreek.c
> +++ b/drivers/net/dsa/hirschmann/hellcreek.c
> @@ -22,7 +22,9 @@
>  #include <linux/spinlock.h>
>  #include <linux/delay.h>
>  #include <linux/ktime.h>
> +#include <linux/time.h>
>  #include <net/dsa.h>
> +#include <net/pkt_sched.h>
>  
>  #include "hellcreek.h"
>  #include "hellcreek_ptp.h"
> @@ -153,6 +155,15 @@ static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid,
>  	hellcreek_write(hellcreek, val, HR_VIDCFG);
>  }
>  
> +static void hellcreek_select_tgd(struct hellcreek *hellcreek, int port)
> +{
> +	u16 val = 0;
> +
> +	val |= port << TR_TGDSEL_TDGSEL_SHIFT;
> +
> +	hellcreek_write(hellcreek, val, TR_TGDSEL);
> +}
> +
>  static int hellcreek_wait_until_ready(struct hellcreek *hellcreek)
>  {
>  	u16 val;
> @@ -958,6 +969,24 @@ static void __hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
>  	}
>  }
>  
> +static void hellcreek_setup_tc_mapping(struct hellcreek *hellcreek,
> +				       struct net_device *netdev)
> +{
> +	int i, j;
> +
> +	/* Setup mapping between traffic classes and port queues. */
> +	for (i = 0; i < netdev_get_num_tc(netdev); ++i) {
> +		for (j = 0; j < netdev->tc_to_txq[i].count; ++j) {
> +			const int queue = j + netdev->tc_to_txq[i].offset;
> +
> +			hellcreek_select_prio(hellcreek, i);
> +			hellcreek_write(hellcreek,
> +					queue << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
> +					HR_PRTCCFG);
> +		}
> +	}
> +}

What other driver have you seen that does this?

> +
> +static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
> +				       const struct tc_taprio_qopt_offload *taprio)
> +{
> +	struct net_device *netdev = dsa_to_port(ds, port)->slave;
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port *hellcreek_port;
> +	struct hellcreek_schedule *schedule;
> +	unsigned long flags;
> +	ktime_t start;
> +	u16 ctrl;
> +
> +	hellcreek_port = &hellcreek->ports[port];
> +
> +	/* Convert taprio data to hellcreek schedule */
> +	schedule = hellcreek_taprio_to_schedule(taprio);
> +	if (IS_ERR(schedule))
> +		return PTR_ERR(schedule);
> +
> +	dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n",
> +		port);
> +
> +	/* Cancel an in flight timer */
> +	hrtimer_cancel(&hellcreek_port->cycle_start_timer);
> +
> +	spin_lock_irqsave(&hellcreek->reg_lock, flags);
> +
> +	if (hellcreek_port->current_schedule) {
> +		kfree(hellcreek_port->current_schedule->entries);
> +		kfree(hellcreek_port->current_schedule);
> +	}
> +
> +	hellcreek_port->current_schedule = schedule;
> +
> +	/* First select port */
> +	hellcreek_select_tgd(hellcreek, port);
> +
> +	/* Setup traffic class <-> queue mapping */
> +	hellcreek_setup_tc_mapping(hellcreek, netdev);
> +
> +	/* Enable gating and set the admin state to forward everything in the
> +	 * mean time
> +	 */
> +	ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN;
> +	hellcreek_write(hellcreek, ctrl, TR_TGDCTRL);
> +
> +	/* Cancel pending schedule */
> +	hellcreek_write(hellcreek, 0x00, TR_ESTCMD);
> +
> +	/* Setup a new schedule */
> +	hellcreek_setup_gcl(hellcreek, port, schedule);
> +
> +	/* Configure cycle time */
> +	hellcreek_set_cycle_time(hellcreek, schedule);
> +
> +	/* Setup timer for schedule switch: The IP core only allows to set a
> +	 * cycle start timer 8 seconds in the future. This is why we setup the
> +	 * hritmer to base_time - 5 seconds. Then, we have enough time to
> +	 * activate IP core's EST timer.
> +	 */
> +	start = ktime_sub_ns(schedule->base_time, (u64)5 * NSEC_PER_SEC);
> +	hrtimer_start_range_ns(&hellcreek_port->cycle_start_timer, start,
> +			       NSEC_PER_SEC, HRTIMER_MODE_ABS);
> +
> +	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
> +
> +	return 0;
> +}

Thanks,
-Vladimir

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-22 14:39   ` Vladimir Oltean
@ 2020-08-24  6:10     ` Kurt Kanzenbach
  2020-08-24 23:45       ` Vinicius Costa Gomes
  0 siblings, 1 reply; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-24  6:10 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: 3076 bytes --]

On Sat Aug 22 2020, Vladimir Oltean wrote:
> Hi Kurt,
>
> On Thu, Aug 20, 2020 at 10:11:15AM +0200, Kurt Kanzenbach wrote:
>> The switch has support for the 802.1Qbv Time Aware Shaper (TAS). Traffic
>> schedules may be configured individually on each front port. Each port has eight
>> egress queues. The traffic is mapped to a traffic class respectively via the PCP
>> field of a VLAN tagged frame.
>> 
>> The TAPRIO Qdisc already implements that. Therefore, this interface can simply
>> be reused. Add .port_setup_tc() accordingly.
>> 
>> The activation of a schedule on a port is split into two parts:
>> 
>>  * Programming the necessary gate control list (GCL)
>>  * Setup hrtimer for starting the schedule
>> 
>> The hardware supports starting a schedule up to eight seconds in the future. The
>> TAPRIO interface provides an absolute base time. Therefore, hrtimers are
>> leveraged.
>> 
>> Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
>> ---
>>  drivers/net/dsa/hirschmann/hellcreek.c | 294 +++++++++++++++++++++++++
>>  drivers/net/dsa/hirschmann/hellcreek.h |  21 ++
>>  2 files changed, 315 insertions(+)
>> 
>> diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
>> index 745ca60342b4..e5b54f42c635 100644
>> --- a/drivers/net/dsa/hirschmann/hellcreek.c
>> +++ b/drivers/net/dsa/hirschmann/hellcreek.c
>> @@ -22,7 +22,9 @@
>>  #include <linux/spinlock.h>
>>  #include <linux/delay.h>
>>  #include <linux/ktime.h>
>> +#include <linux/time.h>
>>  #include <net/dsa.h>
>> +#include <net/pkt_sched.h>
>>  
>>  #include "hellcreek.h"
>>  #include "hellcreek_ptp.h"
>> @@ -153,6 +155,15 @@ static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid,
>>  	hellcreek_write(hellcreek, val, HR_VIDCFG);
>>  }
>>  
>> +static void hellcreek_select_tgd(struct hellcreek *hellcreek, int port)
>> +{
>> +	u16 val = 0;
>> +
>> +	val |= port << TR_TGDSEL_TDGSEL_SHIFT;
>> +
>> +	hellcreek_write(hellcreek, val, TR_TGDSEL);
>> +}
>> +
>>  static int hellcreek_wait_until_ready(struct hellcreek *hellcreek)
>>  {
>>  	u16 val;
>> @@ -958,6 +969,24 @@ static void __hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
>>  	}
>>  }
>>  
>> +static void hellcreek_setup_tc_mapping(struct hellcreek *hellcreek,
>> +				       struct net_device *netdev)
>> +{
>> +	int i, j;
>> +
>> +	/* Setup mapping between traffic classes and port queues. */
>> +	for (i = 0; i < netdev_get_num_tc(netdev); ++i) {
>> +		for (j = 0; j < netdev->tc_to_txq[i].count; ++j) {
>> +			const int queue = j + netdev->tc_to_txq[i].offset;
>> +
>> +			hellcreek_select_prio(hellcreek, i);
>> +			hellcreek_write(hellcreek,
>> +					queue << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
>> +					HR_PRTCCFG);
>> +		}
>> +	}
>> +}
>
> What other driver have you seen that does this?
>

Probably none.

With TAPRIO traffic classes and the mapping to queues can be
configured. The switch can also map traffic classes. That sounded like a
good match to me.

Thanks,
Kurt

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

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

* Re: [PATCH v3 0/8] Hirschmann Hellcreek DSA driver
  2020-08-20  8:11 [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
                   ` (7 preceding siblings ...)
  2020-08-20  8:11 ` [PATCH v3 8/8] dt-bindings: net: dsa: Add documentation for Hellcreek switches Kurt Kanzenbach
@ 2020-08-24 21:31 ` Jakub Kicinski
  2020-08-24 22:02   ` Vladimir Oltean
  8 siblings, 1 reply; 44+ messages in thread
From: Jakub Kicinski @ 2020-08-24 21:31 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	netdev, Rob Herring, devicetree, Sebastian Andrzej Siewior,
	Richard Cochran, Kamil Alkhouri, ilias.apalodimas,
	Vladimir Oltean, Ivan Khoronzhuk, Vinicius Costa Gomes,
	Xiaoliang Yang, Po Liu

On Thu, 20 Aug 2020 10:11:10 +0200 Kurt Kanzenbach wrote:
> 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

I don't see anything worth complaining about here, but this is not my
area of expertise.. 

DSA and TAPRIO folks - does this look good to you?

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

* Re: [PATCH v3 0/8] Hirschmann Hellcreek DSA driver
  2020-08-24 21:31 ` [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Jakub Kicinski
@ 2020-08-24 22:02   ` Vladimir Oltean
  2020-08-24 22:35     ` David Miller
  0 siblings, 1 reply; 44+ messages in thread
From: Vladimir Oltean @ 2020-08-24 22:02 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kurt Kanzenbach, Andrew Lunn, Vivien Didelot, Florian Fainelli,
	David S. Miller, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Ivan Khoronzhuk, Vinicius Costa Gomes,
	Xiaoliang Yang, Po Liu

On Mon, Aug 24, 2020 at 02:31:10PM -0700, Jakub Kicinski wrote:
> On Thu, 20 Aug 2020 10:11:10 +0200 Kurt Kanzenbach wrote:
> > 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
> 
> I don't see anything worth complaining about here, but this is not my
> area of expertise.. 
> 
> DSA and TAPRIO folks - does this look good to you?

Just my comment on patch 5/8 about netdev->tc_to_txq. There are 2
distinct things about that:
- accessing struct net_device directly hurts the DSA model a little bit.
- I think there's some confusion regarding the use of netdev->tc_to_txq
  itself. I don't think that's the right place to setup a VLAN PCP to
  traffic class mapping. That's simply "what traffic class does each
  netdev queue have". I would even go as far as say that Linux doesn't
  support a VLAN PCP to TC mapping (similar to the DSCP to TC mapping
  from the DCB ops) at all, except for the ingress-qos-map and
  egress-qos-map of the 8021q driver, which can't be offloaded and don't
  map nicely over existing hardware anyway (what hardware has an
  ingress-qos-map and an egress-qos-map per individual VLAN?!).
  Although I do really see the need for having a mapping between VLAN
  PCP and traffic class, I would suggest Kurt to not expose this through
  taprio/mqprio (hardcode the PCP mapping as 1-to-1 with TC, as other
  drivers do), and let's try to come up separately with an abstraction
  for that.

Thanks,
-Vladimir

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

* Re: [PATCH v3 0/8] Hirschmann Hellcreek DSA driver
  2020-08-24 22:02   ` Vladimir Oltean
@ 2020-08-24 22:35     ` David Miller
  2020-08-24 22:57       ` Vladimir Oltean
  2020-08-25 11:21       ` Kurt Kanzenbach
  0 siblings, 2 replies; 44+ messages in thread
From: David Miller @ 2020-08-24 22:35 UTC (permalink / raw)
  To: olteanv
  Cc: kuba, kurt, andrew, vivien.didelot, f.fainelli, netdev, robh+dt,
	devicetree, bigeasy, richardcochran, kamil.alkhouri,
	ilias.apalodimas, ivan.khoronzhuk, vinicius.gomes,
	xiaoliang.yang_1, Po.Liu

From: Vladimir Oltean <olteanv@gmail.com>
Date: Tue, 25 Aug 2020 01:02:03 +0300

> Just my comment on patch 5/8 about netdev->tc_to_txq. There are 2
> distinct things about that:
> - accessing struct net_device directly hurts the DSA model a little bit.
> - I think there's some confusion regarding the use of netdev->tc_to_txq
>   itself. I don't think that's the right place to setup a VLAN PCP to
>   traffic class mapping. That's simply "what traffic class does each
>   netdev queue have". I would even go as far as say that Linux doesn't
>   support a VLAN PCP to TC mapping (similar to the DSCP to TC mapping
>   from the DCB ops) at all, except for the ingress-qos-map and
>   egress-qos-map of the 8021q driver, which can't be offloaded and don't
>   map nicely over existing hardware anyway (what hardware has an
>   ingress-qos-map and an egress-qos-map per individual VLAN?!).
>   Although I do really see the need for having a mapping between VLAN
>   PCP and traffic class, I would suggest Kurt to not expose this through
>   taprio/mqprio (hardcode the PCP mapping as 1-to-1 with TC, as other
>   drivers do), and let's try to come up separately with an abstraction
>   for that.

Agreed, Kurt can you repost this series without the TAPRIO support for
now since it's controversial and needs more discussion and changes?

Thank you.

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

* Re: [PATCH v3 2/8] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-08-20  8:11 ` [PATCH v3 2/8] net: dsa: Add DSA driver " Kurt Kanzenbach
@ 2020-08-24 22:44   ` Andrew Lunn
  2020-08-25  9:07     ` Kurt Kanzenbach
  0 siblings, 1 reply; 44+ messages in thread
From: Andrew Lunn @ 2020-08-24 22:44 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean

On Thu, Aug 20, 2020 at 10:11:12AM +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 | 1170 ++++++++++++++++++++++++
>  drivers/net/dsa/hirschmann/hellcreek.h |  244 +++++
>  6 files changed, 1427 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
> 
> diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
> index 468b3c4273c5..297dc27b92bc 100644
> --- a/drivers/net/dsa/Kconfig
> +++ b/drivers/net/dsa/Kconfig
> @@ -58,6 +58,8 @@ source "drivers/net/dsa/qca/Kconfig"
>  
>  source "drivers/net/dsa/sja1105/Kconfig"
>  
> +source "drivers/net/dsa/hirschmann/Kconfig"
> +
>  config NET_DSA_QCA8K
>  	tristate "Qualcomm Atheros QCA8K Ethernet switch family support"
>  	depends on NET_DSA

Hi Kurt

The DSA entries are sorted into alphabetic order based on what you see
in make menuconfig. As such, "Hirschmann Hellcreek TSN Switch support"
fits in between "DSA mock-up Ethernet switch chip support" and "Lantiq / Intel GSWIP"

> diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
> index 4a943ccc2ca4..a707ccc3a940 100644
> --- a/drivers/net/dsa/Makefile
> +++ b/drivers/net/dsa/Makefile
> @@ -23,3 +23,4 @@ obj-y				+= mv88e6xxx/
>  obj-y				+= ocelot/
>  obj-y				+= qca/
>  obj-y				+= sja1105/
> +obj-y				+= hirschmann/

This file is also sorted. 

> +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_MODULE_ID)
> +		return -ENODEV;

Are there other Hellcreek devices? I'm just wondering if we should
have a specific compatible for 0x4c30 as well as the more generic 
"hirschmann,hellcreek".

> +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;
> +	unsigned long flags;
> +	int i;
> +
> +	hellcreek_port = &hellcreek->ports[port];
> +
> +	spin_lock_irqsave(&hellcreek->reg_lock, flags);
> +	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;
> +
> +		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.
> +		 */

Is there any locking/snapshot of all the counters at once? Most
devices have support for that, so you can compare counters against
each other.

> +		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];
> +	}
> +	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
> +}
> +

  Andrew

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

* Re: [PATCH v3 6/8] net: dsa: hellcreek: Add PTP status LEDs
  2020-08-20  8:11 ` [PATCH v3 6/8] net: dsa: hellcreek: Add PTP status LEDs Kurt Kanzenbach
@ 2020-08-24 22:50   ` Andrew Lunn
  0 siblings, 0 replies; 44+ messages in thread
From: Andrew Lunn @ 2020-08-24 22:50 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean

On Thu, Aug 20, 2020 at 10:11:16AM +0200, Kurt Kanzenbach wrote:
> 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>

    Andrew

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

* Re: [PATCH v3 8/8] dt-bindings: net: dsa: Add documentation for Hellcreek switches
  2020-08-20  8:11 ` [PATCH v3 8/8] dt-bindings: net: dsa: Add documentation for Hellcreek switches Kurt Kanzenbach
@ 2020-08-24 22:52   ` Andrew Lunn
  2020-08-25 22:28   ` Rob Herring
  1 sibling, 0 replies; 44+ messages in thread
From: Andrew Lunn @ 2020-08-24 22:52 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean

On Thu, Aug 20, 2020 at 10:11:18AM +0200, Kurt Kanzenbach wrote:
> Add basic documentation and example.
> 
> Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-20  8:11 ` [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support Kurt Kanzenbach
  2020-08-22 14:39   ` Vladimir Oltean
@ 2020-08-24 22:56   ` Vladimir Oltean
  2020-08-25  9:33     ` Kurt Kanzenbach
  2020-08-24 23:57   ` Vinicius Costa Gomes
  2 siblings, 1 reply; 44+ messages in thread
From: Vladimir Oltean @ 2020-08-24 22: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 Thu, Aug 20, 2020 at 10:11:15AM +0200, Kurt Kanzenbach wrote:
> The switch has support for the 802.1Qbv Time Aware Shaper (TAS). Traffic
> schedules may be configured individually on each front port. Each port has eight
> egress queues. The traffic is mapped to a traffic class respectively via the PCP
> field of a VLAN tagged frame.
> 
> The TAPRIO Qdisc already implements that. Therefore, this interface can simply
> be reused. Add .port_setup_tc() accordingly.
> 
> The activation of a schedule on a port is split into two parts:
> 
>  * Programming the necessary gate control list (GCL)
>  * Setup hrtimer for starting the schedule
> 
> The hardware supports starting a schedule up to eight seconds in the future. The
> TAPRIO interface provides an absolute base time. Therefore, hrtimers are
> leveraged.
> 
> Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
> ---
>  drivers/net/dsa/hirschmann/hellcreek.c | 294 +++++++++++++++++++++++++
>  drivers/net/dsa/hirschmann/hellcreek.h |  21 ++
>  2 files changed, 315 insertions(+)
> 
> diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
> index 745ca60342b4..e5b54f42c635 100644
> --- a/drivers/net/dsa/hirschmann/hellcreek.c
> +++ b/drivers/net/dsa/hirschmann/hellcreek.c
> @@ -22,7 +22,9 @@
>  #include <linux/spinlock.h>
>  #include <linux/delay.h>
>  #include <linux/ktime.h>
> +#include <linux/time.h>
>  #include <net/dsa.h>
> +#include <net/pkt_sched.h>
>  
>  #include "hellcreek.h"
>  #include "hellcreek_ptp.h"
> @@ -153,6 +155,15 @@ static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid,
>  	hellcreek_write(hellcreek, val, HR_VIDCFG);
>  }
>  
> +static void hellcreek_select_tgd(struct hellcreek *hellcreek, int port)
> +{
> +	u16 val = 0;
> +
> +	val |= port << TR_TGDSEL_TDGSEL_SHIFT;
> +
> +	hellcreek_write(hellcreek, val, TR_TGDSEL);
> +}
> +
>  static int hellcreek_wait_until_ready(struct hellcreek *hellcreek)
>  {
>  	u16 val;
> @@ -958,6 +969,24 @@ static void __hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
>  	}
>  }
>  
> +static void hellcreek_setup_tc_mapping(struct hellcreek *hellcreek,
> +				       struct net_device *netdev)
> +{
> +	int i, j;
> +
> +	/* Setup mapping between traffic classes and port queues. */
> +	for (i = 0; i < netdev_get_num_tc(netdev); ++i) {
> +		for (j = 0; j < netdev->tc_to_txq[i].count; ++j) {
> +			const int queue = j + netdev->tc_to_txq[i].offset;
> +
> +			hellcreek_select_prio(hellcreek, i);
> +			hellcreek_write(hellcreek,
> +					queue << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
> +					HR_PRTCCFG);
> +		}
> +	}
> +}
> +
>  static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
>  {
>  	unsigned long flags;
> @@ -1081,6 +1110,267 @@ static void hellcreek_phylink_validate(struct dsa_switch *ds, int port,
>  		   __ETHTOOL_LINK_MODE_MASK_NBITS);
>  }
>  
> +static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port,
> +				const struct hellcreek_schedule *schedule)
> +{
> +	size_t i;
> +
> +	for (i = 1; i <= schedule->num_entries; ++i) {
> +		const struct hellcreek_gcl_entry *cur, *initial, *next;
> +		u16 data;
> +		u8 gates;
> +
> +		cur	= &schedule->entries[i - 1];
> +		initial = &schedule->entries[0];
> +		next	= &schedule->entries[i];
> +
> +		if (i == schedule->num_entries)
> +			gates = initial->gate_states ^
> +				cur->gate_states;
> +		else
> +			gates = next->gate_states ^
> +				cur->gate_states;
> +
> +		data = gates;
> +		if (cur->overrun_ignore)
> +			data |= TR_GCLDAT_GCLOVRI;
> +
> +		if (i == schedule->num_entries)
> +			data |= TR_GCLDAT_GCLWRLAST;
> +
> +		/* Gates states */
> +		hellcreek_write(hellcreek, data, TR_GCLDAT);
> +
> +		/* Time intervall */
> +		hellcreek_write(hellcreek,
> +				cur->interval & 0x0000ffff,
> +				TR_GCLTIL);
> +		hellcreek_write(hellcreek,
> +				(cur->interval & 0xffff0000) >> 16,
> +				TR_GCLTIH);
> +
> +		/* Commit entry */
> +		data = ((i - 1) << TR_GCLCMD_GCLWRADR_SHIFT) |
> +			(initial->gate_states <<
> +			 TR_GCLCMD_INIT_GATE_STATES_SHIFT);
> +		hellcreek_write(hellcreek, data, TR_GCLCMD);
> +	}
> +}
> +
> +static void hellcreek_set_cycle_time(struct hellcreek *hellcreek,
> +				     const struct hellcreek_schedule *schedule)
> +{
> +	u32 cycle_time = schedule->cycle_time;
> +
> +	hellcreek_write(hellcreek, cycle_time & 0x0000ffff, TR_CTWRL);
> +	hellcreek_write(hellcreek, (cycle_time & 0xffff0000) >> 16, TR_CTWRH);
> +}
> +
> +static void hellcreek_start_schedule(struct hellcreek *hellcreek,
> +				     ktime_t start_time)
> +{
> +	struct timespec64 ts = ktime_to_timespec64(start_time);
> +
> +	/* Start can be only 8 seconds in the future */
> +	ts.tv_sec %= 8;
> +
> +	/* Start schedule at this point of time */
> +	hellcreek_write(hellcreek, ts.tv_nsec & 0x0000ffff, TR_ESTWRL);
> +	hellcreek_write(hellcreek, (ts.tv_nsec & 0xffff0000) >> 16, TR_ESTWRH);
> +
> +	/* Arm timer, set seconds and switch schedule */
> +	hellcreek_write(hellcreek, TR_ESTCMD_ESTARM | TR_ESTCMD_ESTSWCFG |
> +		     ((ts.tv_sec & TR_ESTCMD_ESTSEC_MASK) <<
> +		      TR_ESTCMD_ESTSEC_SHIFT), TR_ESTCMD);
> +}
> +
> +static struct hellcreek_schedule *hellcreek_taprio_to_schedule(
> +	const struct tc_taprio_qopt_offload *taprio)

Personal indentation preference:

static struct hellcreek_schedule
*hellcreek_taprio_to_schedule(const struct tc_taprio_qopt_offload *taprio)

> +{
> +	struct hellcreek_schedule *schedule;
> +	size_t i;
> +
> +	/* Allocate some memory first */
> +	schedule = kzalloc(sizeof(*schedule), GFP_KERNEL);
> +	if (!schedule)
> +		return ERR_PTR(-ENOMEM);
> +	schedule->entries = kcalloc(taprio->num_entries,
> +				    sizeof(*schedule->entries),
> +				    GFP_KERNEL);
> +	if (!schedule->entries) {
> +		kfree(schedule);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	/* Construct hellcreek schedule */
> +	schedule->num_entries = taprio->num_entries;
> +	schedule->base_time   = taprio->base_time;
> +
> +	for (i = 0; i < taprio->num_entries; ++i) {
> +		const struct tc_taprio_sched_entry *t = &taprio->entries[i];
> +		struct hellcreek_gcl_entry *k = &schedule->entries[i];
> +
> +		k->interval	  = t->interval;
> +		k->gate_states	  = t->gate_mask;
> +		k->overrun_ignore = 0;

Tab to align with gate_states and interval?
What does overrun_ignore do, anyway?

> +
> +		/* Update complete cycle time */
> +		schedule->cycle_time += t->interval;
> +	}
> +
> +	return schedule;
> +}
> +
> +static enum hrtimer_restart hellcreek_set_schedule(struct hrtimer *timer)
> +{
> +	struct hellcreek_port *hellcreek_port =
> +		hrtimer_to_hellcreek_port(timer);

That moment when not even the helper macro fits in 80 characters..
I think you should let this line have 81 characters.

> +	struct hellcreek *hellcreek = hellcreek_port->hellcreek;
> +	struct hellcreek_schedule *schedule;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&hellcreek->reg_lock, flags);
> +
> +	/* First select port */
> +	hellcreek_select_tgd(hellcreek, hellcreek_port->port);
> +
> +	/* Set admin base time and switch schedule */
> +	hellcreek_start_schedule(hellcreek,
> +				 hellcreek_port->current_schedule->base_time);
> +
> +	schedule = hellcreek_port->current_schedule;
> +	hellcreek_port->current_schedule = NULL;
> +
> +	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
> +
> +	dev_dbg(hellcreek->dev, "ARMed EST timer for port %d\n",
> +		hellcreek_port->port);
> +
> +	/* Free resources */
> +	kfree(schedule->entries);
> +	kfree(schedule);
> +
> +	return HRTIMER_NORESTART;
> +}
> +
> +static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
> +				       const struct tc_taprio_qopt_offload *taprio)
> +{
> +	struct net_device *netdev = dsa_to_port(ds, port)->slave;
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port *hellcreek_port;
> +	struct hellcreek_schedule *schedule;
> +	unsigned long flags;
> +	ktime_t start;
> +	u16 ctrl;
> +
> +	hellcreek_port = &hellcreek->ports[port];
> +
> +	/* Convert taprio data to hellcreek schedule */
> +	schedule = hellcreek_taprio_to_schedule(taprio);
> +	if (IS_ERR(schedule))
> +		return PTR_ERR(schedule);
> +
> +	dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n",
> +		port);
> +
> +	/* Cancel an in flight timer */
> +	hrtimer_cancel(&hellcreek_port->cycle_start_timer);
> +
> +	spin_lock_irqsave(&hellcreek->reg_lock, flags);
> +
> +	if (hellcreek_port->current_schedule) {
> +		kfree(hellcreek_port->current_schedule->entries);
> +		kfree(hellcreek_port->current_schedule);
> +	}
> +
> +	hellcreek_port->current_schedule = schedule;
> +
> +	/* First select port */
> +	hellcreek_select_tgd(hellcreek, port);
> +
> +	/* Setup traffic class <-> queue mapping */
> +	hellcreek_setup_tc_mapping(hellcreek, netdev);
> +
> +	/* Enable gating and set the admin state to forward everything in the
> +	 * mean time
> +	 */
> +	ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN;
> +	hellcreek_write(hellcreek, ctrl, TR_TGDCTRL);
> +
> +	/* Cancel pending schedule */
> +	hellcreek_write(hellcreek, 0x00, TR_ESTCMD);
> +
> +	/* Setup a new schedule */
> +	hellcreek_setup_gcl(hellcreek, port, schedule);
> +
> +	/* Configure cycle time */
> +	hellcreek_set_cycle_time(hellcreek, schedule);
> +
> +	/* Setup timer for schedule switch: The IP core only allows to set a
> +	 * cycle start timer 8 seconds in the future. This is why we setup the
> +	 * hritmer to base_time - 5 seconds. Then, we have enough time to
> +	 * activate IP core's EST timer.
> +	 */
> +	start = ktime_sub_ns(schedule->base_time, (u64)5 * NSEC_PER_SEC);
> +	hrtimer_start_range_ns(&hellcreek_port->cycle_start_timer, start,
> +			       NSEC_PER_SEC, HRTIMER_MODE_ABS);

Explain again how this works, please? The hrtimer measures the CLOCK_TAI
of the CPU, but you are offloading the CLOCK_TAI domain of the NIC? So
you are assuming that the CPU and the NIC PHC are synchronized? What if
they aren't?

And what if the base-time is in the past, do you deal with that (how
does the hardware deal with a base-time in the past)?
A base-time in the past (example: 0) should work: you should advance the
base-time into the nearest future multiple of the cycle-time, to at
least preserve phase correctness of the schedule.

Just trying to understand if this whole hrtimer thing is worth it. It
complicates the driver by quite a significant amount.

> +
> +	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
> +
> +	return 0;
> +}
> +
> +static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port *hellcreek_port;
> +	unsigned long flags;
> +
> +	hellcreek_port = &hellcreek->ports[port];
> +
> +	dev_dbg(hellcreek->dev, "Remove traffic schedule on port %d\n", port);
> +
> +	/* First cancel timer */
> +	hrtimer_cancel(&hellcreek_port->cycle_start_timer);
> +
> +	spin_lock_irqsave(&hellcreek->reg_lock, flags);
> +
> +	if (hellcreek_port->current_schedule) {
> +		kfree(hellcreek_port->current_schedule->entries);
> +		kfree(hellcreek_port->current_schedule);
> +		hellcreek_port->current_schedule = NULL;
> +	}
> +
> +	/* Then select port */
> +	hellcreek_select_tgd(hellcreek, port);
> +
> +	/* Revert tc mapping */
> +	__hellcreek_setup_tc_identity_mapping(hellcreek);
> +
> +	/* Disable gating and return to regular switching flow */
> +	hellcreek_write(hellcreek, 0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT,
> +			TR_TGDCTRL);
> +
> +	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
> +
> +	return 0;
> +}
> +
> +static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port,
> +				   enum tc_setup_type type, void *type_data)
> +{
> +	const struct tc_taprio_qopt_offload *taprio = type_data;
> +
> +	if (type != TC_SETUP_QDISC_TAPRIO)
> +		return -EOPNOTSUPP;
> +
> +	if (taprio->enable)
> +		return hellcreek_port_set_schedule(ds, port, taprio);
> +
> +	return hellcreek_port_del_schedule(ds, port);
> +}
> +
>  static const struct dsa_switch_ops hellcreek_ds_ops = {
>  	.get_tag_protocol    = hellcreek_get_tag_protocol,
>  	.setup		     = hellcreek_setup,
> @@ -1104,6 +1394,7 @@ static const struct dsa_switch_ops hellcreek_ds_ops = {
>  	.port_hwtstamp_get   = hellcreek_port_hwtstamp_get,
>  	.port_txtstamp	     = hellcreek_port_txtstamp,
>  	.port_rxtstamp	     = hellcreek_port_rxtstamp,
> +	.port_setup_tc	     = hellcreek_port_setup_tc,
>  	.get_ts_info	     = hellcreek_get_ts_info,
>  };
>  
> @@ -1135,6 +1426,9 @@ static int hellcreek_probe(struct platform_device *pdev)
>  		if (!port->counter_values)
>  			return -ENOMEM;
>  
> +		hrtimer_init(&port->cycle_start_timer, CLOCK_TAI,
> +			     HRTIMER_MODE_ABS);
> +		port->cycle_start_timer.function = hellcreek_set_schedule;
>  		port->hellcreek = hellcreek;
>  		port->port	= i;
>  	}
> diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h
> index 1d3de72a48a5..d3d1a1144857 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/spinlock.h>
> +#include <linux/hrtimer.h>
>  #include <net/dsa.h>
>  
>  /* Ports:
> @@ -210,6 +211,20 @@ struct hellcreek_counter {
>  	const char *name;
>  };
>  
> +struct hellcreek_gcl_entry {
> +	u32 interval;
> +	u8 gate_states;
> +	bool overrun_ignore;
> +};
> +
> +struct hellcreek_schedule {
> +	struct hellcreek_gcl_entry *entries;
> +	size_t num_entries;
> +	ktime_t base_time;
> +	u32 cycle_time;
> +	int port;
> +};
> +
>  struct hellcreek;
>  
>  /* State flags for hellcreek_port_hwtstamp::state */
> @@ -236,6 +251,8 @@ struct hellcreek_port_hwtstamp {
>  
>  struct hellcreek_port {
>  	struct hellcreek *hellcreek;
> +	struct hellcreek_schedule *current_schedule;
> +	struct hrtimer cycle_start_timer;
>  	int port;
>  	u16 ptcfg;		/* ptcfg shadow */
>  	u64 *counter_values;
> @@ -273,4 +290,8 @@ struct hellcreek {
>  	size_t fdb_entries;
>  };
>  
> +#define hrtimer_to_hellcreek_port(timer)		\
> +	container_of(timer, struct hellcreek_port,	\
> +		     cycle_start_timer)
> +
>  #endif /* _HELLCREEK_H_ */
> -- 
> 2.20.1
> 

Thanks,
-Vladimir

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

* Re: [PATCH v3 0/8] Hirschmann Hellcreek DSA driver
  2020-08-24 22:35     ` David Miller
@ 2020-08-24 22:57       ` Vladimir Oltean
  2020-08-25 11:21       ` Kurt Kanzenbach
  1 sibling, 0 replies; 44+ messages in thread
From: Vladimir Oltean @ 2020-08-24 22:57 UTC (permalink / raw)
  To: David Miller
  Cc: kuba, kurt, andrew, vivien.didelot, f.fainelli, netdev, robh+dt,
	devicetree, bigeasy, richardcochran, kamil.alkhouri,
	ilias.apalodimas, ivan.khoronzhuk, vinicius.gomes,
	xiaoliang.yang_1, Po.Liu

On Mon, Aug 24, 2020 at 03:35:18PM -0700, David Miller wrote:
> From: Vladimir Oltean <olteanv@gmail.com>
> Date: Tue, 25 Aug 2020 01:02:03 +0300
> 
> > Just my comment on patch 5/8 about netdev->tc_to_txq. There are 2
> > distinct things about that:
> > - accessing struct net_device directly hurts the DSA model a little bit.
> > - I think there's some confusion regarding the use of netdev->tc_to_txq
> >   itself. I don't think that's the right place to setup a VLAN PCP to
> >   traffic class mapping. That's simply "what traffic class does each
> >   netdev queue have". I would even go as far as say that Linux doesn't
> >   support a VLAN PCP to TC mapping (similar to the DSCP to TC mapping
> >   from the DCB ops) at all, except for the ingress-qos-map and
> >   egress-qos-map of the 8021q driver, which can't be offloaded and don't
> >   map nicely over existing hardware anyway (what hardware has an
> >   ingress-qos-map and an egress-qos-map per individual VLAN?!).
> >   Although I do really see the need for having a mapping between VLAN
> >   PCP and traffic class, I would suggest Kurt to not expose this through
> >   taprio/mqprio (hardcode the PCP mapping as 1-to-1 with TC, as other
> >   drivers do), and let's try to come up separately with an abstraction
> >   for that.
> 
> Agreed, Kurt can you repost this series without the TAPRIO support for
> now since it's controversial and needs more discussion and changes?
> 
> Thank you.

To be clear, the most important part of the taprio qdisc offload
(setting up the gate control list) does not need to be postponed. It's
just the VLAN PCP mapping that is a bit controversial.

Thanks,
-Vladimir

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-24  6:10     ` Kurt Kanzenbach
@ 2020-08-24 23:45       ` Vinicius Costa Gomes
  2020-08-25  9:42         ` Kurt Kanzenbach
  2020-08-25  9:46         ` Vladimir Oltean
  0 siblings, 2 replies; 44+ messages in thread
From: Vinicius Costa Gomes @ 2020-08-24 23:45 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, Kamil Alkhouri,
	ilias.apalodimas

Hi Kurt,

Kurt Kanzenbach <kurt@linutronix.de> writes:

>>> +static void hellcreek_setup_tc_mapping(struct hellcreek *hellcreek,
>>> +				       struct net_device *netdev)
>>> +{
>>> +	int i, j;
>>> +
>>> +	/* Setup mapping between traffic classes and port queues. */
>>> +	for (i = 0; i < netdev_get_num_tc(netdev); ++i) {
>>> +		for (j = 0; j < netdev->tc_to_txq[i].count; ++j) {
>>> +			const int queue = j + netdev->tc_to_txq[i].offset;
>>> +
>>> +			hellcreek_select_prio(hellcreek, i);
>>> +			hellcreek_write(hellcreek,
>>> +					queue << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
>>> +					HR_PRTCCFG);
>>> +		}
>>> +	}
>>> +}
>>
>> What other driver have you seen that does this?
>>
>
> Probably none.
>
> With TAPRIO traffic classes and the mapping to queues can be
> configured. The switch can also map traffic classes. That sounded like a
> good match to me.

The only reason I could think that you would need this that *right now*
taprio has pretty glaring oversight: that in the offload parameters each entry
'gate_mask' reference the "Traffic Class" (i.e. bit 0 is Traffic Class
0), and it really should be the HW queue.

I have a patch that does the conversion on taprio before talking to the
driver. Do you think it would help you avoid doing this on the driver
side?

>
> Thanks,
> Kurt


Cheers,
-- 
Vinicius

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-20  8:11 ` [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support Kurt Kanzenbach
  2020-08-22 14:39   ` Vladimir Oltean
  2020-08-24 22:56   ` Vladimir Oltean
@ 2020-08-24 23:57   ` Vinicius Costa Gomes
  2020-08-25  9:23     ` Kurt Kanzenbach
  2 siblings, 1 reply; 44+ messages in thread
From: Vinicius Costa Gomes @ 2020-08-24 23:57 UTC (permalink / raw)
  To: Kurt Kanzenbach, Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean, Kurt Kanzenbach

Hi,

Kurt Kanzenbach <kurt@linutronix.de> writes:

> The switch has support for the 802.1Qbv Time Aware Shaper (TAS). Traffic
> schedules may be configured individually on each front port. Each port has eight
> egress queues. The traffic is mapped to a traffic class respectively via the PCP
> field of a VLAN tagged frame.
>
> The TAPRIO Qdisc already implements that. Therefore, this interface can simply
> be reused. Add .port_setup_tc() accordingly.
>
> The activation of a schedule on a port is split into two parts:
>
>  * Programming the necessary gate control list (GCL)
>  * Setup hrtimer for starting the schedule
>
> The hardware supports starting a schedule up to eight seconds in the future. The
> TAPRIO interface provides an absolute base time. Therefore, hrtimers are
> leveraged.

The driver side looks good, looks like a well behaved piece of hardware,
even not supporting schedules with a base time 8 seconds (or later) in
the future is not so bad.

>
> Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
> ---
>  drivers/net/dsa/hirschmann/hellcreek.c | 294 +++++++++++++++++++++++++
>  drivers/net/dsa/hirschmann/hellcreek.h |  21 ++
>  2 files changed, 315 insertions(+)
>
> diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
> index 745ca60342b4..e5b54f42c635 100644
> --- a/drivers/net/dsa/hirschmann/hellcreek.c
> +++ b/drivers/net/dsa/hirschmann/hellcreek.c
> @@ -22,7 +22,9 @@
>  #include <linux/spinlock.h>
>  #include <linux/delay.h>
>  #include <linux/ktime.h>
> +#include <linux/time.h>
>  #include <net/dsa.h>
> +#include <net/pkt_sched.h>
>  
>  #include "hellcreek.h"
>  #include "hellcreek_ptp.h"
> @@ -153,6 +155,15 @@ static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid,
>  	hellcreek_write(hellcreek, val, HR_VIDCFG);
>  }
>  
> +static void hellcreek_select_tgd(struct hellcreek *hellcreek, int port)
> +{
> +	u16 val = 0;
> +
> +	val |= port << TR_TGDSEL_TDGSEL_SHIFT;
> +
> +	hellcreek_write(hellcreek, val, TR_TGDSEL);
> +}
> +
>  static int hellcreek_wait_until_ready(struct hellcreek *hellcreek)
>  {
>  	u16 val;
> @@ -958,6 +969,24 @@ static void __hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
>  	}
>  }
>  
> +static void hellcreek_setup_tc_mapping(struct hellcreek *hellcreek,
> +				       struct net_device *netdev)
> +{
> +	int i, j;
> +
> +	/* Setup mapping between traffic classes and port queues. */
> +	for (i = 0; i < netdev_get_num_tc(netdev); ++i) {
> +		for (j = 0; j < netdev->tc_to_txq[i].count; ++j) {
> +			const int queue = j + netdev->tc_to_txq[i].offset;
> +
> +			hellcreek_select_prio(hellcreek, i);
> +			hellcreek_write(hellcreek,
> +					queue << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
> +					HR_PRTCCFG);
> +		}
> +	}
> +}
> +
>  static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
>  {
>  	unsigned long flags;
> @@ -1081,6 +1110,267 @@ static void hellcreek_phylink_validate(struct dsa_switch *ds, int port,
>  		   __ETHTOOL_LINK_MODE_MASK_NBITS);
>  }
>  
> +static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port,
> +				const struct hellcreek_schedule *schedule)
> +{
> +	size_t i;
> +
> +	for (i = 1; i <= schedule->num_entries; ++i) {
> +		const struct hellcreek_gcl_entry *cur, *initial, *next;
> +		u16 data;
> +		u8 gates;
> +
> +		cur	= &schedule->entries[i - 1];
> +		initial = &schedule->entries[0];
> +		next	= &schedule->entries[i];
> +
> +		if (i == schedule->num_entries)
> +			gates = initial->gate_states ^
> +				cur->gate_states;
> +		else
> +			gates = next->gate_states ^
> +				cur->gate_states;
> +
> +		data = gates;
> +		if (cur->overrun_ignore)
> +			data |= TR_GCLDAT_GCLOVRI;
> +
> +		if (i == schedule->num_entries)
> +			data |= TR_GCLDAT_GCLWRLAST;
> +
> +		/* Gates states */
> +		hellcreek_write(hellcreek, data, TR_GCLDAT);
> +
> +		/* Time intervall */
> +		hellcreek_write(hellcreek,
> +				cur->interval & 0x0000ffff,
> +				TR_GCLTIL);
> +		hellcreek_write(hellcreek,
> +				(cur->interval & 0xffff0000) >> 16,
> +				TR_GCLTIH);
> +
> +		/* Commit entry */
> +		data = ((i - 1) << TR_GCLCMD_GCLWRADR_SHIFT) |
> +			(initial->gate_states <<
> +			 TR_GCLCMD_INIT_GATE_STATES_SHIFT);
> +		hellcreek_write(hellcreek, data, TR_GCLCMD);
> +	}
> +}
> +
> +static void hellcreek_set_cycle_time(struct hellcreek *hellcreek,
> +				     const struct hellcreek_schedule *schedule)
> +{
> +	u32 cycle_time = schedule->cycle_time;
> +
> +	hellcreek_write(hellcreek, cycle_time & 0x0000ffff, TR_CTWRL);
> +	hellcreek_write(hellcreek, (cycle_time & 0xffff0000) >> 16, TR_CTWRH);
> +}
> +
> +static void hellcreek_start_schedule(struct hellcreek *hellcreek,
> +				     ktime_t start_time)
> +{
> +	struct timespec64 ts = ktime_to_timespec64(start_time);
> +
> +	/* Start can be only 8 seconds in the future */
> +	ts.tv_sec %= 8;
> +
> +	/* Start schedule at this point of time */
> +	hellcreek_write(hellcreek, ts.tv_nsec & 0x0000ffff, TR_ESTWRL);
> +	hellcreek_write(hellcreek, (ts.tv_nsec & 0xffff0000) >> 16, TR_ESTWRH);
> +
> +	/* Arm timer, set seconds and switch schedule */
> +	hellcreek_write(hellcreek, TR_ESTCMD_ESTARM | TR_ESTCMD_ESTSWCFG |
> +		     ((ts.tv_sec & TR_ESTCMD_ESTSEC_MASK) <<
> +		      TR_ESTCMD_ESTSEC_SHIFT), TR_ESTCMD);
> +}
> +
> +static struct hellcreek_schedule *hellcreek_taprio_to_schedule(
> +	const struct tc_taprio_qopt_offload *taprio)
> +{
> +	struct hellcreek_schedule *schedule;
> +	size_t i;
> +
> +	/* Allocate some memory first */
> +	schedule = kzalloc(sizeof(*schedule), GFP_KERNEL);
> +	if (!schedule)
> +		return ERR_PTR(-ENOMEM);
> +	schedule->entries = kcalloc(taprio->num_entries,
> +				    sizeof(*schedule->entries),
> +				    GFP_KERNEL);
> +	if (!schedule->entries) {
> +		kfree(schedule);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	/* Construct hellcreek schedule */
> +	schedule->num_entries = taprio->num_entries;
> +	schedule->base_time   = taprio->base_time;
> +
> +	for (i = 0; i < taprio->num_entries; ++i) {
> +		const struct tc_taprio_sched_entry *t = &taprio->entries[i];
> +		struct hellcreek_gcl_entry *k = &schedule->entries[i];
> +
> +		k->interval	  = t->interval;
> +		k->gate_states	  = t->gate_mask;
> +		k->overrun_ignore = 0;
> +
> +		/* Update complete cycle time */
> +		schedule->cycle_time += t->interval;
> +	}
> +
> +	return schedule;
> +}
> +
> +static enum hrtimer_restart hellcreek_set_schedule(struct hrtimer *timer)
> +{
> +	struct hellcreek_port *hellcreek_port =
> +		hrtimer_to_hellcreek_port(timer);
> +	struct hellcreek *hellcreek = hellcreek_port->hellcreek;
> +	struct hellcreek_schedule *schedule;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&hellcreek->reg_lock, flags);
> +
> +	/* First select port */
> +	hellcreek_select_tgd(hellcreek, hellcreek_port->port);
> +
> +	/* Set admin base time and switch schedule */
> +	hellcreek_start_schedule(hellcreek,
> +				 hellcreek_port->current_schedule->base_time);
> +
> +	schedule = hellcreek_port->current_schedule;
> +	hellcreek_port->current_schedule = NULL;
> +
> +	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
> +
> +	dev_dbg(hellcreek->dev, "ARMed EST timer for port %d\n",
> +		hellcreek_port->port);
> +
> +	/* Free resources */
> +	kfree(schedule->entries);
> +	kfree(schedule);
> +
> +	return HRTIMER_NORESTART;
> +}
> +
> +static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
> +				       const struct tc_taprio_qopt_offload *taprio)
> +{
> +	struct net_device *netdev = dsa_to_port(ds, port)->slave;
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port *hellcreek_port;
> +	struct hellcreek_schedule *schedule;
> +	unsigned long flags;
> +	ktime_t start;
> +	u16 ctrl;
> +
> +	hellcreek_port = &hellcreek->ports[port];
> +
> +	/* Convert taprio data to hellcreek schedule */
> +	schedule = hellcreek_taprio_to_schedule(taprio);
> +	if (IS_ERR(schedule))
> +		return PTR_ERR(schedule);
> +
> +	dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n",
> +		port);
> +
> +	/* Cancel an in flight timer */
> +	hrtimer_cancel(&hellcreek_port->cycle_start_timer);
> +
> +	spin_lock_irqsave(&hellcreek->reg_lock, flags);
> +
> +	if (hellcreek_port->current_schedule) {
> +		kfree(hellcreek_port->current_schedule->entries);
> +		kfree(hellcreek_port->current_schedule);
> +	}
> +
> +	hellcreek_port->current_schedule = schedule;
> +
> +	/* First select port */
> +	hellcreek_select_tgd(hellcreek, port);
> +
> +	/* Setup traffic class <-> queue mapping */
> +	hellcreek_setup_tc_mapping(hellcreek, netdev);
> +
> +	/* Enable gating and set the admin state to forward everything in the
> +	 * mean time
> +	 */
> +	ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN;
> +	hellcreek_write(hellcreek, ctrl, TR_TGDCTRL);
> +
> +	/* Cancel pending schedule */
> +	hellcreek_write(hellcreek, 0x00, TR_ESTCMD);
> +
> +	/* Setup a new schedule */
> +	hellcreek_setup_gcl(hellcreek, port, schedule);
> +
> +	/* Configure cycle time */
> +	hellcreek_set_cycle_time(hellcreek, schedule);
> +
> +	/* Setup timer for schedule switch: The IP core only allows to set a
> +	 * cycle start timer 8 seconds in the future. This is why we setup the
> +	 * hritmer to base_time - 5 seconds. Then, we have enough time to
> +	 * activate IP core's EST timer.
> +	 */
> +	start = ktime_sub_ns(schedule->base_time, (u64)5 * NSEC_PER_SEC);
> +	hrtimer_start_range_ns(&hellcreek_port->cycle_start_timer, start,
> +			       NSEC_PER_SEC, HRTIMER_MODE_ABS);

If we are talking about seconds here, I don't think you need to use a
hrtimer, you could use a workqueue/delayed_work. Should make things a
bit simpler. 

> +
> +	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
> +
> +	return 0;
> +}
> +
> +static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port)
> +{
> +	struct hellcreek *hellcreek = ds->priv;
> +	struct hellcreek_port *hellcreek_port;
> +	unsigned long flags;
> +
> +	hellcreek_port = &hellcreek->ports[port];
> +
> +	dev_dbg(hellcreek->dev, "Remove traffic schedule on port %d\n", port);
> +
> +	/* First cancel timer */
> +	hrtimer_cancel(&hellcreek_port->cycle_start_timer);
> +
> +	spin_lock_irqsave(&hellcreek->reg_lock, flags);
> +
> +	if (hellcreek_port->current_schedule) {
> +		kfree(hellcreek_port->current_schedule->entries);
> +		kfree(hellcreek_port->current_schedule);
> +		hellcreek_port->current_schedule = NULL;
> +	}
> +
> +	/* Then select port */
> +	hellcreek_select_tgd(hellcreek, port);
> +
> +	/* Revert tc mapping */
> +	__hellcreek_setup_tc_identity_mapping(hellcreek);
> +
> +	/* Disable gating and return to regular switching flow */
> +	hellcreek_write(hellcreek, 0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT,
> +			TR_TGDCTRL);
> +
> +	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
> +
> +	return 0;
> +}
> +
> +static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port,
> +				   enum tc_setup_type type, void *type_data)
> +{
> +	const struct tc_taprio_qopt_offload *taprio = type_data;
> +
> +	if (type != TC_SETUP_QDISC_TAPRIO)
> +		return -EOPNOTSUPP;
> +
> +	if (taprio->enable)
> +		return hellcreek_port_set_schedule(ds, port, taprio);
> +
> +	return hellcreek_port_del_schedule(ds, port);
> +}
> +
>  static const struct dsa_switch_ops hellcreek_ds_ops = {
>  	.get_tag_protocol    = hellcreek_get_tag_protocol,
>  	.setup		     = hellcreek_setup,
> @@ -1104,6 +1394,7 @@ static const struct dsa_switch_ops hellcreek_ds_ops = {
>  	.port_hwtstamp_get   = hellcreek_port_hwtstamp_get,
>  	.port_txtstamp	     = hellcreek_port_txtstamp,
>  	.port_rxtstamp	     = hellcreek_port_rxtstamp,
> +	.port_setup_tc	     = hellcreek_port_setup_tc,
>  	.get_ts_info	     = hellcreek_get_ts_info,
>  };
>  
> @@ -1135,6 +1426,9 @@ static int hellcreek_probe(struct platform_device *pdev)
>  		if (!port->counter_values)
>  			return -ENOMEM;
>  
> +		hrtimer_init(&port->cycle_start_timer, CLOCK_TAI,
> +			     HRTIMER_MODE_ABS);
> +		port->cycle_start_timer.function = hellcreek_set_schedule;
>  		port->hellcreek = hellcreek;
>  		port->port	= i;
>  	}
> diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h
> index 1d3de72a48a5..d3d1a1144857 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/spinlock.h>
> +#include <linux/hrtimer.h>
>  #include <net/dsa.h>
>  
>  /* Ports:
> @@ -210,6 +211,20 @@ struct hellcreek_counter {
>  	const char *name;
>  };
>  
> +struct hellcreek_gcl_entry {
> +	u32 interval;
> +	u8 gate_states;
> +	bool overrun_ignore;
> +};
> +
> +struct hellcreek_schedule {
> +	struct hellcreek_gcl_entry *entries;
> +	size_t num_entries;
> +	ktime_t base_time;
> +	u32 cycle_time;
> +	int port;
> +};
> +
>  struct hellcreek;
>  
>  /* State flags for hellcreek_port_hwtstamp::state */
> @@ -236,6 +251,8 @@ struct hellcreek_port_hwtstamp {
>  
>  struct hellcreek_port {
>  	struct hellcreek *hellcreek;
> +	struct hellcreek_schedule *current_schedule;
> +	struct hrtimer cycle_start_timer;
>  	int port;
>  	u16 ptcfg;		/* ptcfg shadow */
>  	u64 *counter_values;
> @@ -273,4 +290,8 @@ struct hellcreek {
>  	size_t fdb_entries;
>  };
>  
> +#define hrtimer_to_hellcreek_port(timer)		\
> +	container_of(timer, struct hellcreek_port,	\
> +		     cycle_start_timer)
> +
>  #endif /* _HELLCREEK_H_ */
> -- 
> 2.20.1
>

Cheers,
-- 
Vinicius

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

* Re: [PATCH v3 2/8] net: dsa: Add DSA driver for Hirschmann Hellcreek switches
  2020-08-24 22:44   ` Andrew Lunn
@ 2020-08-25  9:07     ` Kurt Kanzenbach
  2020-08-25 13:56       ` Andrew Lunn
  0 siblings, 1 reply; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-25  9:07 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean

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

On Tue Aug 25 2020, Andrew Lunn wrote:
[snip]
>>  
>>  source "drivers/net/dsa/sja1105/Kconfig"
>>  
>> +source "drivers/net/dsa/hirschmann/Kconfig"
>> +
>>  config NET_DSA_QCA8K
>>  	tristate "Qualcomm Atheros QCA8K Ethernet switch family support"
>>  	depends on NET_DSA
>
> Hi Kurt
>
> The DSA entries are sorted into alphabetic order based on what you see
> in make menuconfig. As such, "Hirschmann Hellcreek TSN Switch support"
> fits in between "DSA mock-up Ethernet switch chip support" and "Lantiq
> / Intel GSWIP"

Yes, of course. I've only sorted the entries in the previous patch...

>
>> diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
>> index 4a943ccc2ca4..a707ccc3a940 100644
>> --- a/drivers/net/dsa/Makefile
>> +++ b/drivers/net/dsa/Makefile
>> @@ -23,3 +23,4 @@ obj-y				+= mv88e6xxx/
>>  obj-y				+= ocelot/
>>  obj-y				+= qca/
>>  obj-y				+= sja1105/
>> +obj-y				+= hirschmann/
>
> This file is also sorted. 
>
>> +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_MODULE_ID)
>> +		return -ENODEV;
>
> Are there other Hellcreek devices? I'm just wondering if we should
> have a specific compatible for 0x4c30 as well as the more generic 
> "hirschmann,hellcreek".

Yes, there will be different revisions of the Hellcreek devices. This ID
is really device specific. A lot of features of this switch are
configured in the VHDL code. For instance the MAC settings (100 or 1000
Mbit/s).

I've discussed this with the HW engineer from Hirschmann. He suggested
to keep this check here, as the driver is currently specific for the
that device. We have to make sure that the driver matches the hardware.

My plan was to extend this when I have access to other revisions. There
will be a SPI variant as well. But, I didn't want to implement it without the
ability to test it.

>
>> +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;
>> +	unsigned long flags;
>> +	int i;
>> +
>> +	hellcreek_port = &hellcreek->ports[port];
>> +
>> +	spin_lock_irqsave(&hellcreek->reg_lock, flags);
>> +	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;
>> +
>> +		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.
>> +		 */
>
> Is there any locking/snapshot of all the counters at once? Most
> devices have support for that, so you can compare counters against
> each other.

No, there is not.

Thanks,
Kurt

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

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-24 23:57   ` Vinicius Costa Gomes
@ 2020-08-25  9:23     ` Kurt Kanzenbach
  2020-08-25  9:32       ` Vladimir Oltean
  2020-08-25 17:50       ` Vinicius Costa Gomes
  0 siblings, 2 replies; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-25  9:23 UTC (permalink / raw)
  To: Vinicius Costa Gomes, Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean

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

On Mon Aug 24 2020, Vinicius Costa Gomes wrote:
> Hi,
>
> Kurt Kanzenbach <kurt@linutronix.de> writes:
>
[snip]
>> +	/* Setup timer for schedule switch: The IP core only allows to set a
>> +	 * cycle start timer 8 seconds in the future. This is why we setup the
>> +	 * hritmer to base_time - 5 seconds. Then, we have enough time to
>> +	 * activate IP core's EST timer.
>> +	 */
>> +	start = ktime_sub_ns(schedule->base_time, (u64)5 * NSEC_PER_SEC);
>> +	hrtimer_start_range_ns(&hellcreek_port->cycle_start_timer, start,
>> +			       NSEC_PER_SEC, HRTIMER_MODE_ABS);
>
> If we are talking about seconds here, I don't think you need to use a
> hrtimer, you could use a workqueue/delayed_work. Should make things a
> bit simpler.

I've used hrtimers for one reason: The hrtimer provides a way to fire at
an absolute base time based on CLOCK_TAI. All the other facilities such
as workqueues, timer list timers, etc do not.

In the typical setup, we run ptp4l as boundary clock (or as TAB which is
work in progress) and phc2sys to synchronize the ptp clock to the Linux
system. Let's assume we setup a TAPRIO schedule with a base time X. Now,
the grand master time changes meaning the timer has to go off earlier or
later.

Thanks,
Kurt

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

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-25  9:23     ` Kurt Kanzenbach
@ 2020-08-25  9:32       ` Vladimir Oltean
  2020-09-01 14:20         ` Kurt Kanzenbach
  2020-08-25 17:50       ` Vinicius Costa Gomes
  1 sibling, 1 reply; 44+ messages in thread
From: Vladimir Oltean @ 2020-08-25  9:32 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Vinicius Costa Gomes, 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, Aug 25, 2020 at 11:23:56AM +0200, Kurt Kanzenbach wrote:
> On Mon Aug 24 2020, Vinicius Costa Gomes wrote:
> > Hi,
> >
> > Kurt Kanzenbach <kurt@linutronix.de> writes:
> >
> [snip]
> >> +	/* Setup timer for schedule switch: The IP core only allows to set a
> >> +	 * cycle start timer 8 seconds in the future. This is why we setup the
> >> +	 * hritmer to base_time - 5 seconds. Then, we have enough time to
> >> +	 * activate IP core's EST timer.
> >> +	 */
> >> +	start = ktime_sub_ns(schedule->base_time, (u64)5 * NSEC_PER_SEC);
> >> +	hrtimer_start_range_ns(&hellcreek_port->cycle_start_timer, start,
> >> +			       NSEC_PER_SEC, HRTIMER_MODE_ABS);
> >
> > If we are talking about seconds here, I don't think you need to use a
> > hrtimer, you could use a workqueue/delayed_work. Should make things a
> > bit simpler.
> 
> I've used hrtimers for one reason: The hrtimer provides a way to fire at
> an absolute base time based on CLOCK_TAI. All the other facilities such
> as workqueues, timer list timers, etc do not.

That still doesn't justify the complexity of irqsave spinlocks and such.
You could just as well schedule a workqueue from that hrtimer and have
process context...

Thanks,
-Vladimir

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-24 22:56   ` Vladimir Oltean
@ 2020-08-25  9:33     ` Kurt Kanzenbach
  2020-08-25  9:38       ` Vladimir Oltean
  0 siblings, 1 reply; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-25  9:33 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: 5900 bytes --]

On Tue Aug 25 2020, Vladimir Oltean wrote:
> On Thu, Aug 20, 2020 at 10:11:15AM +0200, Kurt Kanzenbach wrote:
[snip]
>> +static struct hellcreek_schedule *hellcreek_taprio_to_schedule(
>> +	const struct tc_taprio_qopt_offload *taprio)
>
> Personal indentation preference:
>
> static struct hellcreek_schedule
> *hellcreek_taprio_to_schedule(const struct tc_taprio_qopt_offload *taprio)

Sure.

>
>> +{
>> +	struct hellcreek_schedule *schedule;
>> +	size_t i;
>> +
>> +	/* Allocate some memory first */
>> +	schedule = kzalloc(sizeof(*schedule), GFP_KERNEL);
>> +	if (!schedule)
>> +		return ERR_PTR(-ENOMEM);
>> +	schedule->entries = kcalloc(taprio->num_entries,
>> +				    sizeof(*schedule->entries),
>> +				    GFP_KERNEL);
>> +	if (!schedule->entries) {
>> +		kfree(schedule);
>> +		return ERR_PTR(-ENOMEM);
>> +	}
>> +
>> +	/* Construct hellcreek schedule */
>> +	schedule->num_entries = taprio->num_entries;
>> +	schedule->base_time   = taprio->base_time;
>> +
>> +	for (i = 0; i < taprio->num_entries; ++i) {
>> +		const struct tc_taprio_sched_entry *t = &taprio->entries[i];
>> +		struct hellcreek_gcl_entry *k = &schedule->entries[i];
>> +
>> +		k->interval	  = t->interval;
>> +		k->gate_states	  = t->gate_mask;
>> +		k->overrun_ignore = 0;
>
> Tab to align with gate_states and interval?

Hm. I've used M x align. It should take care of it.

> What does overrun_ignore do, anyway?

I don't remember. The HW engineer suggested to set it to zero.

>
>> +
>> +		/* Update complete cycle time */
>> +		schedule->cycle_time += t->interval;
>> +	}
>> +
>> +	return schedule;
>> +}
>> +
>> +static enum hrtimer_restart hellcreek_set_schedule(struct hrtimer *timer)
>> +{
>> +	struct hellcreek_port *hellcreek_port =
>> +		hrtimer_to_hellcreek_port(timer);
>
> That moment when not even the helper macro fits in 80 characters..
> I think you should let this line have 81 characters.

OK.

>
>> +	struct hellcreek *hellcreek = hellcreek_port->hellcreek;
>> +	struct hellcreek_schedule *schedule;
>> +	unsigned long flags;
>> +
>> +	spin_lock_irqsave(&hellcreek->reg_lock, flags);
>> +
>> +	/* First select port */
>> +	hellcreek_select_tgd(hellcreek, hellcreek_port->port);
>> +
>> +	/* Set admin base time and switch schedule */
>> +	hellcreek_start_schedule(hellcreek,
>> +				 hellcreek_port->current_schedule->base_time);
>> +
>> +	schedule = hellcreek_port->current_schedule;
>> +	hellcreek_port->current_schedule = NULL;
>> +
>> +	spin_unlock_irqrestore(&hellcreek->reg_lock, flags);
>> +
>> +	dev_dbg(hellcreek->dev, "ARMed EST timer for port %d\n",
>> +		hellcreek_port->port);
>> +
>> +	/* Free resources */
>> +	kfree(schedule->entries);
>> +	kfree(schedule);
>> +
>> +	return HRTIMER_NORESTART;
>> +}
>> +
>> +static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
>> +				       const struct tc_taprio_qopt_offload *taprio)
>> +{
>> +	struct net_device *netdev = dsa_to_port(ds, port)->slave;
>> +	struct hellcreek *hellcreek = ds->priv;
>> +	struct hellcreek_port *hellcreek_port;
>> +	struct hellcreek_schedule *schedule;
>> +	unsigned long flags;
>> +	ktime_t start;
>> +	u16 ctrl;
>> +
>> +	hellcreek_port = &hellcreek->ports[port];
>> +
>> +	/* Convert taprio data to hellcreek schedule */
>> +	schedule = hellcreek_taprio_to_schedule(taprio);
>> +	if (IS_ERR(schedule))
>> +		return PTR_ERR(schedule);
>> +
>> +	dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n",
>> +		port);
>> +
>> +	/* Cancel an in flight timer */
>> +	hrtimer_cancel(&hellcreek_port->cycle_start_timer);
>> +
>> +	spin_lock_irqsave(&hellcreek->reg_lock, flags);
>> +
>> +	if (hellcreek_port->current_schedule) {
>> +		kfree(hellcreek_port->current_schedule->entries);
>> +		kfree(hellcreek_port->current_schedule);
>> +	}
>> +
>> +	hellcreek_port->current_schedule = schedule;
>> +
>> +	/* First select port */
>> +	hellcreek_select_tgd(hellcreek, port);
>> +
>> +	/* Setup traffic class <-> queue mapping */
>> +	hellcreek_setup_tc_mapping(hellcreek, netdev);
>> +
>> +	/* Enable gating and set the admin state to forward everything in the
>> +	 * mean time
>> +	 */
>> +	ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN;
>> +	hellcreek_write(hellcreek, ctrl, TR_TGDCTRL);
>> +
>> +	/* Cancel pending schedule */
>> +	hellcreek_write(hellcreek, 0x00, TR_ESTCMD);
>> +
>> +	/* Setup a new schedule */
>> +	hellcreek_setup_gcl(hellcreek, port, schedule);
>> +
>> +	/* Configure cycle time */
>> +	hellcreek_set_cycle_time(hellcreek, schedule);
>> +
>> +	/* Setup timer for schedule switch: The IP core only allows to set a
>> +	 * cycle start timer 8 seconds in the future. This is why we setup the
>> +	 * hritmer to base_time - 5 seconds. Then, we have enough time to
>> +	 * activate IP core's EST timer.
>> +	 */
>> +	start = ktime_sub_ns(schedule->base_time, (u64)5 * NSEC_PER_SEC);
>> +	hrtimer_start_range_ns(&hellcreek_port->cycle_start_timer, start,
>> +			       NSEC_PER_SEC, HRTIMER_MODE_ABS);
>
> Explain again how this works, please? The hrtimer measures the CLOCK_TAI
> of the CPU, but you are offloading the CLOCK_TAI domain of the NIC? So
> you are assuming that the CPU and the NIC PHC are synchronized? What if
> they aren't?

Yes, I assume that's synchronized with e.g. phc2sys.

>
> And what if the base-time is in the past, do you deal with that (how
> does the hardware deal with a base-time in the past)?
> A base-time in the past (example: 0) should work: you should advance the
> base-time into the nearest future multiple of the cycle-time, to at
> least preserve phase correctness of the schedule.

If the hrtimer is programmed with a value in the past, it fires
instantly. The callback is executed and the start time is
programmed.

>
> Just trying to understand if this whole hrtimer thing is worth it. It
> complicates the driver by quite a significant amount.

See my other reply mail, why I used hrtimers.

Thanks,
Kurt

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

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-25  9:33     ` Kurt Kanzenbach
@ 2020-08-25  9:38       ` Vladimir Oltean
  2020-08-25  9:55         ` Kurt Kanzenbach
  0 siblings, 1 reply; 44+ messages in thread
From: Vladimir Oltean @ 2020-08-25  9:38 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, Aug 25, 2020 at 11:33:53AM +0200, Kurt Kanzenbach wrote:
> On Tue Aug 25 2020, Vladimir Oltean wrote:
> > On Thu, Aug 20, 2020 at 10:11:15AM +0200, Kurt Kanzenbach wrote:
> >
> > Explain again how this works, please? The hrtimer measures the CLOCK_TAI
> > of the CPU, but you are offloading the CLOCK_TAI domain of the NIC? So
> > you are assuming that the CPU and the NIC PHC are synchronized? What if
> > they aren't?
> 
> Yes, I assume that's synchronized with e.g. phc2sys.
> 

My intuition tells me that this isn't the user's expectation, and that
it should do the right thing even if it's not synchronized to the system
clock.

> >
> > And what if the base-time is in the past, do you deal with that (how
> > does the hardware deal with a base-time in the past)?
> > A base-time in the past (example: 0) should work: you should advance the
> > base-time into the nearest future multiple of the cycle-time, to at
> > least preserve phase correctness of the schedule.
> 
> If the hrtimer is programmed with a value in the past, it fires
> instantly.

Yes, it does.

> The callback is executed and the start time is programmed.
> 

With a valid value from the hardware's perspective?

Thanks,
-Vladimir

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-24 23:45       ` Vinicius Costa Gomes
@ 2020-08-25  9:42         ` Kurt Kanzenbach
  2020-08-25 17:58           ` Vinicius Costa Gomes
  2020-08-25  9:46         ` Vladimir Oltean
  1 sibling, 1 reply; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-25  9:42 UTC (permalink / raw)
  To: Vinicius Costa Gomes, 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: 1526 bytes --]

On Mon Aug 24 2020, Vinicius Costa Gomes wrote:
> Hi Kurt,
>
> Kurt Kanzenbach <kurt@linutronix.de> writes:
>
>>>> +static void hellcreek_setup_tc_mapping(struct hellcreek *hellcreek,
>>>> +				       struct net_device *netdev)
>>>> +{
>>>> +	int i, j;
>>>> +
>>>> +	/* Setup mapping between traffic classes and port queues. */
>>>> +	for (i = 0; i < netdev_get_num_tc(netdev); ++i) {
>>>> +		for (j = 0; j < netdev->tc_to_txq[i].count; ++j) {
>>>> +			const int queue = j + netdev->tc_to_txq[i].offset;
>>>> +
>>>> +			hellcreek_select_prio(hellcreek, i);
>>>> +			hellcreek_write(hellcreek,
>>>> +					queue << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
>>>> +					HR_PRTCCFG);
>>>> +		}
>>>> +	}
>>>> +}
>>>
>>> What other driver have you seen that does this?
>>>
>>
>> Probably none.
>>
>> With TAPRIO traffic classes and the mapping to queues can be
>> configured. The switch can also map traffic classes. That sounded like a
>> good match to me.
>
> The only reason I could think that you would need this that *right now*
> taprio has pretty glaring oversight: that in the offload parameters each entry
> 'gate_mask' reference the "Traffic Class" (i.e. bit 0 is Traffic Class
> 0), and it really should be the HW queue.
>
> I have a patch that does the conversion on taprio before talking to the
> driver. Do you think it would help you avoid doing this on the driver
> side?

I think so. As Vladimir pointed out, the driver should setup an identity
mapping which I already did by default.

Can you point me your patch?

Thanks,
Kurt

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

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-24 23:45       ` Vinicius Costa Gomes
  2020-08-25  9:42         ` Kurt Kanzenbach
@ 2020-08-25  9:46         ` Vladimir Oltean
  2020-08-25 10:09           ` Kurt Kanzenbach
  2020-08-25 17:33           ` Vinicius Costa Gomes
  1 sibling, 2 replies; 44+ messages in thread
From: Vladimir Oltean @ 2020-08-25  9:46 UTC (permalink / raw)
  To: Vinicius Costa Gomes
  Cc: Kurt Kanzenbach, Andrew Lunn, Vivien Didelot, Florian Fainelli,
	David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas

Hi Vinicius,

On Mon, Aug 24, 2020 at 04:45:50PM -0700, Vinicius Costa Gomes wrote:
> Kurt Kanzenbach <kurt@linutronix.de> writes:
> >
> > With TAPRIO traffic classes and the mapping to queues can be
> > configured. The switch can also map traffic classes. That sounded like a
> > good match to me.
>
> The only reason I could think that you would need this that *right now*
> taprio has pretty glaring oversight: that in the offload parameters each entry
> 'gate_mask' reference the "Traffic Class" (i.e. bit 0 is Traffic Class
> 0), and it really should be the HW queue.
>

Sorry, but could you please explain why having the gate_mask reference
the traffic classes is a glaring oversight, and how changing it would
help here?

Also, Kurt, could you please explain what the
HR_PRTCCFG_PCP_TC_MAP_SHIFT field in HR_PRTCCFG is doing?
To me, it appears that it's configuring ingress QoS classification on
the port (and the reason why this is strange to me is because you're
applying this configuration through an egress qdisc), but I want to make
sure I'm not misunderstanding.

Thanks,
-Vladimir

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-25  9:38       ` Vladimir Oltean
@ 2020-08-25  9:55         ` Kurt Kanzenbach
  2020-08-27 16:25           ` Richard Cochran
  0 siblings, 1 reply; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-25  9:55 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: 1455 bytes --]

On Tue Aug 25 2020, Vladimir Oltean wrote:
> On Tue, Aug 25, 2020 at 11:33:53AM +0200, Kurt Kanzenbach wrote:
>> On Tue Aug 25 2020, Vladimir Oltean wrote:
>> > On Thu, Aug 20, 2020 at 10:11:15AM +0200, Kurt Kanzenbach wrote:
>> >
>> > Explain again how this works, please? The hrtimer measures the CLOCK_TAI
>> > of the CPU, but you are offloading the CLOCK_TAI domain of the NIC? So
>> > you are assuming that the CPU and the NIC PHC are synchronized? What if
>> > they aren't?
>> 
>> Yes, I assume that's synchronized with e.g. phc2sys.
>> 
>
> My intuition tells me that this isn't the user's expectation, and that
> it should do the right thing even if it's not synchronized to the system
> clock.

I get your point. But how to do it? We would need a timer based on the
PTP clock in the switch.

>
>> >
>> > And what if the base-time is in the past, do you deal with that (how
>> > does the hardware deal with a base-time in the past)?
>> > A base-time in the past (example: 0) should work: you should advance the
>> > base-time into the nearest future multiple of the cycle-time, to at
>> > least preserve phase correctness of the schedule.
>> 
>> If the hrtimer is programmed with a value in the past, it fires
>> instantly.
>
> Yes, it does.
>
>> The callback is executed and the start time is programmed.
>> 
>
> With a valid value from the hardware's perspective?

Yes. That's no problem.

Thanks,
Kurt

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

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-25  9:46         ` Vladimir Oltean
@ 2020-08-25 10:09           ` Kurt Kanzenbach
  2020-08-25 17:33           ` Vinicius Costa Gomes
  1 sibling, 0 replies; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-25 10:09 UTC (permalink / raw)
  To: Vladimir Oltean, Vinicius Costa Gomes
  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: 1443 bytes --]

On Tue Aug 25 2020, Vladimir Oltean wrote:
> Hi Vinicius,
>
> On Mon, Aug 24, 2020 at 04:45:50PM -0700, Vinicius Costa Gomes wrote:
>> Kurt Kanzenbach <kurt@linutronix.de> writes:
>> >
>> > With TAPRIO traffic classes and the mapping to queues can be
>> > configured. The switch can also map traffic classes. That sounded like a
>> > good match to me.
>>
>> The only reason I could think that you would need this that *right now*
>> taprio has pretty glaring oversight: that in the offload parameters each entry
>> 'gate_mask' reference the "Traffic Class" (i.e. bit 0 is Traffic Class
>> 0), and it really should be the HW queue.
>>
>
> Sorry, but could you please explain why having the gate_mask reference
> the traffic classes is a glaring oversight, and how changing it would
> help here?
>
> Also, Kurt, could you please explain what the
> HR_PRTCCFG_PCP_TC_MAP_SHIFT field in HR_PRTCCFG is doing?
> To me, it appears that it's configuring ingress QoS classification on
> the port (and the reason why this is strange to me is because you're
> applying this configuration through an egress qdisc), but I want to make
> sure I'm not misunderstanding.

All the TSN operations in the switch such as the gate control work on
internally on traffic classes. The traffic class is determined by the
PCP field on the VLAN tagged frames. This mapping is configurable via
the pcp to tc map. And the TC also defines the hardware queue.

Thanks,
Kurt

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

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

* Re: [PATCH v3 0/8] Hirschmann Hellcreek DSA driver
  2020-08-24 22:35     ` David Miller
  2020-08-24 22:57       ` Vladimir Oltean
@ 2020-08-25 11:21       ` Kurt Kanzenbach
  2020-08-25 17:14         ` Florian Fainelli
  1 sibling, 1 reply; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-25 11:21 UTC (permalink / raw)
  To: David Miller, olteanv
  Cc: kuba, andrew, vivien.didelot, f.fainelli, netdev, robh+dt,
	devicetree, bigeasy, richardcochran, kamil.alkhouri,
	ilias.apalodimas, ivan.khoronzhuk, vinicius.gomes,
	xiaoliang.yang_1, Po.Liu

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

On Mon Aug 24 2020, David Miller wrote:
> Agreed, Kurt can you repost this series without the TAPRIO support for
> now since it's controversial and needs more discussion and changes?

OK. It seems like the TAPRIO implementation has to be discussed more and
it might be good to do that separately.

I'll replace the spinlocks (which were only introduced for the hrtimers)
with mutexes and post a sane version of the driver without the TAPRIO
support.

>
> Thank you.

Thanks,
Kurt

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

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

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

> >> +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_MODULE_ID)
> >> +		return -ENODEV;
> >
> > Are there other Hellcreek devices? I'm just wondering if we should
> > have a specific compatible for 0x4c30 as well as the more generic 
> > "hirschmann,hellcreek".
> 
> Yes, there will be different revisions of the Hellcreek devices. This ID
> is really device specific. A lot of features of this switch are
> configured in the VHDL code. For instance the MAC settings (100 or 1000
> Mbit/s).
> 
> I've discussed this with the HW engineer from Hirschmann. He suggested
> to keep this check here, as the driver is currently specific for the
> that device. We have to make sure that the driver matches the hardware.

I agree with the check here. The question is about the compatible
string. Should there be a more specific compatible string as well as
the generic one?

There have been a few discussions about how the Marvell DSA driver
does its compatible string. The compatible string tells you where to
find the ID register, not what value to expect in the ID register. The
ID register can currently be in one of three different locations. Do
all current and future Hellcreak devices have the same value for
HR_MODID_C? If not, now is a good time to add a more specific
compatible string to tell you where to find the ID register.

> My plan was to extend this when I have access to other revisions. There
> will be a SPI variant as well. But, I didn't want to implement it without the
> ability to test it.

Does the SPI variant use the same value for HR_MODID_C? Maybe you need
a different compatible, maybe not, depending on how the driver is
structured.

The compatible string is part of the ABI. So thinking about it a bit
now can make things easier later. I just want to make sure you have
thought about this.

	Andrew

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

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

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

On Tue Aug 25 2020, Andrew Lunn wrote:
> I agree with the check here. The question is about the compatible
> string. Should there be a more specific compatible string as well as
> the generic one?
>
> There have been a few discussions about how the Marvell DSA driver
> does its compatible string. The compatible string tells you where to
> find the ID register, not what value to expect in the ID register. The
> ID register can currently be in one of three different locations. Do
> all current and future Hellcreak devices have the same value for
> HR_MODID_C?  If not, now is a good time to add a more specific
> compatible string to tell you where to find the ID register.
>
>> My plan was to extend this when I have access to other
>> revisions. There will be a SPI variant as well. But, I didn't want to
>> implement it without the ability to test it.
>
> Does the SPI variant use the same value for HR_MODID_C?  Maybe you
> need a different compatible, maybe not, depending on how the driver is
> structured.
>
> The compatible string is part of the ABI. So thinking about it a bit
> now can make things easier later. I just want to make sure you have
> thought about this.

I totally agree. The Marvell solution seems to work. For all current
devices the module ID is located at 0x00. Depending on the chip ID the
different properties can be configured later. The SPI variant will have
a different module ID. Anyhow, I'll ask how this will be handled for
future devices and in general.

Thanks,
Kurt

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

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

* Re: [PATCH v3 0/8] Hirschmann Hellcreek DSA driver
  2020-08-25 11:21       ` Kurt Kanzenbach
@ 2020-08-25 17:14         ` Florian Fainelli
  0 siblings, 0 replies; 44+ messages in thread
From: Florian Fainelli @ 2020-08-25 17:14 UTC (permalink / raw)
  To: Kurt Kanzenbach, David Miller, olteanv
  Cc: kuba, andrew, vivien.didelot, netdev, robh+dt, devicetree,
	bigeasy, richardcochran, kamil.alkhouri, ilias.apalodimas,
	ivan.khoronzhuk, vinicius.gomes, xiaoliang.yang_1, Po.Liu



On 8/25/2020 4:21 AM, Kurt Kanzenbach wrote:
> On Mon Aug 24 2020, David Miller wrote:
>> Agreed, Kurt can you repost this series without the TAPRIO support for
>> now since it's controversial and needs more discussion and changes?
> 
> OK. It seems like the TAPRIO implementation has to be discussed more and
> it might be good to do that separately.
> 
> I'll replace the spinlocks (which were only introduced for the hrtimers)
> with mutexes and post a sane version of the driver without the TAPRIO
> support.

Sounds great, thanks!
-- 
Florian

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-25  9:46         ` Vladimir Oltean
  2020-08-25 10:09           ` Kurt Kanzenbach
@ 2020-08-25 17:33           ` Vinicius Costa Gomes
  1 sibling, 0 replies; 44+ messages in thread
From: Vinicius Costa Gomes @ 2020-08-25 17:33 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, Kamil Alkhouri,
	ilias.apalodimas

Vladimir Oltean <olteanv@gmail.com> writes:

> Hi Vinicius,
>
> On Mon, Aug 24, 2020 at 04:45:50PM -0700, Vinicius Costa Gomes wrote:
>> Kurt Kanzenbach <kurt@linutronix.de> writes:
>> >
>> > With TAPRIO traffic classes and the mapping to queues can be
>> > configured. The switch can also map traffic classes. That sounded like a
>> > good match to me.
>>
>> The only reason I could think that you would need this that *right now*
>> taprio has pretty glaring oversight: that in the offload parameters each entry
>> 'gate_mask' reference the "Traffic Class" (i.e. bit 0 is Traffic Class
>> 0), and it really should be the HW queue.
>>
>
> Sorry, but could you please explain why having the gate_mask reference
> the traffic classes is a glaring oversight, and how changing it would
> help here?

The glaring oversight is that it when it references the traffic classes,
instead of queues, it basically ignores the 'queues *' mapping that the
user provided. (it was ignored for so long because for many cases the
mapping is 1:1)

On my reading of this part of the hellcreek code, all this was doing was
assigning priorities (based on the traffic classes) to queues, and
taprio is able to "hide" this from the driver, so all the driver needs
to care about are queues.

>
> Also, Kurt, could you please explain what the
> HR_PRTCCFG_PCP_TC_MAP_SHIFT field in HR_PRTCCFG is doing?
> To me, it appears that it's configuring ingress QoS classification on
> the port (and the reason why this is strange to me is because you're
> applying this configuration through an egress qdisc), but I want to make
> sure I'm not misunderstanding.
>
> Thanks,
> -Vladimir

-- 
Vinicius

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-25  9:23     ` Kurt Kanzenbach
  2020-08-25  9:32       ` Vladimir Oltean
@ 2020-08-25 17:50       ` Vinicius Costa Gomes
  1 sibling, 0 replies; 44+ messages in thread
From: Vinicius Costa Gomes @ 2020-08-25 17:50 UTC (permalink / raw)
  To: Kurt Kanzenbach, Andrew Lunn, Vivien Didelot, Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Richard Cochran, Kamil Alkhouri,
	ilias.apalodimas, Vladimir Oltean

Hi Kurt,

Kurt Kanzenbach <kurt@linutronix.de> writes:

> On Mon Aug 24 2020, Vinicius Costa Gomes wrote:
>> Hi,
>>
>> Kurt Kanzenbach <kurt@linutronix.de> writes:
>>
> [snip]
>>> +	/* Setup timer for schedule switch: The IP core only allows to set a
>>> +	 * cycle start timer 8 seconds in the future. This is why we setup the
>>> +	 * hritmer to base_time - 5 seconds. Then, we have enough time to
>>> +	 * activate IP core's EST timer.
>>> +	 */
>>> +	start = ktime_sub_ns(schedule->base_time, (u64)5 * NSEC_PER_SEC);
>>> +	hrtimer_start_range_ns(&hellcreek_port->cycle_start_timer, start,
>>> +			       NSEC_PER_SEC, HRTIMER_MODE_ABS);
>>
>> If we are talking about seconds here, I don't think you need to use a
>> hrtimer, you could use a workqueue/delayed_work. Should make things a
>> bit simpler.
>
> I've used hrtimers for one reason: The hrtimer provides a way to fire at
> an absolute base time based on CLOCK_TAI. All the other facilities such
> as workqueues, timer list timers, etc do not.

Oh, yeah. Good point.


Cheers,
-- 
Vinicius

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-25  9:42         ` Kurt Kanzenbach
@ 2020-08-25 17:58           ` Vinicius Costa Gomes
  2020-08-27 10:12             ` Kurt Kanzenbach
  0 siblings, 1 reply; 44+ messages in thread
From: Vinicius Costa Gomes @ 2020-08-25 17:58 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, Kamil Alkhouri,
	ilias.apalodimas

Hi Kurt,

Kurt Kanzenbach <kurt@linutronix.de> writes:

> I think so. As Vladimir pointed out, the driver should setup an identity
> mapping which I already did by default.
>
> Can you point me your patch?

Just sent it for consideration:

http://patchwork.ozlabs.org/project/netdev/patch/20200825174404.2727633-1-vinicius.gomes@intel.com/


Cheers,
-- 
Vinicius

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

* Re: [PATCH v3 8/8] dt-bindings: net: dsa: Add documentation for Hellcreek switches
  2020-08-20  8:11 ` [PATCH v3 8/8] dt-bindings: net: dsa: Add documentation for Hellcreek switches Kurt Kanzenbach
  2020-08-24 22:52   ` Andrew Lunn
@ 2020-08-25 22:28   ` Rob Herring
  1 sibling, 0 replies; 44+ messages in thread
From: Rob Herring @ 2020-08-25 22:28 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Jakub Kicinski, netdev, devicetree, Sebastian Andrzej Siewior,
	Richard Cochran, Kamil Alkhouri, ilias.apalodimas,
	Vladimir Oltean

On Thu, Aug 20, 2020 at 10:11:18AM +0200, Kurt Kanzenbach wrote:
> Add basic documentation and example.
> 
> Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
> ---
>  .../bindings/net/dsa/hellcreek.yaml           | 125 ++++++++++++++++++
>  1 file changed, 125 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..412f2e573540
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/dsa/hellcreek.yaml
> @@ -0,0 +1,125 @@
> +# 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
> +
> +  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@[0-9]+$":

As there are only 2 LED nodes:

"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

For the 'leds' node:

       additionalProperties: false

With those fixes,

Reviewed-by: Rob Herring <robh@kernel.org>

> +
> +required:
> +  - compatible
> +  - reg
> +  - reg-names
> +  - ethernet-ports
> +  - leds
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +        switch0: switch@ff240000 {
> +            compatible = "hirschmann,hellcreek";
> +            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	[flat|nested] 44+ messages in thread

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-25 17:58           ` Vinicius Costa Gomes
@ 2020-08-27 10:12             ` Kurt Kanzenbach
  0 siblings, 0 replies; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-27 10:12 UTC (permalink / raw)
  To: Vinicius Costa Gomes, 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: 628 bytes --]

Hi Vinicius,

On Tue Aug 25 2020, Vinicius Costa Gomes wrote:
> Hi Kurt,
>
> Kurt Kanzenbach <kurt@linutronix.de> writes:
>
>> I think so. As Vladimir pointed out, the driver should setup an identity
>> mapping which I already did by default.
>>
>> Can you point me your patch?
>
> Just sent it for consideration:
>
> http://patchwork.ozlabs.org/project/netdev/patch/20200825174404.2727633-1-vinicius.gomes@intel.com/

Thank you. That looks good. So the driver just has to deal with queues
and I can setup an identity mapping in the hellcreek code.

I see the patch is already merged, otherwise I'd have acked it.

Thanks,
Kurt

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

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

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

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

Hi Andrew,

On Tue Aug 25 2020, Kurt Kanzenbach wrote:
> On Tue Aug 25 2020, Andrew Lunn wrote:
>> I agree with the check here. The question is about the compatible
>> string. Should there be a more specific compatible string as well as
>> the generic one?
>>
>> There have been a few discussions about how the Marvell DSA driver
>> does its compatible string. The compatible string tells you where to
>> find the ID register, not what value to expect in the ID register. The
>> ID register can currently be in one of three different locations. Do
>> all current and future Hellcreak devices have the same value for
>> HR_MODID_C?  If not, now is a good time to add a more specific
>> compatible string to tell you where to find the ID register.
>>
>>> My plan was to extend this when I have access to other
>>> revisions. There will be a SPI variant as well. But, I didn't want to
>>> implement it without the ability to test it.
>>
>> Does the SPI variant use the same value for HR_MODID_C?  Maybe you
>> need a different compatible, maybe not, depending on how the driver is
>> structured.
>>
>> The compatible string is part of the ABI. So thinking about it a bit
>> now can make things easier later. I just want to make sure you have
>> thought about this.
>
> I totally agree. The Marvell solution seems to work. For all current
> devices the module ID is located at 0x00. Depending on the chip ID the
> different properties can be configured later. The SPI variant will have
> a different module ID. Anyhow, I'll ask how this will be handled for
> future devices and in general.

After further discussion, we cannot use the Marvell solution.

So, the module id doesn't help us in determining anything about the
hardware. The module id can be arbitrarily chosen. A lot of the features
of the switch IP core are compile time options and cannot be read back
via registers. The hardware integrator can chose which features are
used, the precision of the get of the day for the ptp clock, the module
id, the port speed, additional debug options and so on.

All of this depends on how the IP core is integrated into the hardware
most likely an FPGA. My suggestion for now is to use different
compatible strings. Currently two variants exists:

 * An evaluation platform based on a Cyclone V based DE1 board from
   Terasic
 * The kairos chip (via SPI) which I mentioned in the cover letter

More variants are about to come with more ports, etc.

So, we would need different strings to distinguish between them. Proposal:

 * "hirschmann,hellcreek-de1soc-r1"
 * "hirschmann,hellcreek-ksp-rbrb-trsrr-r208"

And create some form of platform_data with:

 * module id
 * amount of ports
 * port speed
 * qbv support
 * ...

Any thoughts on this?

Thanks,
Kurt

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

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-25  9:55         ` Kurt Kanzenbach
@ 2020-08-27 16:25           ` Richard Cochran
  2020-08-28 12:31             ` Kurt Kanzenbach
  0 siblings, 1 reply; 44+ messages in thread
From: Richard Cochran @ 2020-08-27 16:25 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Vladimir Oltean, Andrew Lunn, Vivien Didelot, Florian Fainelli,
	David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Kamil Alkhouri, ilias.apalodimas

On Tue, Aug 25, 2020 at 11:55:37AM +0200, Kurt Kanzenbach wrote:
> 
> I get your point. But how to do it? We would need a timer based on the
> PTP clock in the switch.

Can't you use an hrtimer based on CLOCK_MONOTONIC?

I would expect the driver to work based solely on the device's clock.

Thanks,
Richard

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-27 16:25           ` Richard Cochran
@ 2020-08-28 12:31             ` Kurt Kanzenbach
  0 siblings, 0 replies; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-08-28 12:31 UTC (permalink / raw)
  To: Richard Cochran
  Cc: Vladimir Oltean, Andrew Lunn, Vivien Didelot, Florian Fainelli,
	David S. Miller, Jakub Kicinski, netdev, Rob Herring, devicetree,
	Sebastian Andrzej Siewior, Kamil Alkhouri, ilias.apalodimas

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

Hi Richard,

On Thu Aug 27 2020, Richard Cochran wrote:
> On Tue, Aug 25, 2020 at 11:55:37AM +0200, Kurt Kanzenbach wrote:
>> 
>> I get your point. But how to do it? We would need a timer based on the
>> PTP clock in the switch.
>
> Can't you use an hrtimer based on CLOCK_MONOTONIC?

When the switch and the Linux machine aren't synchronized, we would
calculate the difference between both systems and could arm the Linux
timer based on CLOCK_MONOTONIC. Given the fact that we eight seconds, it
would *probably* work when the ptp offset adjustments are in that range.

>
> I would expect the driver to work based solely on the device's clock.

Understood.

Thanks,
Kurt

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

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-08-25  9:32       ` Vladimir Oltean
@ 2020-09-01 14:20         ` Kurt Kanzenbach
  2020-09-01 14:47           ` Vladimir Oltean
  0 siblings, 1 reply; 44+ messages in thread
From: Kurt Kanzenbach @ 2020-09-01 14:20 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Vinicius Costa Gomes, 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: 1569 bytes --]

On Tue Aug 25 2020, Vladimir Oltean wrote:
> On Tue, Aug 25, 2020 at 11:23:56AM +0200, Kurt Kanzenbach wrote:
>> On Mon Aug 24 2020, Vinicius Costa Gomes wrote:
>> > Hi,
>> >
>> > Kurt Kanzenbach <kurt@linutronix.de> writes:
>> >
>> [snip]
>> >> +	/* Setup timer for schedule switch: The IP core only allows to set a
>> >> +	 * cycle start timer 8 seconds in the future. This is why we setup the
>> >> +	 * hritmer to base_time - 5 seconds. Then, we have enough time to
>> >> +	 * activate IP core's EST timer.
>> >> +	 */
>> >> +	start = ktime_sub_ns(schedule->base_time, (u64)5 * NSEC_PER_SEC);
>> >> +	hrtimer_start_range_ns(&hellcreek_port->cycle_start_timer, start,
>> >> +			       NSEC_PER_SEC, HRTIMER_MODE_ABS);
>> >
>> > If we are talking about seconds here, I don't think you need to use a
>> > hrtimer, you could use a workqueue/delayed_work. Should make things a
>> > bit simpler.
>> 
>> I've used hrtimers for one reason: The hrtimer provides a way to fire at
>> an absolute base time based on CLOCK_TAI. All the other facilities such
>> as workqueues, timer list timers, etc do not.
>
> That still doesn't justify the complexity of irqsave spinlocks and such.
> You could just as well schedule a workqueue from that hrtimer and have
> process context...

After giving this a bit more thought, it can be implemented by using
workqueues only. That ptp time is "cached" anyway the we could just
periodically check for the base time arrival. That should solve the
irqsave and the being synchronized problem.

Thanks,
Kurt

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

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

* Re: [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support
  2020-09-01 14:20         ` Kurt Kanzenbach
@ 2020-09-01 14:47           ` Vladimir Oltean
  0 siblings, 0 replies; 44+ messages in thread
From: Vladimir Oltean @ 2020-09-01 14:47 UTC (permalink / raw)
  To: Kurt Kanzenbach
  Cc: Vinicius Costa Gomes, 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, Sep 01, 2020 at 04:20:00PM +0200, Kurt Kanzenbach wrote:
>
> After giving this a bit more thought, it can be implemented by using
> workqueues only. That ptp time is "cached" anyway the we could just
> periodically check for the base time arrival. That should solve the
> irqsave and the being synchronized problem.
>
> Thanks,
> Kurt

Ok, this sounds simple enough. If the base-time is within 8 seconds of
the current PTP time, then apply the taprio configuration, otherwise
reschedule a delayed workqueue after N seconds (where N has what
value?).

If my math is correct, then N can't simply be the the delta between the
current PTP time and the (base-time minus 8 seconds) value - i.e. just
one schedule_delayed_work - because at large deltas, the PHC frequency
adjustment (+/- 6.25%) starts to matter. At maximum frequency, the PHC
can exceed the monotonic clock of the system by more than 8 seconds in
(8 * 100 / 6.25) = 128 seconds. So if the base-time is in the future by
more than 128 seconds and you plan for a single schedule_delayed_work,
there's a chance that you'll miss the window. And even if you try to
compensate using the current frequency adjustment, that's all that it is
- the current, instantaneous frequency adjustment, not the one from 128
seconds later.

How about N being half that delta? It's not ideal, since there would
need to be log2(delta) reschedules, but at least the error of the first
approximation won't propagate to the next, and the delta will keep
decreasing as time passes, therefore so will the error.

Thanks,
-Vladimir

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

end of thread, other threads:[~2020-09-01 14:48 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-20  8:11 [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Kurt Kanzenbach
2020-08-20  8:11 ` [PATCH v3 1/8] net: dsa: Add tag handling for Hirschmann Hellcreek switches Kurt Kanzenbach
2020-08-20  8:11 ` [PATCH v3 2/8] net: dsa: Add DSA driver " Kurt Kanzenbach
2020-08-24 22:44   ` Andrew Lunn
2020-08-25  9:07     ` Kurt Kanzenbach
2020-08-25 13:56       ` Andrew Lunn
2020-08-25 14:48         ` Kurt Kanzenbach
2020-08-27 10:29           ` Kurt Kanzenbach
2020-08-20  8:11 ` [PATCH v3 3/8] net: dsa: hellcreek: Add PTP clock support Kurt Kanzenbach
2020-08-20  8:11 ` [PATCH v3 4/8] net: dsa: hellcreek: Add support for hardware timestamping Kurt Kanzenbach
2020-08-20  8:11 ` [PATCH v3 5/8] net: dsa: hellcreek: Add TAPRIO offloading support Kurt Kanzenbach
2020-08-22 14:39   ` Vladimir Oltean
2020-08-24  6:10     ` Kurt Kanzenbach
2020-08-24 23:45       ` Vinicius Costa Gomes
2020-08-25  9:42         ` Kurt Kanzenbach
2020-08-25 17:58           ` Vinicius Costa Gomes
2020-08-27 10:12             ` Kurt Kanzenbach
2020-08-25  9:46         ` Vladimir Oltean
2020-08-25 10:09           ` Kurt Kanzenbach
2020-08-25 17:33           ` Vinicius Costa Gomes
2020-08-24 22:56   ` Vladimir Oltean
2020-08-25  9:33     ` Kurt Kanzenbach
2020-08-25  9:38       ` Vladimir Oltean
2020-08-25  9:55         ` Kurt Kanzenbach
2020-08-27 16:25           ` Richard Cochran
2020-08-28 12:31             ` Kurt Kanzenbach
2020-08-24 23:57   ` Vinicius Costa Gomes
2020-08-25  9:23     ` Kurt Kanzenbach
2020-08-25  9:32       ` Vladimir Oltean
2020-09-01 14:20         ` Kurt Kanzenbach
2020-09-01 14:47           ` Vladimir Oltean
2020-08-25 17:50       ` Vinicius Costa Gomes
2020-08-20  8:11 ` [PATCH v3 6/8] net: dsa: hellcreek: Add PTP status LEDs Kurt Kanzenbach
2020-08-24 22:50   ` Andrew Lunn
2020-08-20  8:11 ` [PATCH v3 7/8] dt-bindings: Add vendor prefix for Hirschmann Kurt Kanzenbach
2020-08-20  8:11 ` [PATCH v3 8/8] dt-bindings: net: dsa: Add documentation for Hellcreek switches Kurt Kanzenbach
2020-08-24 22:52   ` Andrew Lunn
2020-08-25 22:28   ` Rob Herring
2020-08-24 21:31 ` [PATCH v3 0/8] Hirschmann Hellcreek DSA driver Jakub Kicinski
2020-08-24 22:02   ` Vladimir Oltean
2020-08-24 22:35     ` David Miller
2020-08-24 22:57       ` Vladimir Oltean
2020-08-25 11:21       ` Kurt Kanzenbach
2020-08-25 17:14         ` Florian Fainelli

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