ipq40xx: 6.1: use latest DSA and ethernet patches
[openwrt/staging/jow.git] / target / linux / ipq40xx / patches-6.1 / 702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch
1 From 4975e2b3f1d37bba04f262784cef0d5b7e0a30a4 Mon Sep 17 00:00:00 2001
2 From: Maxime Chevallier <maxime.chevallier@bootlin.com>
3 Date: Fri, 4 Nov 2022 18:41:50 +0100
4 Subject: [PATCH] net: ipqess: Add out-of-band DSA tagging support
5
6 On the IPQ4019, there's an 5 ports switch connected to the CPU through
7 the IPQESS Ethernet controller. The way the DSA tag is sent-out to that
8 switch is through the DMA descriptor, due to how tightly it is
9 integrated with the switch.
10
11 We use the out-of-band tagging protocol by getting the source
12 port from the descriptor, push it into the skb extensions, and have the
13 tagger pull it to infer the destination netdev. The reverse process is
14 done on the TX side, where the driver pulls the tag from the skb and
15 builds the descriptor accordingly.
16
17 Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
18 ---
19 drivers/net/ethernet/qualcomm/Kconfig | 1 +
20 drivers/net/ethernet/qualcomm/ipqess/ipqess.c | 64 ++++++++++++++++++-
21 drivers/net/ethernet/qualcomm/ipqess/ipqess.h | 4 ++
22 3 files changed, 68 insertions(+), 1 deletion(-)
23
24 --- a/drivers/net/ethernet/qualcomm/Kconfig
25 +++ b/drivers/net/ethernet/qualcomm/Kconfig
26 @@ -64,6 +64,7 @@ config QCOM_IPQ4019_ESS_EDMA
27 tristate "Qualcomm Atheros IPQ4019 ESS EDMA support"
28 depends on (OF && ARCH_QCOM) || COMPILE_TEST
29 select PHYLINK
30 + select NET_DSA_TAG_OOB
31 help
32 This driver supports the Qualcomm Atheros IPQ40xx built-in
33 ESS EDMA ethernet controller.
34 --- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c
35 +++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c
36 @@ -9,6 +9,7 @@
37
38 #include <linux/bitfield.h>
39 #include <linux/clk.h>
40 +#include <linux/dsa/oob.h>
41 #include <linux/if_vlan.h>
42 #include <linux/interrupt.h>
43 #include <linux/module.h>
44 @@ -22,6 +23,7 @@
45 #include <linux/skbuff.h>
46 #include <linux/vmalloc.h>
47 #include <net/checksum.h>
48 +#include <net/dsa.h>
49 #include <net/ip6_checksum.h>
50
51 #include "ipqess.h"
52 @@ -327,6 +329,7 @@ static int ipqess_rx_poll(struct ipqess_
53 tail &= IPQESS_RFD_CONS_IDX_MASK;
54
55 while (done < budget) {
56 + struct dsa_oob_tag_info *tag_info;
57 struct ipqess_rx_desc *rd;
58 struct sk_buff *skb;
59
60 @@ -406,6 +409,12 @@ static int ipqess_rx_poll(struct ipqess_
61 __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021AD),
62 le16_to_cpu(rd->rrd4));
63
64 + if (likely(rx_ring->ess->dsa_ports)) {
65 + tag_info = skb_ext_add(skb, SKB_EXT_DSA_OOB);
66 + tag_info->port = FIELD_GET(IPQESS_RRD_PORT_ID_MASK,
67 + le16_to_cpu(rd->rrd1));
68 + }
69 +
70 napi_gro_receive(&rx_ring->napi_rx, skb);
71
72 rx_ring->ess->stats.rx_packets++;
73 @@ -706,6 +715,23 @@ static void ipqess_rollback_tx(struct ip
74 tx_ring->head = start_index;
75 }
76
77 +static void ipqess_process_dsa_tag_sh(struct ipqess *ess, struct sk_buff *skb,
78 + u32 *word3)
79 +{
80 + struct dsa_oob_tag_info *tag_info;
81 +
82 + if (unlikely(!ess->dsa_ports))
83 + return;
84 +
85 + tag_info = skb_ext_find(skb, SKB_EXT_DSA_OOB);
86 + if (!tag_info)
87 + return;
88 +
89 + *word3 |= tag_info->port << IPQESS_TPD_PORT_BITMAP_SHIFT;
90 + *word3 |= BIT(IPQESS_TPD_FROM_CPU_SHIFT);
91 + *word3 |= 0x3e << IPQESS_TPD_PORT_BITMAP_SHIFT;
92 +}
93 +
94 static int ipqess_tx_map_and_fill(struct ipqess_tx_ring *tx_ring,
95 struct sk_buff *skb)
96 {
97 @@ -716,6 +742,8 @@ static int ipqess_tx_map_and_fill(struct
98 u16 len;
99 int i;
100
101 + ipqess_process_dsa_tag_sh(tx_ring->ess, skb, &word3);
102 +
103 if (skb_is_gso(skb)) {
104 if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) {
105 lso_word1 |= IPQESS_TPD_IPV4_EN;
106 @@ -917,6 +945,33 @@ static const struct net_device_ops ipqes
107 .ndo_tx_timeout = ipqess_tx_timeout,
108 };
109
110 +static int ipqess_netdevice_event(struct notifier_block *nb,
111 + unsigned long event, void *ptr)
112 +{
113 + struct ipqess *ess = container_of(nb, struct ipqess, netdev_notifier);
114 + struct net_device *dev = netdev_notifier_info_to_dev(ptr);
115 + struct netdev_notifier_changeupper_info *info;
116 +
117 + if (dev != ess->netdev)
118 + return NOTIFY_DONE;
119 +
120 + switch (event) {
121 + case NETDEV_CHANGEUPPER:
122 + info = ptr;
123 +
124 + if (!dsa_slave_dev_check(info->upper_dev))
125 + return NOTIFY_DONE;
126 +
127 + if (info->linking)
128 + ess->dsa_ports++;
129 + else
130 + ess->dsa_ports--;
131 +
132 + return NOTIFY_DONE;
133 + }
134 + return NOTIFY_OK;
135 +}
136 +
137 static void ipqess_hw_stop(struct ipqess *ess)
138 {
139 int i;
140 @@ -1184,12 +1239,19 @@ static int ipqess_axi_probe(struct platf
141 netif_napi_add(netdev, &ess->rx_ring[i].napi_rx, ipqess_rx_napi);
142 }
143
144 - err = register_netdev(netdev);
145 + ess->netdev_notifier.notifier_call = ipqess_netdevice_event;
146 + err = register_netdevice_notifier(&ess->netdev_notifier);
147 if (err)
148 goto err_hw_stop;
149
150 + err = register_netdev(netdev);
151 + if (err)
152 + goto err_notifier_unregister;
153 +
154 return 0;
155
156 +err_notifier_unregister:
157 + unregister_netdevice_notifier(&ess->netdev_notifier);
158 err_hw_stop:
159 ipqess_hw_stop(ess);
160
161 --- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.h
162 +++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.h
163 @@ -171,6 +171,10 @@ struct ipqess {
164 struct platform_device *pdev;
165 struct phylink *phylink;
166 struct phylink_config phylink_config;
167 +
168 + struct notifier_block netdev_notifier;
169 + int dsa_ports;
170 +
171 struct ipqess_tx_ring tx_ring[IPQESS_NETDEV_QUEUES];
172
173 struct ipqess_statistics ipqess_stats;