1 // SPDX-License-Identifier: GPL-2.0
2 /* Renesas Ethernet Switch device driver
3 *
4 * Copyright (C) 2025 - 2026 Renesas Electronics Corporation
5 */
6
7 #include <linux/err.h>
8 #include <linux/etherdevice.h>
9 #include <linux/if_bridge.h>
10 #include <linux/kernel.h>
11 #include <net/switchdev.h>
12
13 #include "rswitch.h"
14 #include "rswitch_l2.h"
15
rdev_for_l2_offload(struct rswitch_device * rdev)16 static bool rdev_for_l2_offload(struct rswitch_device *rdev)
17 {
18 return rdev->priv->offload_brdev &&
19 rdev->brdev == rdev->priv->offload_brdev &&
20 (test_bit(rdev->port, rdev->priv->opened_ports));
21 }
22
rswitch_change_l2_hw_offloading(struct rswitch_device * rdev,bool start,bool learning)23 static void rswitch_change_l2_hw_offloading(struct rswitch_device *rdev,
24 bool start, bool learning)
25 {
26 u32 bits = learning ? FWPC0_MACSSA | FWPC0_MACHLA | FWPC0_MACHMA : FWPC0_MACDSA;
27 u32 clear = start ? 0 : bits;
28 u32 set = start ? bits : 0;
29
30 if ((learning && rdev->learning_offloaded == start) ||
31 (!learning && rdev->forwarding_offloaded == start))
32 return;
33
34 rswitch_modify(rdev->priv->addr, FWPC0(rdev->port), clear, set);
35
36 if (learning)
37 rdev->learning_offloaded = start;
38 else
39 rdev->forwarding_offloaded = start;
40
41 netdev_info(rdev->ndev, "%s hw %s\n", start ? "starting" : "stopping",
42 learning ? "learning" : "forwarding");
43 }
44
rswitch_update_l2_hw_learning(struct rswitch_private * priv)45 static void rswitch_update_l2_hw_learning(struct rswitch_private *priv)
46 {
47 struct rswitch_device *rdev;
48 bool learning_needed;
49
50 rswitch_for_all_ports(priv, rdev) {
51 if (rdev_for_l2_offload(rdev))
52 learning_needed = rdev->learning_requested;
53 else
54 learning_needed = false;
55
56 rswitch_change_l2_hw_offloading(rdev, learning_needed, true);
57 }
58 }
59
rswitch_update_l2_hw_forwarding(struct rswitch_private * priv)60 static void rswitch_update_l2_hw_forwarding(struct rswitch_private *priv)
61 {
62 struct rswitch_device *rdev;
63 bool new_forwarding_offload;
64 unsigned int fwd_mask;
65
66 /* calculate fwd_mask with zeroes in bits corresponding to ports that
67 * shall participate in hardware forwarding
68 */
69 fwd_mask = GENMASK(RSWITCH_NUM_AGENTS - 1, 0);
70
71 rswitch_for_all_ports(priv, rdev) {
72 if (rdev_for_l2_offload(rdev) && rdev->forwarding_requested)
73 fwd_mask &= ~BIT(rdev->port);
74 }
75
76 rswitch_for_all_ports(priv, rdev) {
77 new_forwarding_offload = (rdev_for_l2_offload(rdev) && rdev->forwarding_requested);
78
79 if (new_forwarding_offload || rdev->forwarding_offloaded) {
80 /* Update allowed offload destinations even for ports
81 * with L2 offload enabled earlier.
82 *
83 * Do not allow L2 forwarding to self for hw port.
84 */
85 iowrite32(FIELD_PREP(FWCP2_LTWFW_MASK, fwd_mask | BIT(rdev->port)),
86 priv->addr + FWPC2(rdev->port));
87 }
88
89 if (new_forwarding_offload && !rdev->forwarding_offloaded)
90 rswitch_change_l2_hw_offloading(rdev, true, false);
91 else if (!new_forwarding_offload && rdev->forwarding_offloaded)
92 rswitch_change_l2_hw_offloading(rdev, false, false);
93 }
94 }
95
rswitch_update_l2_offload(struct rswitch_private * priv)96 void rswitch_update_l2_offload(struct rswitch_private *priv)
97 {
98 rswitch_update_l2_hw_learning(priv);
99 rswitch_update_l2_hw_forwarding(priv);
100 }
101
rswitch_update_offload_brdev(struct rswitch_private * priv)102 static void rswitch_update_offload_brdev(struct rswitch_private *priv)
103 {
104 struct net_device *offload_brdev = NULL;
105 struct rswitch_device *rdev, *rdev2;
106
107 rswitch_for_all_ports(priv, rdev) {
108 if (!rdev->brdev)
109 continue;
110 rswitch_for_all_ports(priv, rdev2) {
111 if (rdev2 == rdev)
112 break;
113 if (rdev2->brdev == rdev->brdev) {
114 offload_brdev = rdev->brdev;
115 break;
116 }
117 }
118 if (offload_brdev)
119 break;
120 }
121
122 if (offload_brdev == priv->offload_brdev)
123 dev_dbg(&priv->pdev->dev,
124 "changing l2 offload from %s to %s\n",
125 netdev_name(priv->offload_brdev),
126 netdev_name(offload_brdev));
127 else if (offload_brdev)
128 dev_dbg(&priv->pdev->dev, "starting l2 offload for %s\n",
129 netdev_name(offload_brdev));
130 else if (!offload_brdev)
131 dev_dbg(&priv->pdev->dev, "stopping l2 offload for %s\n",
132 netdev_name(priv->offload_brdev));
133
134 priv->offload_brdev = offload_brdev;
135
136 rswitch_update_l2_offload(priv);
137 }
138
rswitch_port_check(const struct net_device * ndev)139 static bool rswitch_port_check(const struct net_device *ndev)
140 {
141 return is_rdev(ndev);
142 }
143
rswitch_port_update_brdev(struct net_device * ndev,struct net_device * brdev)144 static void rswitch_port_update_brdev(struct net_device *ndev,
145 struct net_device *brdev)
146 {
147 struct rswitch_device *rdev;
148
149 if (!is_rdev(ndev))
150 return;
151
152 rdev = netdev_priv(ndev);
153 rdev->brdev = brdev;
154 rswitch_update_offload_brdev(rdev->priv);
155 }
156
rswitch_port_update_stp_state(struct net_device * ndev,u8 stp_state)157 static int rswitch_port_update_stp_state(struct net_device *ndev, u8 stp_state)
158 {
159 struct rswitch_device *rdev;
160
161 if (!is_rdev(ndev))
162 return -ENODEV;
163
164 rdev = netdev_priv(ndev);
165 rdev->learning_requested = (stp_state == BR_STATE_LEARNING ||
166 stp_state == BR_STATE_FORWARDING);
167 rdev->forwarding_requested = (stp_state == BR_STATE_FORWARDING);
168 rswitch_update_l2_offload(rdev->priv);
169
170 return 0;
171 }
172
rswitch_netdevice_event(struct notifier_block * nb,unsigned long event,void * ptr)173 static int rswitch_netdevice_event(struct notifier_block *nb,
174 unsigned long event, void *ptr)
175 {
176 struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
177 struct netdev_notifier_changeupper_info *info;
178 struct net_device *brdev;
179
180 if (!rswitch_port_check(ndev))
181 return NOTIFY_DONE;
182 if (event != NETDEV_CHANGEUPPER)
183 return NOTIFY_DONE;
184
185 info = ptr;
186
187 if (netif_is_bridge_master(info->upper_dev)) {
188 brdev = info->linking ? info->upper_dev : NULL;
189 rswitch_port_update_brdev(ndev, brdev);
190 }
191
192 return NOTIFY_OK;
193 }
194
rswitch_update_ageing_time(struct net_device * ndev,clock_t time)195 static int rswitch_update_ageing_time(struct net_device *ndev, clock_t time)
196 {
197 struct rswitch_device *rdev = netdev_priv(ndev);
198 u32 reg_val;
199
200 if (!is_rdev(ndev))
201 return -ENODEV;
202
203 if (!FIELD_FIT(FWMACAGC_MACAGT, time))
204 return -EINVAL;
205
206 reg_val = FIELD_PREP(FWMACAGC_MACAGT, time);
207 reg_val |= FWMACAGC_MACAGE | FWMACAGC_MACAGSL;
208 iowrite32(reg_val, rdev->priv->addr + FWMACAGC);
209
210 return 0;
211 }
212
rswitch_port_attr_set(struct net_device * ndev,const void * ctx,const struct switchdev_attr * attr,struct netlink_ext_ack * extack)213 static int rswitch_port_attr_set(struct net_device *ndev, const void *ctx,
214 const struct switchdev_attr *attr,
215 struct netlink_ext_ack *extack)
216 {
217 switch (attr->id) {
218 case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
219 return rswitch_port_update_stp_state(ndev, attr->u.stp_state);
220 case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
221 return rswitch_update_ageing_time(ndev, attr->u.ageing_time);
222 default:
223 return -EOPNOTSUPP;
224 }
225 }
226
rswitch_switchdev_event(struct notifier_block * nb,unsigned long event,void * ptr)227 static int rswitch_switchdev_event(struct notifier_block *nb,
228 unsigned long event, void *ptr)
229 {
230 struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
231 int ret;
232
233 if (event == SWITCHDEV_PORT_ATTR_SET) {
234 ret = switchdev_handle_port_attr_set(ndev, ptr,
235 rswitch_port_check,
236 rswitch_port_attr_set);
237 return notifier_from_errno(ret);
238 }
239
240 if (!rswitch_port_check(ndev))
241 return NOTIFY_DONE;
242
243 return notifier_from_errno(-EOPNOTSUPP);
244 }
245
rswitch_switchdev_blocking_event(struct notifier_block * nb,unsigned long event,void * ptr)246 static int rswitch_switchdev_blocking_event(struct notifier_block *nb,
247 unsigned long event, void *ptr)
248 {
249 struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
250 int ret;
251
252 switch (event) {
253 case SWITCHDEV_PORT_OBJ_ADD:
254 return -EOPNOTSUPP;
255 case SWITCHDEV_PORT_OBJ_DEL:
256 return -EOPNOTSUPP;
257 case SWITCHDEV_PORT_ATTR_SET:
258 ret = switchdev_handle_port_attr_set(ndev, ptr,
259 rswitch_port_check,
260 rswitch_port_attr_set);
261 break;
262 default:
263 if (!rswitch_port_check(ndev))
264 return NOTIFY_DONE;
265 ret = -EOPNOTSUPP;
266 }
267
268 return notifier_from_errno(ret);
269 }
270
271 static struct notifier_block rswitch_netdevice_nb = {
272 .notifier_call = rswitch_netdevice_event,
273 };
274
275 static struct notifier_block rswitch_switchdev_nb = {
276 .notifier_call = rswitch_switchdev_event,
277 };
278
279 static struct notifier_block rswitch_switchdev_blocking_nb = {
280 .notifier_call = rswitch_switchdev_blocking_event,
281 };
282
rswitch_register_notifiers(void)283 int rswitch_register_notifiers(void)
284 {
285 int ret;
286
287 ret = register_netdevice_notifier(&rswitch_netdevice_nb);
288 if (ret)
289 goto register_netdevice_notifier_failed;
290
291 ret = register_switchdev_notifier(&rswitch_switchdev_nb);
292 if (ret)
293 goto register_switchdev_notifier_failed;
294
295 ret = register_switchdev_blocking_notifier(&rswitch_switchdev_blocking_nb);
296 if (ret)
297 goto register_switchdev_blocking_notifier_failed;
298
299 return 0;
300
301 register_switchdev_blocking_notifier_failed:
302 unregister_switchdev_notifier(&rswitch_switchdev_nb);
303 register_switchdev_notifier_failed:
304 unregister_netdevice_notifier(&rswitch_netdevice_nb);
305 register_netdevice_notifier_failed:
306
307 return ret;
308 }
309
rswitch_unregister_notifiers(void)310 void rswitch_unregister_notifiers(void)
311 {
312 unregister_switchdev_blocking_notifier(&rswitch_switchdev_blocking_nb);
313 unregister_switchdev_notifier(&rswitch_switchdev_nb);
314 unregister_netdevice_notifier(&rswitch_netdevice_nb);
315 }
316