You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
332 lines
14 KiB
Diff
332 lines
14 KiB
Diff
From 0254efd8d7e8f533b57bdf8665991fd5548c65a8 Mon Sep 17 00:00:00 2001
|
|
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
|
Date: Thu, 14 Nov 2019 17:03:29 +0200
|
|
Subject: [PATCH] net: dsa: ocelot: add tagger for Ocelot/Felix switches
|
|
|
|
While it is entirely possible that this tagger format is in fact more
|
|
generic than just these 2 switch families, I don't have that knowledge.
|
|
The Seville switch in NXP T1040 has a similar frame format, but there
|
|
are enough differences (e.g. DEST field starts at bit 57 instead of 56)
|
|
that calling this file tag_vitesse.c is a bit of a stretch at the
|
|
moment. The frame format has been listed in a comment so that people who
|
|
add support for further Vitesse switches can rework this tagger while
|
|
keeping compatibility with Felix.
|
|
|
|
The "ocelot" name was chosen instead of "felix" because even the Ocelot
|
|
switch can act as a DSA device when it is used in NPI mode, and the Felix
|
|
tagger format is almost identical. Currently it is only used for the
|
|
Felix switch embedded in the NXP LS1028A chip.
|
|
|
|
The ABI for this tagger should be considered "not stable" at the moment.
|
|
The DSA tag is always placed before the Ethernet header and therefore,
|
|
we are using the long prefix for RX tags to avoid putting the DSA master
|
|
port in promiscuous mode. Once there will be an API in DSA for drivers
|
|
to request DSA masters to be in promiscuous mode unconditionally, we
|
|
will switch to the "no prefix" extraction frame header, which will save
|
|
16 padding bytes for each RX frame.
|
|
|
|
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
|
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
|
|
Signed-off-by: David S. Miller <davem@davemloft.net>
|
|
---
|
|
MAINTAINERS | 7 ++
|
|
include/net/dsa.h | 2 +
|
|
net/dsa/Kconfig | 7 ++
|
|
net/dsa/Makefile | 1 +
|
|
net/dsa/tag_ocelot.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
5 files changed, 246 insertions(+)
|
|
create mode 100644 net/dsa/tag_ocelot.c
|
|
|
|
--- a/MAINTAINERS
|
|
+++ b/MAINTAINERS
|
|
@@ -17354,6 +17354,13 @@ S: Maintained
|
|
F: drivers/input/serio/userio.c
|
|
F: include/uapi/linux/userio.h
|
|
|
|
+VITESSE FELIX ETHERNET SWITCH DRIVER
|
|
+M: Vladimir Oltean <vladimir.oltean@nxp.com>
|
|
+M: Claudiu Manoil <claudiu.manoil@nxp.com>
|
|
+L: netdev@vger.kernel.org
|
|
+S: Maintained
|
|
+F: net/dsa/tag_ocelot.c
|
|
+
|
|
VIVID VIRTUAL VIDEO DRIVER
|
|
M: Hans Verkuil <hverkuil@xs4all.nl>
|
|
L: linux-media@vger.kernel.org
|
|
--- a/include/net/dsa.h
|
|
+++ b/include/net/dsa.h
|
|
@@ -42,6 +42,7 @@ struct phylink_link_state;
|
|
#define DSA_TAG_PROTO_8021Q_VALUE 12
|
|
#define DSA_TAG_PROTO_SJA1105_VALUE 13
|
|
#define DSA_TAG_PROTO_KSZ8795_VALUE 14
|
|
+#define DSA_TAG_PROTO_OCELOT_VALUE 15
|
|
|
|
enum dsa_tag_protocol {
|
|
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
|
|
@@ -59,6 +60,7 @@ enum dsa_tag_protocol {
|
|
DSA_TAG_PROTO_8021Q = DSA_TAG_PROTO_8021Q_VALUE,
|
|
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,
|
|
};
|
|
|
|
struct packet_type;
|
|
--- a/net/dsa/Kconfig
|
|
+++ b/net/dsa/Kconfig
|
|
@@ -80,6 +80,13 @@ config NET_DSA_TAG_KSZ
|
|
Say Y if you want to enable support for tagging frames for the
|
|
Microchip 8795/9477/9893 families of switches.
|
|
|
|
+config NET_DSA_TAG_OCELOT
|
|
+ tristate "Tag driver for Ocelot family of switches"
|
|
+ select PACKING
|
|
+ help
|
|
+ Say Y or M if you want to enable support for tagging frames for the
|
|
+ Ocelot switches (VSC7511, VSC7512, VSC7513, VSC7514, VSC9959).
|
|
+
|
|
config NET_DSA_TAG_QCA
|
|
tristate "Tag driver for Qualcomm Atheros QCA8K switches"
|
|
help
|
|
--- a/net/dsa/Makefile
|
|
+++ b/net/dsa/Makefile
|
|
@@ -12,6 +12,7 @@ obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_g
|
|
obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
|
|
obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
|
|
obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
|
|
+obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
|
|
obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
|
|
obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
|
|
obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
|
|
--- /dev/null
|
|
+++ b/net/dsa/tag_ocelot.c
|
|
@@ -0,0 +1,229 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/* Copyright 2019 NXP Semiconductors
|
|
+ */
|
|
+#include <soc/mscc/ocelot.h>
|
|
+#include <linux/packing.h>
|
|
+#include "dsa_priv.h"
|
|
+
|
|
+/* The CPU injection header and the CPU extraction header can have 3 types of
|
|
+ * prefixes: long, short and no prefix. The format of the header itself is the
|
|
+ * same in all 3 cases.
|
|
+ *
|
|
+ * Extraction with long prefix:
|
|
+ *
|
|
+ * +-------------------+-------------------+------+------+------------+-------+
|
|
+ * | ff:ff:ff:ff:ff:ff | ff:ff:ff:ff:ff:ff | 8880 | 000a | extraction | frame |
|
|
+ * | | | | | header | |
|
|
+ * +-------------------+-------------------+------+------+------------+-------+
|
|
+ * 48 bits 48 bits 16 bits 16 bits 128 bits
|
|
+ *
|
|
+ * Extraction with short prefix:
|
|
+ *
|
|
+ * +------+------+------------+-------+
|
|
+ * | 8880 | 000a | extraction | frame |
|
|
+ * | | | header | |
|
|
+ * +------+------+------------+-------+
|
|
+ * 16 bits 16 bits 128 bits
|
|
+ *
|
|
+ * Extraction with no prefix:
|
|
+ *
|
|
+ * +------------+-------+
|
|
+ * | extraction | frame |
|
|
+ * | header | |
|
|
+ * +------------+-------+
|
|
+ * 128 bits
|
|
+ *
|
|
+ *
|
|
+ * Injection with long prefix:
|
|
+ *
|
|
+ * +-------------------+-------------------+------+------+------------+-------+
|
|
+ * | any dmac | any smac | 8880 | 000a | injection | frame |
|
|
+ * | | | | | header | |
|
|
+ * +-------------------+-------------------+------+------+------------+-------+
|
|
+ * 48 bits 48 bits 16 bits 16 bits 128 bits
|
|
+ *
|
|
+ * Injection with short prefix:
|
|
+ *
|
|
+ * +------+------+------------+-------+
|
|
+ * | 8880 | 000a | injection | frame |
|
|
+ * | | | header | |
|
|
+ * +------+------+------------+-------+
|
|
+ * 16 bits 16 bits 128 bits
|
|
+ *
|
|
+ * Injection with no prefix:
|
|
+ *
|
|
+ * +------------+-------+
|
|
+ * | injection | frame |
|
|
+ * | header | |
|
|
+ * +------------+-------+
|
|
+ * 128 bits
|
|
+ *
|
|
+ * The injection header looks like this (network byte order, bit 127
|
|
+ * is part of lowest address byte in memory, bit 0 is part of highest
|
|
+ * address byte):
|
|
+ *
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 127:120 |BYPASS| MASQ | MASQ_PORT |REW_OP|REW_OP|
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 119:112 | REW_OP |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 111:104 | REW_VAL |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 103: 96 | REW_VAL |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 95: 88 | REW_VAL |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 87: 80 | REW_VAL |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 79: 72 | RSV |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 71: 64 | RSV | DEST |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 63: 56 | DEST |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 55: 48 | RSV |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 47: 40 | RSV | SRC_PORT | RSV |TFRM_TIMER|
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 39: 32 | TFRM_TIMER | RSV |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 31: 24 | RSV | DP | POP_CNT | CPUQ |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 23: 16 | CPUQ | QOS_CLASS |TAG_TYPE|
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 15: 8 | PCP | DEI | VID |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 7: 0 | VID |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ *
|
|
+ * And the extraction header looks like this:
|
|
+ *
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 127:120 | RSV | REW_OP |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 119:112 | REW_OP | REW_VAL |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 111:104 | REW_VAL |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 103: 96 | REW_VAL |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 95: 88 | REW_VAL |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 87: 80 | REW_VAL | LLEN |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 79: 72 | LLEN | WLEN |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 71: 64 | WLEN | RSV |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 63: 56 | RSV |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 55: 48 | RSV |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 47: 40 | RSV | SRC_PORT | ACL_ID |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 39: 32 | ACL_ID | RSV | SFLOW_ID |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 31: 24 |ACL_HIT| DP | LRN_FLAGS | CPUQ |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 23: 16 | CPUQ | QOS_CLASS |TAG_TYPE|
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 15: 8 | PCP | DEI | VID |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ * 7: 0 | VID |
|
|
+ * +------+------+------+------+------+------+------+------+
|
|
+ */
|
|
+
|
|
+static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
|
|
+ struct net_device *netdev)
|
|
+{
|
|
+ struct dsa_port *dp = dsa_slave_to_port(netdev);
|
|
+ u64 bypass, dest, src, qos_class;
|
|
+ struct dsa_switch *ds = dp->ds;
|
|
+ int port = dp->index;
|
|
+ u8 *injection;
|
|
+
|
|
+ if (unlikely(skb_cow_head(skb, OCELOT_TAG_LEN) < 0)) {
|
|
+ netdev_err(netdev, "Cannot make room for tag.\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ injection = skb_push(skb, OCELOT_TAG_LEN);
|
|
+
|
|
+ memset(injection, 0, OCELOT_TAG_LEN);
|
|
+
|
|
+ src = dsa_upstream_port(ds, port);
|
|
+ dest = BIT(port);
|
|
+ bypass = true;
|
|
+ qos_class = skb->priority;
|
|
+
|
|
+ packing(injection, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0);
|
|
+ packing(injection, &dest, 68, 56, OCELOT_TAG_LEN, PACK, 0);
|
|
+ packing(injection, &src, 46, 43, OCELOT_TAG_LEN, PACK, 0);
|
|
+ packing(injection, &qos_class, 19, 17, OCELOT_TAG_LEN, PACK, 0);
|
|
+
|
|
+ return skb;
|
|
+}
|
|
+
|
|
+static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
|
|
+ struct net_device *netdev,
|
|
+ struct packet_type *pt)
|
|
+{
|
|
+ u64 src_port, qos_class;
|
|
+ u8 *start = skb->data;
|
|
+ u8 *extraction;
|
|
+
|
|
+ /* Revert skb->data by the amount consumed by the DSA master,
|
|
+ * so it points to the beginning of the frame.
|
|
+ */
|
|
+ skb_push(skb, ETH_HLEN);
|
|
+ /* We don't care about the long prefix, it is just for easy entrance
|
|
+ * into the DSA master's RX filter. Discard it now by moving it into
|
|
+ * the headroom.
|
|
+ */
|
|
+ skb_pull(skb, OCELOT_LONG_PREFIX_LEN);
|
|
+ /* And skb->data now points to the extraction frame header.
|
|
+ * Keep a pointer to it.
|
|
+ */
|
|
+ extraction = skb->data;
|
|
+ /* Now the EFH is part of the headroom as well */
|
|
+ skb_pull(skb, OCELOT_TAG_LEN);
|
|
+ /* Reset the pointer to the real MAC header */
|
|
+ skb_reset_mac_header(skb);
|
|
+ skb_reset_mac_len(skb);
|
|
+ /* And move skb->data to the correct location again */
|
|
+ skb_pull(skb, ETH_HLEN);
|
|
+
|
|
+ /* Remove from inet csum the extraction header */
|
|
+ skb_postpull_rcsum(skb, start, OCELOT_LONG_PREFIX_LEN + OCELOT_TAG_LEN);
|
|
+
|
|
+ packing(extraction, &src_port, 46, 43, OCELOT_TAG_LEN, UNPACK, 0);
|
|
+ packing(extraction, &qos_class, 19, 17, OCELOT_TAG_LEN, UNPACK, 0);
|
|
+
|
|
+ skb->dev = dsa_master_find_slave(netdev, 0, src_port);
|
|
+ if (!skb->dev)
|
|
+ /* The switch will reflect back some frames sent through
|
|
+ * sockets opened on the bare DSA master. These will come back
|
|
+ * with src_port equal to the index of the CPU port, for which
|
|
+ * there is no slave registered. So don't print any error
|
|
+ * message here (ignore and drop those frames).
|
|
+ */
|
|
+ return NULL;
|
|
+
|
|
+ skb->offload_fwd_mark = 1;
|
|
+ skb->priority = qos_class;
|
|
+
|
|
+ return skb;
|
|
+}
|
|
+
|
|
+static struct dsa_device_ops ocelot_netdev_ops = {
|
|
+ .name = "ocelot",
|
|
+ .proto = DSA_TAG_PROTO_OCELOT,
|
|
+ .xmit = ocelot_xmit,
|
|
+ .rcv = ocelot_rcv,
|
|
+ .overhead = OCELOT_TAG_LEN + OCELOT_LONG_PREFIX_LEN,
|
|
+};
|
|
+
|
|
+MODULE_LICENSE("GPL v2");
|
|
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OCELOT);
|
|
+
|
|
+module_dsa_tag_driver(ocelot_netdev_ops);
|