diff mbox series

[net-next,v3,02/12] net: dsa: add Renesas RZ/N1 switch tag driver

Message ID 20220504093000.132579-3-clement.leger@bootlin.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series add support for Renesas RZ/N1 ethernet subsystem devices | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 18 this patch: 18
netdev/cc_maintainers success CCed 9 of 9 maintainers
netdev/build_clang success Errors and warnings before: 17 this patch: 17
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 18 this patch: 18
netdev/checkpatch warning WARNING: 'ment' may be misspelled - perhaps 'meant'? WARNING: Prefer "GPL" over "GPL v2" - see commit bf7fbeeae6db ("module: Cure the MODULE_LICENSE "GPL" vs. "GPL v2" bogosity") WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 82 exceeds 80 columns WARNING: please write a help paragraph that fully describes the config symbol
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Clément Léger May 4, 2022, 9:29 a.m. UTC
The switch that is present on the Renesas RZ/N1 SoC uses a specific
VLAN value followed by 6 bytes which contains forwarding configuration.

Signed-off-by: Clément Léger <clement.leger@bootlin.com>
---
 include/net/dsa.h        |   2 +
 net/dsa/Kconfig          |   7 +++
 net/dsa/Makefile         |   1 +
 net/dsa/tag_rzn1_a5psw.c | 114 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 124 insertions(+)
 create mode 100644 net/dsa/tag_rzn1_a5psw.c

Comments

Vladimir Oltean May 4, 2022, 4 p.m. UTC | #1
On Wed, May 04, 2022 at 11:29:50AM +0200, Clément Léger wrote:
> The switch that is present on the Renesas RZ/N1 SoC uses a specific
> VLAN value followed by 6 bytes which contains forwarding configuration.
> 
> Signed-off-by: Clément Léger <clement.leger@bootlin.com>
> ---
>  include/net/dsa.h        |   2 +
>  net/dsa/Kconfig          |   7 +++
>  net/dsa/Makefile         |   1 +
>  net/dsa/tag_rzn1_a5psw.c | 114 +++++++++++++++++++++++++++++++++++++++
>  4 files changed, 124 insertions(+)
>  create mode 100644 net/dsa/tag_rzn1_a5psw.c
> 
> diff --git a/include/net/dsa.h b/include/net/dsa.h
> index d9da32aacbf1..9aaaa7deb102 100644
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -53,6 +53,7 @@ struct phylink_link_state;
>  #define DSA_TAG_PROTO_SJA1110_VALUE		23
>  #define DSA_TAG_PROTO_RTL8_4_VALUE		24
>  #define DSA_TAG_PROTO_RTL8_4T_VALUE		25
> +#define DSA_TAG_PROTO_RZN1_A5PSW_VALUE		26
>  
>  enum dsa_tag_protocol {
>  	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
> @@ -81,6 +82,7 @@ enum dsa_tag_protocol {
>  	DSA_TAG_PROTO_SJA1110		= DSA_TAG_PROTO_SJA1110_VALUE,
>  	DSA_TAG_PROTO_RTL8_4		= DSA_TAG_PROTO_RTL8_4_VALUE,
>  	DSA_TAG_PROTO_RTL8_4T		= DSA_TAG_PROTO_RTL8_4T_VALUE,
> +	DSA_TAG_PROTO_RZN1_A5PSW	= DSA_TAG_PROTO_RZN1_A5PSW_VALUE,
>  };
>  
>  struct dsa_switch;
> diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
> index 8cb87b5067ee..e22f33d7cf60 100644
> --- a/net/dsa/Kconfig
> +++ b/net/dsa/Kconfig
> @@ -132,6 +132,13 @@ config NET_DSA_TAG_RTL8_4
>  	  Say Y or M if you want to enable support for tagging frames for Realtek
>  	  switches with 8 byte protocol 4 tags, such as the Realtek RTL8365MB-VC.
>  
> +config NET_DSA_TAG_RZN1_A5PSW
> +	tristate "Tag driver for Renesas RZ/N1 A5PSW switch"
> +	help
> +	  Say Y or M if you want to enable support for tagging frames for
> +	  Renesas RZ/N1 embedded switch that uses a 8 byte tag located after

"an" 8 byte tag

> +	  destination MAC address.
> +
>  config NET_DSA_TAG_LAN9303
>  	tristate "Tag driver for SMSC/Microchip LAN9303 family of switches"
>  	help
> diff --git a/net/dsa/Makefile b/net/dsa/Makefile
> index 9f75820e7c98..af28c24ead18 100644
> --- a/net/dsa/Makefile
> +++ b/net/dsa/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o
>  obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
>  obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o
>  obj-$(CONFIG_NET_DSA_TAG_RTL8_4) += tag_rtl8_4.o
> +obj-$(CONFIG_NET_DSA_TAG_RZN1_A5PSW) += tag_rzn1_a5psw.o
>  obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
>  obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
>  obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o
> diff --git a/net/dsa/tag_rzn1_a5psw.c b/net/dsa/tag_rzn1_a5psw.c
> new file mode 100644
> index 000000000000..87177682eb54
> --- /dev/null
> +++ b/net/dsa/tag_rzn1_a5psw.c
> @@ -0,0 +1,114 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2022 Schneider Electric
> + *
> + * Clément Léger <clement.leger@bootlin.com>
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/etherdevice.h>
> +#include <net/dsa.h>
> +
> +#include "dsa_priv.h"
> +
> +/* To define the outgoing port and to discover the incoming port a TAG is
> + * inserted after Src MAC :
> + *
> + *       Dest MAC       Src MAC           TAG         Type
> + * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 7 8 | 1 2 |...
> + *                                |<--------------->|
> + *
> + * See struct a5psw_tag for layout
> + */
> +
> +#define A5PSW_TAG_VALUE			0xE001

Maybe an ETH_P_DSA_A5PSW definition in include/uapi/linux/if_ether.h
would be appropriate? I'm not sure.

> +#define A5PSW_TAG_LEN			8
> +#define A5PSW_CTRL_DATA_FORCE_FORWARD	BIT(0)
> +/* This is both used for xmit tag and rcv tagging */
> +#define A5PSW_CTRL_DATA_PORT		GENMASK(3, 0)
> +
> +struct a5psw_tag {
> +	__be16 ctrl_tag;
> +	__be16 ctrl_data;
> +	__be16 ctrl_data2_hi;
> +	__be16 ctrl_data2_lo;
> +};
> +
> +static struct sk_buff *a5psw_tag_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct dsa_port *dp = dsa_slave_to_port(dev);
> +	struct a5psw_tag *ptag;
> +	u32 data2_val;
> +
> +	BUILD_BUG_ON(sizeof(*ptag) != A5PSW_TAG_LEN);
> +
> +	/* The Ethernet switch we are interfaced with needs packets to be at
> +	 * least 64 bytes (including FCS) otherwise they will be discarded when
> +	 * they enter the switch port logic. When tagging is enabled, we need
> +	 * to make sure that packets are at least 70 bytes (including FCS and
> +	 * tag).
> +	 */
> +	if (__skb_put_padto(skb, ETH_ZLEN + ETH_FCS_LEN + A5PSW_TAG_LEN, false))
> +		return NULL;

I'm confused by the inclusion of the FCS length in this calculation,
since the FCS space isn't present in the skb buffer as far as I know?

"64 bytes including FCS" means "60 bytes excluding FCS".
And ETH_ZLEN is 60...

And I'm also not sure how we got to the number 70? A5PSW_TAG_LEN is 8.
If we add it to 60 we get 68. If we add it to 64 we get 72?

> +
> +	/* provide 'A5PSW_TAG_LEN' bytes additional space */
> +	skb_push(skb, A5PSW_TAG_LEN);
> +
> +	/* make room between MACs and Ether-Type to insert tag */
> +	dsa_alloc_etype_header(skb, A5PSW_TAG_LEN);
> +
> +	ptag = dsa_etype_header_pos_tx(skb);
> +
> +	data2_val = FIELD_PREP(A5PSW_CTRL_DATA_PORT, BIT(dp->index));
> +	ptag->ctrl_tag = htons(A5PSW_TAG_VALUE);
> +	ptag->ctrl_data = htons(A5PSW_CTRL_DATA_FORCE_FORWARD);
> +	ptag->ctrl_data2_lo = htons(data2_val);
> +	ptag->ctrl_data2_hi = 0;
> +
> +	return skb;
> +}
> +
> +static struct sk_buff *a5psw_tag_rcv(struct sk_buff *skb,
> +				     struct net_device *dev)
> +{
> +	struct a5psw_tag *tag;
> +	int port;
> +
> +	if (unlikely(!pskb_may_pull(skb, A5PSW_TAG_LEN))) {
> +		dev_warn_ratelimited(&dev->dev,
> +				     "Dropping packet, cannot pull\n");
> +		return NULL;
> +	}
> +
> +	tag = dsa_etype_header_pos_rx(skb);
> +
> +	if (tag->ctrl_tag != htons(A5PSW_TAG_VALUE)) {
> +		dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid TAG marker\n");
> +		return NULL;
> +	}
> +
> +	port = FIELD_GET(A5PSW_CTRL_DATA_PORT, ntohs(tag->ctrl_data));
> +
> +	skb->dev = dsa_master_find_slave(dev, 0, port);
> +	if (!skb->dev)
> +		return NULL;
> +
> +	skb_pull_rcsum(skb, A5PSW_TAG_LEN);
> +	dsa_strip_etype_header(skb, A5PSW_TAG_LEN);
> +
> +	dsa_default_offload_fwd_mark(skb);
> +
> +	return skb;
> +}
> +
> +static const struct dsa_device_ops a5psw_netdev_ops = {
> +	.name	= "a5psw",
> +	.proto	= DSA_TAG_PROTO_RZN1_A5PSW,
> +	.xmit	= a5psw_tag_xmit,
> +	.rcv	= a5psw_tag_rcv,
> +	.needed_headroom = A5PSW_TAG_LEN,
> +};
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_A5PSW);
> +module_dsa_tag_driver(a5psw_netdev_ops);
> -- 
> 2.34.1
>
Clément Léger May 5, 2022, 1:48 p.m. UTC | #2
Le Wed, 4 May 2022 19:00:39 +0300,
Vladimir Oltean <olteanv@gmail.com> a écrit :

> > +/* To define the outgoing port and to discover the incoming port a TAG is
> > + * inserted after Src MAC :
> > + *
> > + *       Dest MAC       Src MAC           TAG         Type
> > + * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 7 8 | 1 2 |...
> > + *                                |<--------------->|
> > + *
> > + * See struct a5psw_tag for layout
> > + */
> > +
> > +#define A5PSW_TAG_VALUE			0xE001  
> 
> Maybe an ETH_P_DSA_A5PSW definition in include/uapi/linux/if_ether.h
> would be appropriate? I'm not sure.

That's a good question. Actually, this value is the default 
but is configurable in the hardware so I'm not sure this should be a
reserved value. Maybe it would make sense to add it anyway to have the
define shared between the switch driver and the tag driver.

> 
> > +#define A5PSW_TAG_LEN			8
> > +#define A5PSW_CTRL_DATA_FORCE_FORWARD	BIT(0)
> > +/* This is both used for xmit tag and rcv tagging */
> > +#define A5PSW_CTRL_DATA_PORT		GENMASK(3, 0)
> > +
> > +struct a5psw_tag {
> > +	__be16 ctrl_tag;
> > +	__be16 ctrl_data;
> > +	__be16 ctrl_data2_hi;
> > +	__be16 ctrl_data2_lo;
> > +};
> > +
> > +static struct sk_buff *a5psw_tag_xmit(struct sk_buff *skb, struct net_device *dev)
> > +{
> > +	struct dsa_port *dp = dsa_slave_to_port(dev);
> > +	struct a5psw_tag *ptag;
> > +	u32 data2_val;
> > +
> > +	BUILD_BUG_ON(sizeof(*ptag) != A5PSW_TAG_LEN);
> > +
> > +	/* The Ethernet switch we are interfaced with needs packets to be at
> > +	 * least 64 bytes (including FCS) otherwise they will be discarded when
> > +	 * they enter the switch port logic. When tagging is enabled, we need
> > +	 * to make sure that packets are at least 70 bytes (including FCS and
> > +	 * tag).
> > +	 */
> > +	if (__skb_put_padto(skb, ETH_ZLEN + ETH_FCS_LEN + A5PSW_TAG_LEN, false))
> > +		return NULL;  
> 
> I'm confused by the inclusion of the FCS length in this calculation,
> since the FCS space isn't present in the skb buffer as far as I know?

I'm not sure either, the documentation is not really clear on what is
the requirement for the minimal size of a packet. This was the closest
thing I could find about that requirement:

"A frame has a valid length if it contains at least 64 octets and does
not exceed the programmed maximum length"

And the figure associated to the frame show that the frame length
includes the FCS which lead to a 64bytes frame.
> 
> "64 bytes including FCS" means "60 bytes excluding FCS".
> And ETH_ZLEN is 60...
> 
> And I'm also not sure how we got to the number 70? A5PSW_TAG_LEN is 8.
> If we add it to 60 we get 68. If we add it to 64 we get 72?
>

I'll check all these numbers and find the correct size that is expected
by the switch.
diff mbox series

Patch

diff --git a/include/net/dsa.h b/include/net/dsa.h
index d9da32aacbf1..9aaaa7deb102 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -53,6 +53,7 @@  struct phylink_link_state;
 #define DSA_TAG_PROTO_SJA1110_VALUE		23
 #define DSA_TAG_PROTO_RTL8_4_VALUE		24
 #define DSA_TAG_PROTO_RTL8_4T_VALUE		25
+#define DSA_TAG_PROTO_RZN1_A5PSW_VALUE		26
 
 enum dsa_tag_protocol {
 	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
@@ -81,6 +82,7 @@  enum dsa_tag_protocol {
 	DSA_TAG_PROTO_SJA1110		= DSA_TAG_PROTO_SJA1110_VALUE,
 	DSA_TAG_PROTO_RTL8_4		= DSA_TAG_PROTO_RTL8_4_VALUE,
 	DSA_TAG_PROTO_RTL8_4T		= DSA_TAG_PROTO_RTL8_4T_VALUE,
+	DSA_TAG_PROTO_RZN1_A5PSW	= DSA_TAG_PROTO_RZN1_A5PSW_VALUE,
 };
 
 struct dsa_switch;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 8cb87b5067ee..e22f33d7cf60 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -132,6 +132,13 @@  config NET_DSA_TAG_RTL8_4
 	  Say Y or M if you want to enable support for tagging frames for Realtek
 	  switches with 8 byte protocol 4 tags, such as the Realtek RTL8365MB-VC.
 
+config NET_DSA_TAG_RZN1_A5PSW
+	tristate "Tag driver for Renesas RZ/N1 A5PSW switch"
+	help
+	  Say Y or M if you want to enable support for tagging frames for
+	  Renesas RZ/N1 embedded switch that uses a 8 byte tag located after
+	  destination MAC address.
+
 config NET_DSA_TAG_LAN9303
 	tristate "Tag driver for SMSC/Microchip LAN9303 family of switches"
 	help
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index 9f75820e7c98..af28c24ead18 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -17,6 +17,7 @@  obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o
 obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
 obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o
 obj-$(CONFIG_NET_DSA_TAG_RTL8_4) += tag_rtl8_4.o
+obj-$(CONFIG_NET_DSA_TAG_RZN1_A5PSW) += tag_rzn1_a5psw.o
 obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
 obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
 obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o
diff --git a/net/dsa/tag_rzn1_a5psw.c b/net/dsa/tag_rzn1_a5psw.c
new file mode 100644
index 000000000000..87177682eb54
--- /dev/null
+++ b/net/dsa/tag_rzn1_a5psw.c
@@ -0,0 +1,114 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 Schneider Electric
+ *
+ * Clément Léger <clement.leger@bootlin.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/etherdevice.h>
+#include <net/dsa.h>
+
+#include "dsa_priv.h"
+
+/* To define the outgoing port and to discover the incoming port a TAG is
+ * inserted after Src MAC :
+ *
+ *       Dest MAC       Src MAC           TAG         Type
+ * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 7 8 | 1 2 |...
+ *                                |<--------------->|
+ *
+ * See struct a5psw_tag for layout
+ */
+
+#define A5PSW_TAG_VALUE			0xE001
+#define A5PSW_TAG_LEN			8
+#define A5PSW_CTRL_DATA_FORCE_FORWARD	BIT(0)
+/* This is both used for xmit tag and rcv tagging */
+#define A5PSW_CTRL_DATA_PORT		GENMASK(3, 0)
+
+struct a5psw_tag {
+	__be16 ctrl_tag;
+	__be16 ctrl_data;
+	__be16 ctrl_data2_hi;
+	__be16 ctrl_data2_lo;
+};
+
+static struct sk_buff *a5psw_tag_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct dsa_port *dp = dsa_slave_to_port(dev);
+	struct a5psw_tag *ptag;
+	u32 data2_val;
+
+	BUILD_BUG_ON(sizeof(*ptag) != A5PSW_TAG_LEN);
+
+	/* The Ethernet switch we are interfaced with needs packets to be at
+	 * least 64 bytes (including FCS) otherwise they will be discarded when
+	 * they enter the switch port logic. When tagging is enabled, we need
+	 * to make sure that packets are at least 70 bytes (including FCS and
+	 * tag).
+	 */
+	if (__skb_put_padto(skb, ETH_ZLEN + ETH_FCS_LEN + A5PSW_TAG_LEN, false))
+		return NULL;
+
+	/* provide 'A5PSW_TAG_LEN' bytes additional space */
+	skb_push(skb, A5PSW_TAG_LEN);
+
+	/* make room between MACs and Ether-Type to insert tag */
+	dsa_alloc_etype_header(skb, A5PSW_TAG_LEN);
+
+	ptag = dsa_etype_header_pos_tx(skb);
+
+	data2_val = FIELD_PREP(A5PSW_CTRL_DATA_PORT, BIT(dp->index));
+	ptag->ctrl_tag = htons(A5PSW_TAG_VALUE);
+	ptag->ctrl_data = htons(A5PSW_CTRL_DATA_FORCE_FORWARD);
+	ptag->ctrl_data2_lo = htons(data2_val);
+	ptag->ctrl_data2_hi = 0;
+
+	return skb;
+}
+
+static struct sk_buff *a5psw_tag_rcv(struct sk_buff *skb,
+				     struct net_device *dev)
+{
+	struct a5psw_tag *tag;
+	int port;
+
+	if (unlikely(!pskb_may_pull(skb, A5PSW_TAG_LEN))) {
+		dev_warn_ratelimited(&dev->dev,
+				     "Dropping packet, cannot pull\n");
+		return NULL;
+	}
+
+	tag = dsa_etype_header_pos_rx(skb);
+
+	if (tag->ctrl_tag != htons(A5PSW_TAG_VALUE)) {
+		dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid TAG marker\n");
+		return NULL;
+	}
+
+	port = FIELD_GET(A5PSW_CTRL_DATA_PORT, ntohs(tag->ctrl_data));
+
+	skb->dev = dsa_master_find_slave(dev, 0, port);
+	if (!skb->dev)
+		return NULL;
+
+	skb_pull_rcsum(skb, A5PSW_TAG_LEN);
+	dsa_strip_etype_header(skb, A5PSW_TAG_LEN);
+
+	dsa_default_offload_fwd_mark(skb);
+
+	return skb;
+}
+
+static const struct dsa_device_ops a5psw_netdev_ops = {
+	.name	= "a5psw",
+	.proto	= DSA_TAG_PROTO_RZN1_A5PSW,
+	.xmit	= a5psw_tag_xmit,
+	.rcv	= a5psw_tag_rcv,
+	.needed_headroom = A5PSW_TAG_LEN,
+};
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_A5PSW);
+module_dsa_tag_driver(a5psw_netdev_ops);