net: dsa: add support for Atheros AR9331 TAG format
authorOleksij Rempel <o.rempel@pengutronix.de>
Wed, 18 Dec 2019 08:02:14 +0000 (09:02 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sat, 21 Dec 2019 01:05:47 +0000 (17:05 -0800)
Add support for tag format used in Atheros AR9331 built-in switch.

Reviewed-by: Vivien Didelot <vivien.didelot@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/dsa.h
net/dsa/Kconfig
net/dsa/Makefile
net/dsa/tag_ar9331.c [new file with mode: 0644]

index 6767dc3..da5578d 100644 (file)
@@ -43,6 +43,7 @@ struct phylink_link_state;
 #define DSA_TAG_PROTO_SJA1105_VALUE            13
 #define DSA_TAG_PROTO_KSZ8795_VALUE            14
 #define DSA_TAG_PROTO_OCELOT_VALUE             15
+#define DSA_TAG_PROTO_AR9331_VALUE             16
 
 enum dsa_tag_protocol {
        DSA_TAG_PROTO_NONE              = DSA_TAG_PROTO_NONE_VALUE,
@@ -61,6 +62,7 @@ enum dsa_tag_protocol {
        DSA_TAG_PROTO_SJA1105           = DSA_TAG_PROTO_SJA1105_VALUE,
        DSA_TAG_PROTO_KSZ8795           = DSA_TAG_PROTO_KSZ8795_VALUE,
        DSA_TAG_PROTO_OCELOT            = DSA_TAG_PROTO_OCELOT_VALUE,
+       DSA_TAG_PROTO_AR9331            = DSA_TAG_PROTO_AR9331_VALUE,
 };
 
 struct packet_type;
index 1e6c3ca..92663dc 100644 (file)
@@ -29,6 +29,12 @@ config NET_DSA_TAG_8021Q
 
          Drivers which use these helpers should select this as dependency.
 
+config NET_DSA_TAG_AR9331
+       tristate "Tag driver for Atheros AR9331 SoC with built-in switch"
+       help
+         Say Y or M if you want to enable support for tagging frames for
+         the Atheros AR9331 SoC with built-in switch.
+
 config NET_DSA_TAG_BRCM_COMMON
        tristate
        default n
index 9a482c3..108486c 100644 (file)
@@ -5,6 +5,7 @@ dsa_core-y += dsa.o dsa2.o master.o port.o slave.o switch.o
 
 # tagging formats
 obj-$(CONFIG_NET_DSA_TAG_8021Q) += tag_8021q.o
+obj-$(CONFIG_NET_DSA_TAG_AR9331) += tag_ar9331.o
 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
diff --git a/net/dsa/tag_ar9331.c b/net/dsa/tag_ar9331.c
new file mode 100644 (file)
index 0000000..466ffa9
--- /dev/null
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
+ */
+
+
+#include <linux/bitfield.h>
+#include <linux/etherdevice.h>
+
+#include "dsa_priv.h"
+
+#define AR9331_HDR_LEN                 2
+#define AR9331_HDR_VERSION             1
+
+#define AR9331_HDR_VERSION_MASK                GENMASK(15, 14)
+#define AR9331_HDR_PRIORITY_MASK       GENMASK(13, 12)
+#define AR9331_HDR_TYPE_MASK           GENMASK(10, 8)
+#define AR9331_HDR_BROADCAST           BIT(7)
+#define AR9331_HDR_FROM_CPU            BIT(6)
+/* AR9331_HDR_RESERVED - not used or may be version field.
+ * According to the AR8216 doc it should 0b10. On AR9331 it is 0b11 on RX path
+ * and should be set to 0b11 to make it work.
+ */
+#define AR9331_HDR_RESERVED_MASK       GENMASK(5, 4)
+#define AR9331_HDR_PORT_NUM_MASK       GENMASK(3, 0)
+
+static struct sk_buff *ar9331_tag_xmit(struct sk_buff *skb,
+                                      struct net_device *dev)
+{
+       struct dsa_port *dp = dsa_slave_to_port(dev);
+       __le16 *phdr;
+       u16 hdr;
+
+       if (skb_cow_head(skb, 0) < 0)
+               return NULL;
+
+       phdr = skb_push(skb, AR9331_HDR_LEN);
+
+       hdr = FIELD_PREP(AR9331_HDR_VERSION_MASK, AR9331_HDR_VERSION);
+       hdr |= AR9331_HDR_FROM_CPU | dp->index;
+       /* 0b10 for AR8216 and 0b11 for AR9331 */
+       hdr |= AR9331_HDR_RESERVED_MASK;
+
+       phdr[0] = cpu_to_le16(hdr);
+
+       return skb;
+}
+
+static struct sk_buff *ar9331_tag_rcv(struct sk_buff *skb,
+                                     struct net_device *ndev,
+                                     struct packet_type *pt)
+{
+       u8 ver, port;
+       u16 hdr;
+
+       if (unlikely(!pskb_may_pull(skb, AR9331_HDR_LEN)))
+               return NULL;
+
+       hdr = le16_to_cpu(*(__le16 *)skb_mac_header(skb));
+
+       ver = FIELD_GET(AR9331_HDR_VERSION_MASK, hdr);
+       if (unlikely(ver != AR9331_HDR_VERSION)) {
+               netdev_warn_once(ndev, "%s:%i wrong header version 0x%2x\n",
+                                __func__, __LINE__, hdr);
+               return NULL;
+       }
+
+       if (unlikely(hdr & AR9331_HDR_FROM_CPU)) {
+               netdev_warn_once(ndev, "%s:%i packet should not be from cpu 0x%2x\n",
+                                __func__, __LINE__, hdr);
+               return NULL;
+       }
+
+       skb_pull_rcsum(skb, AR9331_HDR_LEN);
+
+       /* Get source port information */
+       port = FIELD_GET(AR9331_HDR_PORT_NUM_MASK, hdr);
+
+       skb->dev = dsa_master_find_slave(ndev, 0, port);
+       if (!skb->dev)
+               return NULL;
+
+       return skb;
+}
+
+static const struct dsa_device_ops ar9331_netdev_ops = {
+       .name   = "ar9331",
+       .proto  = DSA_TAG_PROTO_AR9331,
+       .xmit   = ar9331_tag_xmit,
+       .rcv    = ar9331_tag_rcv,
+       .overhead = AR9331_HDR_LEN,
+};
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_AR9331);
+module_dsa_tag_driver(ar9331_netdev_ops);