summaryrefslogtreecommitdiff
path: root/net/dsa/tag_gswip.c
blob: 49e9b73f1be38740d4773fe5737797b4e9647dd6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// SPDX-License-Identifier: GPL-2.0
/*
 * Intel / Lantiq GSWIP V2.0 PMAC tag support
 *
 * Copyright (C) 2017 - 2018 Hauke Mehrtens <hauke@hauke-m.de>
 */

#include <linux/bitops.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <net/dsa.h>

#include "dsa_priv.h"

#define GSWIP_TX_HEADER_LEN		4

/* special tag in TX path header */
/* Byte 0 */
#define GSWIP_TX_SLPID_SHIFT		0	/* source port ID */
#define  GSWIP_TX_SLPID_CPU		2
#define  GSWIP_TX_SLPID_APP1		3
#define  GSWIP_TX_SLPID_APP2		4
#define  GSWIP_TX_SLPID_APP3		5
#define  GSWIP_TX_SLPID_APP4		6
#define  GSWIP_TX_SLPID_APP5		7

/* Byte 1 */
#define GSWIP_TX_CRCGEN_DIS		BIT(7)
#define GSWIP_TX_DPID_SHIFT		0	/* destination group ID */
#define  GSWIP_TX_DPID_ELAN		0
#define  GSWIP_TX_DPID_EWAN		1
#define  GSWIP_TX_DPID_CPU		2
#define  GSWIP_TX_DPID_APP1		3
#define  GSWIP_TX_DPID_APP2		4
#define  GSWIP_TX_DPID_APP3		5
#define  GSWIP_TX_DPID_APP4		6
#define  GSWIP_TX_DPID_APP5		7

/* Byte 2 */
#define GSWIP_TX_PORT_MAP_EN		BIT(7)
#define GSWIP_TX_PORT_MAP_SEL		BIT(6)
#define GSWIP_TX_LRN_DIS		BIT(5)
#define GSWIP_TX_CLASS_EN		BIT(4)
#define GSWIP_TX_CLASS_SHIFT		0
#define GSWIP_TX_CLASS_MASK		GENMASK(3, 0)

/* Byte 3 */
#define GSWIP_TX_DPID_EN		BIT(0)
#define GSWIP_TX_PORT_MAP_SHIFT		1
#define GSWIP_TX_PORT_MAP_MASK		GENMASK(6, 1)

#define GSWIP_RX_HEADER_LEN	8

/* special tag in RX path header */
/* Byte 7 */
#define GSWIP_RX_SPPID_SHIFT		4
#define GSWIP_RX_SPPID_MASK		GENMASK(6, 4)

static struct sk_buff *gswip_tag_xmit(struct sk_buff *skb,
				      struct net_device *dev)
{
	struct dsa_port *dp = dsa_slave_to_port(dev);
	int err;
	u8 *gswip_tag;

	err = skb_cow_head(skb, GSWIP_TX_HEADER_LEN);
	if (err)
		return NULL;

	skb_push(skb, GSWIP_TX_HEADER_LEN);

	gswip_tag = skb->data;
	gswip_tag[0] = GSWIP_TX_SLPID_CPU;
	gswip_tag[1] = GSWIP_TX_DPID_ELAN;
	gswip_tag[2] = GSWIP_TX_PORT_MAP_EN | GSWIP_TX_PORT_MAP_SEL;
	gswip_tag[3] = BIT(dp->index + GSWIP_TX_PORT_MAP_SHIFT) & GSWIP_TX_PORT_MAP_MASK;
	gswip_tag[3] |= GSWIP_TX_DPID_EN;

	return skb;
}

static struct sk_buff *gswip_tag_rcv(struct sk_buff *skb,
				     struct net_device *dev,
				     struct packet_type *pt)
{
	int port;
	u8 *gswip_tag;

	if (unlikely(!pskb_may_pull(skb, GSWIP_RX_HEADER_LEN)))
		return NULL;

	gswip_tag = skb->data - ETH_HLEN;

	/* Get source port information */
	port = (gswip_tag[7] & GSWIP_RX_SPPID_MASK) >> GSWIP_RX_SPPID_SHIFT;
	skb->dev = dsa_master_find_slave(dev, 0, port);
	if (!skb->dev)
		return NULL;

	/* remove GSWIP tag */
	skb_pull_rcsum(skb, GSWIP_RX_HEADER_LEN);

	return skb;
}

const struct dsa_device_ops gswip_netdev_ops = {
	.xmit = gswip_tag_xmit,
	.rcv = gswip_tag_rcv,
};