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