xref: /linux/drivers/net/ethernet/renesas/rswitch_l2.c (revision 1fd1dc41724319406b0aff221a352a400b0ddfc5)
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 
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 
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 
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 
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 
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 
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 
139 static bool rswitch_port_check(const struct net_device *ndev)
140 {
141 	return is_rdev(ndev);
142 }
143 
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 
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 
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 
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 
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 
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 
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 
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 
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