Skip to content

Commit

Permalink
net: dsa: ocelot: add tagger for Ocelot/Felix switches
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
vladimiroltean authored and davem330 committed Nov 15, 2019
1 parent a030dfe commit 8dce89a
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 0 deletions.
7 changes: 7 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -17360,6 +17360,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
Expand Down
2 changes: 2 additions & 0 deletions include/net/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions net/dsa/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,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
Expand Down
1 change: 1 addition & 0 deletions net/dsa/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o
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
229 changes: 229 additions & 0 deletions net/dsa/tag_ocelot.c
Original file line number Diff line number Diff line change
@@ -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);

0 comments on commit 8dce89a

Please sign in to comment.