1501ef306SVadym Kochan // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2501ef306SVadym Kochan /* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
3501ef306SVadym Kochan 
4501ef306SVadym Kochan #include <linux/etherdevice.h>
5501ef306SVadym Kochan #include <linux/jiffies.h>
6501ef306SVadym Kochan #include <linux/list.h>
7501ef306SVadym Kochan #include <linux/module.h>
8501ef306SVadym Kochan #include <linux/netdev_features.h>
9501ef306SVadym Kochan #include <linux/of.h>
10501ef306SVadym Kochan #include <linux/of_net.h>
11255213caSSerhiy Boiko #include <linux/if_vlan.h>
12501ef306SVadym Kochan 
13501ef306SVadym Kochan #include "prestera.h"
14501ef306SVadym Kochan #include "prestera_hw.h"
158b474a9fSSerhiy Boiko #include "prestera_acl.h"
168b474a9fSSerhiy Boiko #include "prestera_flow.h"
1713defa27SSerhiy Boiko #include "prestera_span.h"
18501ef306SVadym Kochan #include "prestera_rxtx.h"
1934dd1710SVadym Kochan #include "prestera_devlink.h"
20a97d3c69SVadym Kochan #include "prestera_ethtool.h"
21e1189d9aSVadym Kochan #include "prestera_switchdev.h"
22501ef306SVadym Kochan 
23501ef306SVadym Kochan #define PRESTERA_MTU_DEFAULT	1536
24501ef306SVadym Kochan 
25501ef306SVadym Kochan #define PRESTERA_STATS_DELAY_MS	1000
26501ef306SVadym Kochan 
27501ef306SVadym Kochan #define PRESTERA_MAC_ADDR_NUM_MAX	255
28501ef306SVadym Kochan 
29501ef306SVadym Kochan static struct workqueue_struct *prestera_wq;
30501ef306SVadym Kochan 
31e1189d9aSVadym Kochan int prestera_port_pvid_set(struct prestera_port *port, u16 vid)
32e1189d9aSVadym Kochan {
33e1189d9aSVadym Kochan 	enum prestera_accept_frm_type frm_type;
34e1189d9aSVadym Kochan 	int err;
35e1189d9aSVadym Kochan 
36e1189d9aSVadym Kochan 	frm_type = PRESTERA_ACCEPT_FRAME_TYPE_TAGGED;
37e1189d9aSVadym Kochan 
38e1189d9aSVadym Kochan 	if (vid) {
39e1189d9aSVadym Kochan 		err = prestera_hw_vlan_port_vid_set(port, vid);
40e1189d9aSVadym Kochan 		if (err)
41e1189d9aSVadym Kochan 			return err;
42e1189d9aSVadym Kochan 
43e1189d9aSVadym Kochan 		frm_type = PRESTERA_ACCEPT_FRAME_TYPE_ALL;
44e1189d9aSVadym Kochan 	}
45e1189d9aSVadym Kochan 
46e1189d9aSVadym Kochan 	err = prestera_hw_port_accept_frm_type(port, frm_type);
47e1189d9aSVadym Kochan 	if (err && frm_type == PRESTERA_ACCEPT_FRAME_TYPE_ALL)
48e1189d9aSVadym Kochan 		prestera_hw_vlan_port_vid_set(port, port->pvid);
49e1189d9aSVadym Kochan 
50e1189d9aSVadym Kochan 	port->pvid = vid;
51e1189d9aSVadym Kochan 	return 0;
52e1189d9aSVadym Kochan }
53e1189d9aSVadym Kochan 
54501ef306SVadym Kochan struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
55501ef306SVadym Kochan 						 u32 dev_id, u32 hw_id)
56501ef306SVadym Kochan {
57501ef306SVadym Kochan 	struct prestera_port *port = NULL;
58501ef306SVadym Kochan 
59501ef306SVadym Kochan 	read_lock(&sw->port_list_lock);
60501ef306SVadym Kochan 	list_for_each_entry(port, &sw->port_list, list) {
61501ef306SVadym Kochan 		if (port->dev_id == dev_id && port->hw_id == hw_id)
62501ef306SVadym Kochan 			break;
63501ef306SVadym Kochan 	}
64501ef306SVadym Kochan 	read_unlock(&sw->port_list_lock);
65501ef306SVadym Kochan 
66501ef306SVadym Kochan 	return port;
67501ef306SVadym Kochan }
68501ef306SVadym Kochan 
69e1189d9aSVadym Kochan struct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id)
70501ef306SVadym Kochan {
71501ef306SVadym Kochan 	struct prestera_port *port = NULL;
72501ef306SVadym Kochan 
73501ef306SVadym Kochan 	read_lock(&sw->port_list_lock);
74501ef306SVadym Kochan 	list_for_each_entry(port, &sw->port_list, list) {
75501ef306SVadym Kochan 		if (port->id == id)
76501ef306SVadym Kochan 			break;
77501ef306SVadym Kochan 	}
78501ef306SVadym Kochan 	read_unlock(&sw->port_list_lock);
79501ef306SVadym Kochan 
80501ef306SVadym Kochan 	return port;
81501ef306SVadym Kochan }
82501ef306SVadym Kochan 
83501ef306SVadym Kochan static int prestera_port_open(struct net_device *dev)
84501ef306SVadym Kochan {
85501ef306SVadym Kochan 	struct prestera_port *port = netdev_priv(dev);
86501ef306SVadym Kochan 	int err;
87501ef306SVadym Kochan 
88501ef306SVadym Kochan 	err = prestera_hw_port_state_set(port, true);
89501ef306SVadym Kochan 	if (err)
90501ef306SVadym Kochan 		return err;
91501ef306SVadym Kochan 
92501ef306SVadym Kochan 	netif_start_queue(dev);
93501ef306SVadym Kochan 
94501ef306SVadym Kochan 	return 0;
95501ef306SVadym Kochan }
96501ef306SVadym Kochan 
97501ef306SVadym Kochan static int prestera_port_close(struct net_device *dev)
98501ef306SVadym Kochan {
99501ef306SVadym Kochan 	struct prestera_port *port = netdev_priv(dev);
100501ef306SVadym Kochan 
101501ef306SVadym Kochan 	netif_stop_queue(dev);
102501ef306SVadym Kochan 
10305372c45SZheng Yongjun 	return prestera_hw_port_state_set(port, false);
104501ef306SVadym Kochan }
105501ef306SVadym Kochan 
106501ef306SVadym Kochan static netdev_tx_t prestera_port_xmit(struct sk_buff *skb,
107501ef306SVadym Kochan 				      struct net_device *dev)
108501ef306SVadym Kochan {
109501ef306SVadym Kochan 	return prestera_rxtx_xmit(netdev_priv(dev), skb);
110501ef306SVadym Kochan }
111501ef306SVadym Kochan 
112501ef306SVadym Kochan static int prestera_is_valid_mac_addr(struct prestera_port *port, u8 *addr)
113501ef306SVadym Kochan {
114501ef306SVadym Kochan 	if (!is_valid_ether_addr(addr))
115501ef306SVadym Kochan 		return -EADDRNOTAVAIL;
116501ef306SVadym Kochan 
117501ef306SVadym Kochan 	/* firmware requires that port's MAC address contains first 5 bytes
118501ef306SVadym Kochan 	 * of the base MAC address
119501ef306SVadym Kochan 	 */
120501ef306SVadym Kochan 	if (memcmp(port->sw->base_mac, addr, ETH_ALEN - 1))
121501ef306SVadym Kochan 		return -EINVAL;
122501ef306SVadym Kochan 
123501ef306SVadym Kochan 	return 0;
124501ef306SVadym Kochan }
125501ef306SVadym Kochan 
126501ef306SVadym Kochan static int prestera_port_set_mac_address(struct net_device *dev, void *p)
127501ef306SVadym Kochan {
128501ef306SVadym Kochan 	struct prestera_port *port = netdev_priv(dev);
129501ef306SVadym Kochan 	struct sockaddr *addr = p;
130501ef306SVadym Kochan 	int err;
131501ef306SVadym Kochan 
132501ef306SVadym Kochan 	err = prestera_is_valid_mac_addr(port, addr->sa_data);
133501ef306SVadym Kochan 	if (err)
134501ef306SVadym Kochan 		return err;
135501ef306SVadym Kochan 
136501ef306SVadym Kochan 	err = prestera_hw_port_mac_set(port, addr->sa_data);
137501ef306SVadym Kochan 	if (err)
138501ef306SVadym Kochan 		return err;
139501ef306SVadym Kochan 
140*f3956ebbSJakub Kicinski 	eth_hw_addr_set(dev, addr->sa_data);
141501ef306SVadym Kochan 
142501ef306SVadym Kochan 	return 0;
143501ef306SVadym Kochan }
144501ef306SVadym Kochan 
145501ef306SVadym Kochan static int prestera_port_change_mtu(struct net_device *dev, int mtu)
146501ef306SVadym Kochan {
147501ef306SVadym Kochan 	struct prestera_port *port = netdev_priv(dev);
148501ef306SVadym Kochan 	int err;
149501ef306SVadym Kochan 
150501ef306SVadym Kochan 	err = prestera_hw_port_mtu_set(port, mtu);
151501ef306SVadym Kochan 	if (err)
152501ef306SVadym Kochan 		return err;
153501ef306SVadym Kochan 
154501ef306SVadym Kochan 	dev->mtu = mtu;
155501ef306SVadym Kochan 
156501ef306SVadym Kochan 	return 0;
157501ef306SVadym Kochan }
158501ef306SVadym Kochan 
159501ef306SVadym Kochan static void prestera_port_get_stats64(struct net_device *dev,
160501ef306SVadym Kochan 				      struct rtnl_link_stats64 *stats)
161501ef306SVadym Kochan {
162501ef306SVadym Kochan 	struct prestera_port *port = netdev_priv(dev);
163501ef306SVadym Kochan 	struct prestera_port_stats *port_stats = &port->cached_hw_stats.stats;
164501ef306SVadym Kochan 
165501ef306SVadym Kochan 	stats->rx_packets = port_stats->broadcast_frames_received +
166501ef306SVadym Kochan 				port_stats->multicast_frames_received +
167501ef306SVadym Kochan 				port_stats->unicast_frames_received;
168501ef306SVadym Kochan 
169501ef306SVadym Kochan 	stats->tx_packets = port_stats->broadcast_frames_sent +
170501ef306SVadym Kochan 				port_stats->multicast_frames_sent +
171501ef306SVadym Kochan 				port_stats->unicast_frames_sent;
172501ef306SVadym Kochan 
173501ef306SVadym Kochan 	stats->rx_bytes = port_stats->good_octets_received;
174501ef306SVadym Kochan 
175501ef306SVadym Kochan 	stats->tx_bytes = port_stats->good_octets_sent;
176501ef306SVadym Kochan 
177501ef306SVadym Kochan 	stats->rx_errors = port_stats->rx_error_frame_received;
178501ef306SVadym Kochan 	stats->tx_errors = port_stats->mac_trans_error;
179501ef306SVadym Kochan 
180501ef306SVadym Kochan 	stats->rx_dropped = port_stats->buffer_overrun;
181501ef306SVadym Kochan 	stats->tx_dropped = 0;
182501ef306SVadym Kochan 
183501ef306SVadym Kochan 	stats->multicast = port_stats->multicast_frames_received;
184501ef306SVadym Kochan 	stats->collisions = port_stats->excessive_collision;
185501ef306SVadym Kochan 
186501ef306SVadym Kochan 	stats->rx_crc_errors = port_stats->bad_crc;
187501ef306SVadym Kochan }
188501ef306SVadym Kochan 
189501ef306SVadym Kochan static void prestera_port_get_hw_stats(struct prestera_port *port)
190501ef306SVadym Kochan {
191501ef306SVadym Kochan 	prestera_hw_port_stats_get(port, &port->cached_hw_stats.stats);
192501ef306SVadym Kochan }
193501ef306SVadym Kochan 
194501ef306SVadym Kochan static void prestera_port_stats_update(struct work_struct *work)
195501ef306SVadym Kochan {
196501ef306SVadym Kochan 	struct prestera_port *port =
197501ef306SVadym Kochan 		container_of(work, struct prestera_port,
198501ef306SVadym Kochan 			     cached_hw_stats.caching_dw.work);
199501ef306SVadym Kochan 
200501ef306SVadym Kochan 	prestera_port_get_hw_stats(port);
201501ef306SVadym Kochan 
202501ef306SVadym Kochan 	queue_delayed_work(prestera_wq, &port->cached_hw_stats.caching_dw,
203501ef306SVadym Kochan 			   msecs_to_jiffies(PRESTERA_STATS_DELAY_MS));
204501ef306SVadym Kochan }
205501ef306SVadym Kochan 
2068b474a9fSSerhiy Boiko static int prestera_port_setup_tc(struct net_device *dev,
2078b474a9fSSerhiy Boiko 				  enum tc_setup_type type,
2088b474a9fSSerhiy Boiko 				  void *type_data)
2098b474a9fSSerhiy Boiko {
2108b474a9fSSerhiy Boiko 	struct prestera_port *port = netdev_priv(dev);
2118b474a9fSSerhiy Boiko 
2128b474a9fSSerhiy Boiko 	switch (type) {
2138b474a9fSSerhiy Boiko 	case TC_SETUP_BLOCK:
2148b474a9fSSerhiy Boiko 		return prestera_flow_block_setup(port, type_data);
2158b474a9fSSerhiy Boiko 	default:
2168b474a9fSSerhiy Boiko 		return -EOPNOTSUPP;
2178b474a9fSSerhiy Boiko 	}
2188b474a9fSSerhiy Boiko }
2198b474a9fSSerhiy Boiko 
220501ef306SVadym Kochan static const struct net_device_ops prestera_netdev_ops = {
221501ef306SVadym Kochan 	.ndo_open = prestera_port_open,
222501ef306SVadym Kochan 	.ndo_stop = prestera_port_close,
223501ef306SVadym Kochan 	.ndo_start_xmit = prestera_port_xmit,
2248b474a9fSSerhiy Boiko 	.ndo_setup_tc = prestera_port_setup_tc,
225501ef306SVadym Kochan 	.ndo_change_mtu = prestera_port_change_mtu,
226501ef306SVadym Kochan 	.ndo_get_stats64 = prestera_port_get_stats64,
227501ef306SVadym Kochan 	.ndo_set_mac_address = prestera_port_set_mac_address,
22834dd1710SVadym Kochan 	.ndo_get_devlink_port = prestera_devlink_get_port,
229501ef306SVadym Kochan };
230501ef306SVadym Kochan 
231a97d3c69SVadym Kochan int prestera_port_autoneg_set(struct prestera_port *port, bool enable,
232a97d3c69SVadym Kochan 			      u64 adver_link_modes, u8 adver_fec)
233501ef306SVadym Kochan {
234501ef306SVadym Kochan 	bool refresh = false;
235a97d3c69SVadym Kochan 	u64 link_modes;
236501ef306SVadym Kochan 	int err;
237a97d3c69SVadym Kochan 	u8 fec;
238501ef306SVadym Kochan 
239501ef306SVadym Kochan 	if (port->caps.type != PRESTERA_PORT_TYPE_TP)
240501ef306SVadym Kochan 		return enable ? -EINVAL : 0;
241501ef306SVadym Kochan 
242a97d3c69SVadym Kochan 	if (!enable)
243a97d3c69SVadym Kochan 		goto set_autoneg;
244a97d3c69SVadym Kochan 
245a97d3c69SVadym Kochan 	link_modes = port->caps.supp_link_modes & adver_link_modes;
246a97d3c69SVadym Kochan 	fec = port->caps.supp_fec & adver_fec;
247a97d3c69SVadym Kochan 
248a97d3c69SVadym Kochan 	if (!link_modes && !fec)
249a97d3c69SVadym Kochan 		return -EOPNOTSUPP;
250a97d3c69SVadym Kochan 
251a97d3c69SVadym Kochan 	if (link_modes && port->adver_link_modes != link_modes) {
252501ef306SVadym Kochan 		port->adver_link_modes = link_modes;
253501ef306SVadym Kochan 		refresh = true;
254501ef306SVadym Kochan 	}
255501ef306SVadym Kochan 
256a97d3c69SVadym Kochan 	if (fec && port->adver_fec != fec) {
257a97d3c69SVadym Kochan 		port->adver_fec = fec;
258a97d3c69SVadym Kochan 		refresh = true;
259a97d3c69SVadym Kochan 	}
260a97d3c69SVadym Kochan 
261a97d3c69SVadym Kochan set_autoneg:
262a97d3c69SVadym Kochan 	if (port->autoneg == enable && !refresh)
263501ef306SVadym Kochan 		return 0;
264501ef306SVadym Kochan 
265501ef306SVadym Kochan 	err = prestera_hw_port_autoneg_set(port, enable, port->adver_link_modes,
266501ef306SVadym Kochan 					   port->adver_fec);
267501ef306SVadym Kochan 	if (err)
268501ef306SVadym Kochan 		return err;
269501ef306SVadym Kochan 
270501ef306SVadym Kochan 	port->autoneg = enable;
271501ef306SVadym Kochan 
272501ef306SVadym Kochan 	return 0;
273501ef306SVadym Kochan }
274501ef306SVadym Kochan 
275501ef306SVadym Kochan static void prestera_port_list_add(struct prestera_port *port)
276501ef306SVadym Kochan {
277501ef306SVadym Kochan 	write_lock(&port->sw->port_list_lock);
278501ef306SVadym Kochan 	list_add(&port->list, &port->sw->port_list);
279501ef306SVadym Kochan 	write_unlock(&port->sw->port_list_lock);
280501ef306SVadym Kochan }
281501ef306SVadym Kochan 
282501ef306SVadym Kochan static void prestera_port_list_del(struct prestera_port *port)
283501ef306SVadym Kochan {
284501ef306SVadym Kochan 	write_lock(&port->sw->port_list_lock);
285501ef306SVadym Kochan 	list_del(&port->list);
286501ef306SVadym Kochan 	write_unlock(&port->sw->port_list_lock);
287501ef306SVadym Kochan }
288501ef306SVadym Kochan 
289501ef306SVadym Kochan static int prestera_port_create(struct prestera_switch *sw, u32 id)
290501ef306SVadym Kochan {
291501ef306SVadym Kochan 	struct prestera_port *port;
292501ef306SVadym Kochan 	struct net_device *dev;
293501ef306SVadym Kochan 	int err;
294501ef306SVadym Kochan 
295501ef306SVadym Kochan 	dev = alloc_etherdev(sizeof(*port));
296501ef306SVadym Kochan 	if (!dev)
297501ef306SVadym Kochan 		return -ENOMEM;
298501ef306SVadym Kochan 
299501ef306SVadym Kochan 	port = netdev_priv(dev);
300501ef306SVadym Kochan 
301e1189d9aSVadym Kochan 	INIT_LIST_HEAD(&port->vlans_list);
302e1189d9aSVadym Kochan 	port->pvid = PRESTERA_DEFAULT_VID;
303255213caSSerhiy Boiko 	port->lag = NULL;
304501ef306SVadym Kochan 	port->dev = dev;
305501ef306SVadym Kochan 	port->id = id;
306501ef306SVadym Kochan 	port->sw = sw;
307501ef306SVadym Kochan 
308501ef306SVadym Kochan 	err = prestera_hw_port_info_get(port, &port->dev_id, &port->hw_id,
309501ef306SVadym Kochan 					&port->fp_id);
310501ef306SVadym Kochan 	if (err) {
311501ef306SVadym Kochan 		dev_err(prestera_dev(sw), "Failed to get port(%u) info\n", id);
31234dd1710SVadym Kochan 		goto err_port_info_get;
313501ef306SVadym Kochan 	}
314501ef306SVadym Kochan 
31534dd1710SVadym Kochan 	err = prestera_devlink_port_register(port);
31634dd1710SVadym Kochan 	if (err)
31734dd1710SVadym Kochan 		goto err_dl_port_register;
31834dd1710SVadym Kochan 
3198b474a9fSSerhiy Boiko 	dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_HW_TC;
320501ef306SVadym Kochan 	dev->netdev_ops = &prestera_netdev_ops;
321a97d3c69SVadym Kochan 	dev->ethtool_ops = &prestera_ethtool_ops;
322501ef306SVadym Kochan 
323501ef306SVadym Kochan 	netif_carrier_off(dev);
324501ef306SVadym Kochan 
325501ef306SVadym Kochan 	dev->mtu = min_t(unsigned int, sw->mtu_max, PRESTERA_MTU_DEFAULT);
326501ef306SVadym Kochan 	dev->min_mtu = sw->mtu_min;
327501ef306SVadym Kochan 	dev->max_mtu = sw->mtu_max;
328501ef306SVadym Kochan 
329501ef306SVadym Kochan 	err = prestera_hw_port_mtu_set(port, dev->mtu);
330501ef306SVadym Kochan 	if (err) {
331501ef306SVadym Kochan 		dev_err(prestera_dev(sw), "Failed to set port(%u) mtu(%d)\n",
332501ef306SVadym Kochan 			id, dev->mtu);
333501ef306SVadym Kochan 		goto err_port_init;
334501ef306SVadym Kochan 	}
335501ef306SVadym Kochan 
3364de377b6SZhang Changzhong 	if (port->fp_id >= PRESTERA_MAC_ADDR_NUM_MAX) {
3374de377b6SZhang Changzhong 		err = -EINVAL;
338501ef306SVadym Kochan 		goto err_port_init;
3394de377b6SZhang Changzhong 	}
340501ef306SVadym Kochan 
341501ef306SVadym Kochan 	/* firmware requires that port's MAC address consist of the first
342501ef306SVadym Kochan 	 * 5 bytes of the base MAC address
343501ef306SVadym Kochan 	 */
344501ef306SVadym Kochan 	memcpy(dev->dev_addr, sw->base_mac, dev->addr_len - 1);
345501ef306SVadym Kochan 	dev->dev_addr[dev->addr_len - 1] = port->fp_id;
346501ef306SVadym Kochan 
347501ef306SVadym Kochan 	err = prestera_hw_port_mac_set(port, dev->dev_addr);
348501ef306SVadym Kochan 	if (err) {
349501ef306SVadym Kochan 		dev_err(prestera_dev(sw), "Failed to set port(%u) mac addr\n", id);
350501ef306SVadym Kochan 		goto err_port_init;
351501ef306SVadym Kochan 	}
352501ef306SVadym Kochan 
353501ef306SVadym Kochan 	err = prestera_hw_port_cap_get(port, &port->caps);
354501ef306SVadym Kochan 	if (err) {
355501ef306SVadym Kochan 		dev_err(prestera_dev(sw), "Failed to get port(%u) caps\n", id);
356501ef306SVadym Kochan 		goto err_port_init;
357501ef306SVadym Kochan 	}
358501ef306SVadym Kochan 
359501ef306SVadym Kochan 	port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF);
360501ef306SVadym Kochan 	prestera_port_autoneg_set(port, true, port->caps.supp_link_modes,
361501ef306SVadym Kochan 				  port->caps.supp_fec);
362501ef306SVadym Kochan 
363501ef306SVadym Kochan 	err = prestera_hw_port_state_set(port, false);
364501ef306SVadym Kochan 	if (err) {
365501ef306SVadym Kochan 		dev_err(prestera_dev(sw), "Failed to set port(%u) down\n", id);
366501ef306SVadym Kochan 		goto err_port_init;
367501ef306SVadym Kochan 	}
368501ef306SVadym Kochan 
369501ef306SVadym Kochan 	err = prestera_rxtx_port_init(port);
370501ef306SVadym Kochan 	if (err)
371501ef306SVadym Kochan 		goto err_port_init;
372501ef306SVadym Kochan 
373501ef306SVadym Kochan 	INIT_DELAYED_WORK(&port->cached_hw_stats.caching_dw,
374501ef306SVadym Kochan 			  &prestera_port_stats_update);
375501ef306SVadym Kochan 
376501ef306SVadym Kochan 	prestera_port_list_add(port);
377501ef306SVadym Kochan 
378501ef306SVadym Kochan 	err = register_netdev(dev);
379501ef306SVadym Kochan 	if (err)
380501ef306SVadym Kochan 		goto err_register_netdev;
381501ef306SVadym Kochan 
38234dd1710SVadym Kochan 	prestera_devlink_port_set(port);
38334dd1710SVadym Kochan 
384501ef306SVadym Kochan 	return 0;
385501ef306SVadym Kochan 
386501ef306SVadym Kochan err_register_netdev:
387501ef306SVadym Kochan 	prestera_port_list_del(port);
388501ef306SVadym Kochan err_port_init:
38934dd1710SVadym Kochan 	prestera_devlink_port_unregister(port);
39034dd1710SVadym Kochan err_dl_port_register:
39134dd1710SVadym Kochan err_port_info_get:
392501ef306SVadym Kochan 	free_netdev(dev);
393501ef306SVadym Kochan 	return err;
394501ef306SVadym Kochan }
395501ef306SVadym Kochan 
396501ef306SVadym Kochan static void prestera_port_destroy(struct prestera_port *port)
397501ef306SVadym Kochan {
398501ef306SVadym Kochan 	struct net_device *dev = port->dev;
399501ef306SVadym Kochan 
400501ef306SVadym Kochan 	cancel_delayed_work_sync(&port->cached_hw_stats.caching_dw);
40134dd1710SVadym Kochan 	prestera_devlink_port_clear(port);
402501ef306SVadym Kochan 	unregister_netdev(dev);
403501ef306SVadym Kochan 	prestera_port_list_del(port);
40434dd1710SVadym Kochan 	prestera_devlink_port_unregister(port);
405501ef306SVadym Kochan 	free_netdev(dev);
406501ef306SVadym Kochan }
407501ef306SVadym Kochan 
408501ef306SVadym Kochan static void prestera_destroy_ports(struct prestera_switch *sw)
409501ef306SVadym Kochan {
410501ef306SVadym Kochan 	struct prestera_port *port, *tmp;
411501ef306SVadym Kochan 
412501ef306SVadym Kochan 	list_for_each_entry_safe(port, tmp, &sw->port_list, list)
413501ef306SVadym Kochan 		prestera_port_destroy(port);
414501ef306SVadym Kochan }
415501ef306SVadym Kochan 
416501ef306SVadym Kochan static int prestera_create_ports(struct prestera_switch *sw)
417501ef306SVadym Kochan {
418501ef306SVadym Kochan 	struct prestera_port *port, *tmp;
419501ef306SVadym Kochan 	u32 port_idx;
420501ef306SVadym Kochan 	int err;
421501ef306SVadym Kochan 
422501ef306SVadym Kochan 	for (port_idx = 0; port_idx < sw->port_count; port_idx++) {
423501ef306SVadym Kochan 		err = prestera_port_create(sw, port_idx);
424501ef306SVadym Kochan 		if (err)
425501ef306SVadym Kochan 			goto err_port_create;
426501ef306SVadym Kochan 	}
427501ef306SVadym Kochan 
428501ef306SVadym Kochan 	return 0;
429501ef306SVadym Kochan 
430501ef306SVadym Kochan err_port_create:
431501ef306SVadym Kochan 	list_for_each_entry_safe(port, tmp, &sw->port_list, list)
432501ef306SVadym Kochan 		prestera_port_destroy(port);
433501ef306SVadym Kochan 
434501ef306SVadym Kochan 	return err;
435501ef306SVadym Kochan }
436501ef306SVadym Kochan 
437501ef306SVadym Kochan static void prestera_port_handle_event(struct prestera_switch *sw,
438501ef306SVadym Kochan 				       struct prestera_event *evt, void *arg)
439501ef306SVadym Kochan {
440501ef306SVadym Kochan 	struct delayed_work *caching_dw;
441501ef306SVadym Kochan 	struct prestera_port *port;
442501ef306SVadym Kochan 
443501ef306SVadym Kochan 	port = prestera_find_port(sw, evt->port_evt.port_id);
444501ef306SVadym Kochan 	if (!port || !port->dev)
445501ef306SVadym Kochan 		return;
446501ef306SVadym Kochan 
447501ef306SVadym Kochan 	caching_dw = &port->cached_hw_stats.caching_dw;
448501ef306SVadym Kochan 
449501ef306SVadym Kochan 	if (evt->id == PRESTERA_PORT_EVENT_STATE_CHANGED) {
450501ef306SVadym Kochan 		if (evt->port_evt.data.oper_state) {
451501ef306SVadym Kochan 			netif_carrier_on(port->dev);
452501ef306SVadym Kochan 			if (!delayed_work_pending(caching_dw))
453501ef306SVadym Kochan 				queue_delayed_work(prestera_wq, caching_dw, 0);
45433398048SVadym Kochan 		} else if (netif_running(port->dev) &&
45533398048SVadym Kochan 			   netif_carrier_ok(port->dev)) {
456501ef306SVadym Kochan 			netif_carrier_off(port->dev);
457501ef306SVadym Kochan 			if (delayed_work_pending(caching_dw))
458501ef306SVadym Kochan 				cancel_delayed_work(caching_dw);
459501ef306SVadym Kochan 		}
460501ef306SVadym Kochan 	}
461501ef306SVadym Kochan }
462501ef306SVadym Kochan 
463501ef306SVadym Kochan static int prestera_event_handlers_register(struct prestera_switch *sw)
464501ef306SVadym Kochan {
465501ef306SVadym Kochan 	return prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_PORT,
466501ef306SVadym Kochan 						  prestera_port_handle_event,
467501ef306SVadym Kochan 						  NULL);
468501ef306SVadym Kochan }
469501ef306SVadym Kochan 
470501ef306SVadym Kochan static void prestera_event_handlers_unregister(struct prestera_switch *sw)
471501ef306SVadym Kochan {
472501ef306SVadym Kochan 	prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_PORT,
473501ef306SVadym Kochan 					     prestera_port_handle_event);
474501ef306SVadym Kochan }
475501ef306SVadym Kochan 
476501ef306SVadym Kochan static int prestera_switch_set_base_mac_addr(struct prestera_switch *sw)
477501ef306SVadym Kochan {
478501ef306SVadym Kochan 	struct device_node *base_mac_np;
479501ef306SVadym Kochan 	struct device_node *np;
48083216e39SMichael Walle 	int ret;
481501ef306SVadym Kochan 
482501ef306SVadym Kochan 	np = of_find_compatible_node(NULL, NULL, "marvell,prestera");
483501ef306SVadym Kochan 	base_mac_np = of_parse_phandle(np, "base-mac-provider", 0);
484501ef306SVadym Kochan 
48583216e39SMichael Walle 	ret = of_get_mac_address(base_mac_np, sw->base_mac);
48683216e39SMichael Walle 	if (ret) {
487501ef306SVadym Kochan 		eth_random_addr(sw->base_mac);
488501ef306SVadym Kochan 		dev_info(prestera_dev(sw), "using random base mac address\n");
489501ef306SVadym Kochan 	}
49083216e39SMichael Walle 	of_node_put(base_mac_np);
491501ef306SVadym Kochan 
492501ef306SVadym Kochan 	return prestera_hw_switch_mac_set(sw, sw->base_mac);
493501ef306SVadym Kochan }
494501ef306SVadym Kochan 
495255213caSSerhiy Boiko struct prestera_lag *prestera_lag_by_id(struct prestera_switch *sw, u16 id)
496255213caSSerhiy Boiko {
497255213caSSerhiy Boiko 	return id < sw->lag_max ? &sw->lags[id] : NULL;
498255213caSSerhiy Boiko }
499255213caSSerhiy Boiko 
500255213caSSerhiy Boiko static struct prestera_lag *prestera_lag_by_dev(struct prestera_switch *sw,
501255213caSSerhiy Boiko 						struct net_device *dev)
502255213caSSerhiy Boiko {
503255213caSSerhiy Boiko 	struct prestera_lag *lag;
504255213caSSerhiy Boiko 	u16 id;
505255213caSSerhiy Boiko 
506255213caSSerhiy Boiko 	for (id = 0; id < sw->lag_max; id++) {
507255213caSSerhiy Boiko 		lag = &sw->lags[id];
508255213caSSerhiy Boiko 		if (lag->dev == dev)
509255213caSSerhiy Boiko 			return lag;
510255213caSSerhiy Boiko 	}
511255213caSSerhiy Boiko 
512255213caSSerhiy Boiko 	return NULL;
513255213caSSerhiy Boiko }
514255213caSSerhiy Boiko 
515255213caSSerhiy Boiko static struct prestera_lag *prestera_lag_create(struct prestera_switch *sw,
516255213caSSerhiy Boiko 						struct net_device *lag_dev)
517255213caSSerhiy Boiko {
518255213caSSerhiy Boiko 	struct prestera_lag *lag = NULL;
519255213caSSerhiy Boiko 	u16 id;
520255213caSSerhiy Boiko 
521255213caSSerhiy Boiko 	for (id = 0; id < sw->lag_max; id++) {
522255213caSSerhiy Boiko 		lag = &sw->lags[id];
523255213caSSerhiy Boiko 		if (!lag->dev)
524255213caSSerhiy Boiko 			break;
525255213caSSerhiy Boiko 	}
526255213caSSerhiy Boiko 	if (lag) {
527255213caSSerhiy Boiko 		INIT_LIST_HEAD(&lag->members);
528255213caSSerhiy Boiko 		lag->dev = lag_dev;
529255213caSSerhiy Boiko 	}
530255213caSSerhiy Boiko 
531255213caSSerhiy Boiko 	return lag;
532255213caSSerhiy Boiko }
533255213caSSerhiy Boiko 
534255213caSSerhiy Boiko static void prestera_lag_destroy(struct prestera_switch *sw,
535255213caSSerhiy Boiko 				 struct prestera_lag *lag)
536255213caSSerhiy Boiko {
537255213caSSerhiy Boiko 	WARN_ON(!list_empty(&lag->members));
538255213caSSerhiy Boiko 	lag->member_count = 0;
539255213caSSerhiy Boiko 	lag->dev = NULL;
540255213caSSerhiy Boiko }
541255213caSSerhiy Boiko 
542255213caSSerhiy Boiko static int prestera_lag_port_add(struct prestera_port *port,
543255213caSSerhiy Boiko 				 struct net_device *lag_dev)
544255213caSSerhiy Boiko {
545255213caSSerhiy Boiko 	struct prestera_switch *sw = port->sw;
546255213caSSerhiy Boiko 	struct prestera_lag *lag;
547255213caSSerhiy Boiko 	int err;
548255213caSSerhiy Boiko 
549255213caSSerhiy Boiko 	lag = prestera_lag_by_dev(sw, lag_dev);
550255213caSSerhiy Boiko 	if (!lag) {
551255213caSSerhiy Boiko 		lag = prestera_lag_create(sw, lag_dev);
552255213caSSerhiy Boiko 		if (!lag)
553255213caSSerhiy Boiko 			return -ENOSPC;
554255213caSSerhiy Boiko 	}
555255213caSSerhiy Boiko 
556255213caSSerhiy Boiko 	if (lag->member_count >= sw->lag_member_max)
557255213caSSerhiy Boiko 		return -ENOSPC;
558255213caSSerhiy Boiko 
559255213caSSerhiy Boiko 	err = prestera_hw_lag_member_add(port, lag->lag_id);
560255213caSSerhiy Boiko 	if (err) {
561255213caSSerhiy Boiko 		if (!lag->member_count)
562255213caSSerhiy Boiko 			prestera_lag_destroy(sw, lag);
563255213caSSerhiy Boiko 		return err;
564255213caSSerhiy Boiko 	}
565255213caSSerhiy Boiko 
566255213caSSerhiy Boiko 	list_add(&port->lag_member, &lag->members);
567255213caSSerhiy Boiko 	lag->member_count++;
568255213caSSerhiy Boiko 	port->lag = lag;
569255213caSSerhiy Boiko 
570255213caSSerhiy Boiko 	return 0;
571255213caSSerhiy Boiko }
572255213caSSerhiy Boiko 
573255213caSSerhiy Boiko static int prestera_lag_port_del(struct prestera_port *port)
574255213caSSerhiy Boiko {
575255213caSSerhiy Boiko 	struct prestera_switch *sw = port->sw;
576255213caSSerhiy Boiko 	struct prestera_lag *lag = port->lag;
577255213caSSerhiy Boiko 	int err;
578255213caSSerhiy Boiko 
579255213caSSerhiy Boiko 	if (!lag || !lag->member_count)
580255213caSSerhiy Boiko 		return -EINVAL;
581255213caSSerhiy Boiko 
582255213caSSerhiy Boiko 	err = prestera_hw_lag_member_del(port, lag->lag_id);
583255213caSSerhiy Boiko 	if (err)
584255213caSSerhiy Boiko 		return err;
585255213caSSerhiy Boiko 
586255213caSSerhiy Boiko 	list_del(&port->lag_member);
587255213caSSerhiy Boiko 	lag->member_count--;
588255213caSSerhiy Boiko 	port->lag = NULL;
589255213caSSerhiy Boiko 
590255213caSSerhiy Boiko 	if (netif_is_bridge_port(lag->dev)) {
591255213caSSerhiy Boiko 		struct net_device *br_dev;
592255213caSSerhiy Boiko 
593255213caSSerhiy Boiko 		br_dev = netdev_master_upper_dev_get(lag->dev);
594255213caSSerhiy Boiko 
595255213caSSerhiy Boiko 		prestera_bridge_port_leave(br_dev, port);
596255213caSSerhiy Boiko 	}
597255213caSSerhiy Boiko 
598255213caSSerhiy Boiko 	if (!lag->member_count)
599255213caSSerhiy Boiko 		prestera_lag_destroy(sw, lag);
600255213caSSerhiy Boiko 
601255213caSSerhiy Boiko 	return 0;
602255213caSSerhiy Boiko }
603255213caSSerhiy Boiko 
604255213caSSerhiy Boiko bool prestera_port_is_lag_member(const struct prestera_port *port)
605255213caSSerhiy Boiko {
606255213caSSerhiy Boiko 	return !!port->lag;
607255213caSSerhiy Boiko }
608255213caSSerhiy Boiko 
609255213caSSerhiy Boiko u16 prestera_port_lag_id(const struct prestera_port *port)
610255213caSSerhiy Boiko {
611255213caSSerhiy Boiko 	return port->lag->lag_id;
612255213caSSerhiy Boiko }
613255213caSSerhiy Boiko 
614255213caSSerhiy Boiko static int prestera_lag_init(struct prestera_switch *sw)
615255213caSSerhiy Boiko {
616255213caSSerhiy Boiko 	u16 id;
617255213caSSerhiy Boiko 
618255213caSSerhiy Boiko 	sw->lags = kcalloc(sw->lag_max, sizeof(*sw->lags), GFP_KERNEL);
619255213caSSerhiy Boiko 	if (!sw->lags)
620255213caSSerhiy Boiko 		return -ENOMEM;
621255213caSSerhiy Boiko 
622255213caSSerhiy Boiko 	for (id = 0; id < sw->lag_max; id++)
623255213caSSerhiy Boiko 		sw->lags[id].lag_id = id;
624255213caSSerhiy Boiko 
625255213caSSerhiy Boiko 	return 0;
626255213caSSerhiy Boiko }
627255213caSSerhiy Boiko 
628255213caSSerhiy Boiko static void prestera_lag_fini(struct prestera_switch *sw)
629255213caSSerhiy Boiko {
630255213caSSerhiy Boiko 	u8 idx;
631255213caSSerhiy Boiko 
632255213caSSerhiy Boiko 	for (idx = 0; idx < sw->lag_max; idx++)
633255213caSSerhiy Boiko 		WARN_ON(sw->lags[idx].member_count);
634255213caSSerhiy Boiko 
635255213caSSerhiy Boiko 	kfree(sw->lags);
636255213caSSerhiy Boiko }
637255213caSSerhiy Boiko 
638e1189d9aSVadym Kochan bool prestera_netdev_check(const struct net_device *dev)
639e1189d9aSVadym Kochan {
640e1189d9aSVadym Kochan 	return dev->netdev_ops == &prestera_netdev_ops;
641e1189d9aSVadym Kochan }
642e1189d9aSVadym Kochan 
6438b0308feSDavid S. Miller static int prestera_lower_dev_walk(struct net_device *dev,
6448b0308feSDavid S. Miller 				   struct netdev_nested_priv *priv)
645e1189d9aSVadym Kochan {
6468b0308feSDavid S. Miller 	struct prestera_port **pport = (struct prestera_port **)priv->data;
647e1189d9aSVadym Kochan 
648e1189d9aSVadym Kochan 	if (prestera_netdev_check(dev)) {
649e1189d9aSVadym Kochan 		*pport = netdev_priv(dev);
650e1189d9aSVadym Kochan 		return 1;
651e1189d9aSVadym Kochan 	}
652e1189d9aSVadym Kochan 
653e1189d9aSVadym Kochan 	return 0;
654e1189d9aSVadym Kochan }
655e1189d9aSVadym Kochan 
656e1189d9aSVadym Kochan struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev)
657e1189d9aSVadym Kochan {
658e1189d9aSVadym Kochan 	struct prestera_port *port = NULL;
6598b0308feSDavid S. Miller 	struct netdev_nested_priv priv = {
6608b0308feSDavid S. Miller 		.data = (void *)&port,
6618b0308feSDavid S. Miller 	};
662e1189d9aSVadym Kochan 
663e1189d9aSVadym Kochan 	if (prestera_netdev_check(dev))
664e1189d9aSVadym Kochan 		return netdev_priv(dev);
665e1189d9aSVadym Kochan 
6668b0308feSDavid S. Miller 	netdev_walk_all_lower_dev(dev, prestera_lower_dev_walk, &priv);
667e1189d9aSVadym Kochan 
668e1189d9aSVadym Kochan 	return port;
669e1189d9aSVadym Kochan }
670e1189d9aSVadym Kochan 
671255213caSSerhiy Boiko static int prestera_netdev_port_lower_event(struct net_device *dev,
672255213caSSerhiy Boiko 					    unsigned long event, void *ptr)
673255213caSSerhiy Boiko {
674255213caSSerhiy Boiko 	struct netdev_notifier_changelowerstate_info *info = ptr;
675255213caSSerhiy Boiko 	struct netdev_lag_lower_state_info *lower_state_info;
676255213caSSerhiy Boiko 	struct prestera_port *port = netdev_priv(dev);
677255213caSSerhiy Boiko 	bool enabled;
678255213caSSerhiy Boiko 
679255213caSSerhiy Boiko 	if (!netif_is_lag_port(dev))
680255213caSSerhiy Boiko 		return 0;
681255213caSSerhiy Boiko 	if (!prestera_port_is_lag_member(port))
682255213caSSerhiy Boiko 		return 0;
683255213caSSerhiy Boiko 
684255213caSSerhiy Boiko 	lower_state_info = info->lower_state_info;
685255213caSSerhiy Boiko 	enabled = lower_state_info->link_up && lower_state_info->tx_enabled;
686255213caSSerhiy Boiko 
687255213caSSerhiy Boiko 	return prestera_hw_lag_member_enable(port, port->lag->lag_id, enabled);
688255213caSSerhiy Boiko }
689255213caSSerhiy Boiko 
690255213caSSerhiy Boiko static bool prestera_lag_master_check(struct net_device *lag_dev,
691255213caSSerhiy Boiko 				      struct netdev_lag_upper_info *info,
692255213caSSerhiy Boiko 				      struct netlink_ext_ack *ext_ack)
693255213caSSerhiy Boiko {
694255213caSSerhiy Boiko 	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
695255213caSSerhiy Boiko 		NL_SET_ERR_MSG_MOD(ext_ack, "Unsupported LAG Tx type");
696255213caSSerhiy Boiko 		return false;
697255213caSSerhiy Boiko 	}
698255213caSSerhiy Boiko 
699255213caSSerhiy Boiko 	return true;
700255213caSSerhiy Boiko }
701255213caSSerhiy Boiko 
702255213caSSerhiy Boiko static int prestera_netdev_port_event(struct net_device *lower,
703255213caSSerhiy Boiko 				      struct net_device *dev,
704e1189d9aSVadym Kochan 				      unsigned long event, void *ptr)
705e1189d9aSVadym Kochan {
7063d5048ccSVadym Kochan 	struct netdev_notifier_changeupper_info *info = ptr;
70782bbaa05SVadym Kochan 	struct prestera_port *port = netdev_priv(dev);
7083d5048ccSVadym Kochan 	struct netlink_ext_ack *extack;
7093d5048ccSVadym Kochan 	struct net_device *upper;
7103d5048ccSVadym Kochan 
7113d5048ccSVadym Kochan 	extack = netdev_notifier_info_to_extack(&info->info);
7123d5048ccSVadym Kochan 	upper = info->upper_dev;
7133d5048ccSVadym Kochan 
714e1189d9aSVadym Kochan 	switch (event) {
715e1189d9aSVadym Kochan 	case NETDEV_PRECHANGEUPPER:
716255213caSSerhiy Boiko 		if (!netif_is_bridge_master(upper) &&
717255213caSSerhiy Boiko 		    !netif_is_lag_master(upper)) {
7183d5048ccSVadym Kochan 			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
7193d5048ccSVadym Kochan 			return -EINVAL;
720e1189d9aSVadym Kochan 		}
7213d5048ccSVadym Kochan 
7223d5048ccSVadym Kochan 		if (!info->linking)
7233d5048ccSVadym Kochan 			break;
7243d5048ccSVadym Kochan 
7253d5048ccSVadym Kochan 		if (netdev_has_any_upper_dev(upper)) {
7263d5048ccSVadym Kochan 			NL_SET_ERR_MSG_MOD(extack, "Upper device is already enslaved");
7273d5048ccSVadym Kochan 			return -EINVAL;
7283d5048ccSVadym Kochan 		}
729255213caSSerhiy Boiko 
730255213caSSerhiy Boiko 		if (netif_is_lag_master(upper) &&
731255213caSSerhiy Boiko 		    !prestera_lag_master_check(upper, info->upper_info, extack))
732255213caSSerhiy Boiko 			return -EOPNOTSUPP;
733255213caSSerhiy Boiko 		if (netif_is_lag_master(upper) && vlan_uses_dev(dev)) {
734255213caSSerhiy Boiko 			NL_SET_ERR_MSG_MOD(extack,
735255213caSSerhiy Boiko 					   "Master device is a LAG master and port has a VLAN");
736255213caSSerhiy Boiko 			return -EINVAL;
737255213caSSerhiy Boiko 		}
738255213caSSerhiy Boiko 		if (netif_is_lag_port(dev) && is_vlan_dev(upper) &&
739255213caSSerhiy Boiko 		    !netif_is_lag_master(vlan_dev_real_dev(upper))) {
740255213caSSerhiy Boiko 			NL_SET_ERR_MSG_MOD(extack,
741255213caSSerhiy Boiko 					   "Can not put a VLAN on a LAG port");
742255213caSSerhiy Boiko 			return -EINVAL;
743255213caSSerhiy Boiko 		}
7443d5048ccSVadym Kochan 		break;
7453d5048ccSVadym Kochan 
7463d5048ccSVadym Kochan 	case NETDEV_CHANGEUPPER:
74782bbaa05SVadym Kochan 		if (netif_is_bridge_master(upper)) {
74882bbaa05SVadym Kochan 			if (info->linking)
7492f5dc00fSVladimir Oltean 				return prestera_bridge_port_join(upper, port,
7502f5dc00fSVladimir Oltean 								 extack);
75182bbaa05SVadym Kochan 			else
75282bbaa05SVadym Kochan 				prestera_bridge_port_leave(upper, port);
753255213caSSerhiy Boiko 		} else if (netif_is_lag_master(upper)) {
754255213caSSerhiy Boiko 			if (info->linking)
755255213caSSerhiy Boiko 				return prestera_lag_port_add(port, upper);
756255213caSSerhiy Boiko 			else
757255213caSSerhiy Boiko 				prestera_lag_port_del(port);
75882bbaa05SVadym Kochan 		}
7593d5048ccSVadym Kochan 		break;
760255213caSSerhiy Boiko 
761255213caSSerhiy Boiko 	case NETDEV_CHANGELOWERSTATE:
762255213caSSerhiy Boiko 		return prestera_netdev_port_lower_event(dev, event, ptr);
763255213caSSerhiy Boiko 	}
764255213caSSerhiy Boiko 
765255213caSSerhiy Boiko 	return 0;
766255213caSSerhiy Boiko }
767255213caSSerhiy Boiko 
768255213caSSerhiy Boiko static int prestera_netdevice_lag_event(struct net_device *lag_dev,
769255213caSSerhiy Boiko 					unsigned long event, void *ptr)
770255213caSSerhiy Boiko {
771255213caSSerhiy Boiko 	struct net_device *dev;
772255213caSSerhiy Boiko 	struct list_head *iter;
773255213caSSerhiy Boiko 	int err;
774255213caSSerhiy Boiko 
775255213caSSerhiy Boiko 	netdev_for_each_lower_dev(lag_dev, dev, iter) {
776255213caSSerhiy Boiko 		if (prestera_netdev_check(dev)) {
777255213caSSerhiy Boiko 			err = prestera_netdev_port_event(lag_dev, dev, event,
778255213caSSerhiy Boiko 							 ptr);
779255213caSSerhiy Boiko 			if (err)
780255213caSSerhiy Boiko 				return err;
781255213caSSerhiy Boiko 		}
7823d5048ccSVadym Kochan 	}
7833d5048ccSVadym Kochan 
7843d5048ccSVadym Kochan 	return 0;
785e1189d9aSVadym Kochan }
786e1189d9aSVadym Kochan 
787e1189d9aSVadym Kochan static int prestera_netdev_event_handler(struct notifier_block *nb,
788e1189d9aSVadym Kochan 					 unsigned long event, void *ptr)
789e1189d9aSVadym Kochan {
790e1189d9aSVadym Kochan 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
791e1189d9aSVadym Kochan 	int err = 0;
792e1189d9aSVadym Kochan 
793e1189d9aSVadym Kochan 	if (prestera_netdev_check(dev))
794255213caSSerhiy Boiko 		err = prestera_netdev_port_event(dev, dev, event, ptr);
795255213caSSerhiy Boiko 	else if (netif_is_lag_master(dev))
796255213caSSerhiy Boiko 		err = prestera_netdevice_lag_event(dev, event, ptr);
797e1189d9aSVadym Kochan 
798e1189d9aSVadym Kochan 	return notifier_from_errno(err);
799e1189d9aSVadym Kochan }
800e1189d9aSVadym Kochan 
801e1189d9aSVadym Kochan static int prestera_netdev_event_handler_register(struct prestera_switch *sw)
802e1189d9aSVadym Kochan {
803e1189d9aSVadym Kochan 	sw->netdev_nb.notifier_call = prestera_netdev_event_handler;
804e1189d9aSVadym Kochan 
805e1189d9aSVadym Kochan 	return register_netdevice_notifier(&sw->netdev_nb);
806e1189d9aSVadym Kochan }
807e1189d9aSVadym Kochan 
808e1189d9aSVadym Kochan static void prestera_netdev_event_handler_unregister(struct prestera_switch *sw)
809e1189d9aSVadym Kochan {
810e1189d9aSVadym Kochan 	unregister_netdevice_notifier(&sw->netdev_nb);
811e1189d9aSVadym Kochan }
812e1189d9aSVadym Kochan 
813501ef306SVadym Kochan static int prestera_switch_init(struct prestera_switch *sw)
814501ef306SVadym Kochan {
815501ef306SVadym Kochan 	int err;
816501ef306SVadym Kochan 
817501ef306SVadym Kochan 	err = prestera_hw_switch_init(sw);
818501ef306SVadym Kochan 	if (err) {
819501ef306SVadym Kochan 		dev_err(prestera_dev(sw), "Failed to init Switch device\n");
820501ef306SVadym Kochan 		return err;
821501ef306SVadym Kochan 	}
822501ef306SVadym Kochan 
823501ef306SVadym Kochan 	rwlock_init(&sw->port_list_lock);
824501ef306SVadym Kochan 	INIT_LIST_HEAD(&sw->port_list);
825501ef306SVadym Kochan 
826501ef306SVadym Kochan 	err = prestera_switch_set_base_mac_addr(sw);
827501ef306SVadym Kochan 	if (err)
828501ef306SVadym Kochan 		return err;
829501ef306SVadym Kochan 
830e1189d9aSVadym Kochan 	err = prestera_netdev_event_handler_register(sw);
831501ef306SVadym Kochan 	if (err)
832501ef306SVadym Kochan 		return err;
833501ef306SVadym Kochan 
834e1189d9aSVadym Kochan 	err = prestera_switchdev_init(sw);
835e1189d9aSVadym Kochan 	if (err)
836e1189d9aSVadym Kochan 		goto err_swdev_register;
837e1189d9aSVadym Kochan 
838e1189d9aSVadym Kochan 	err = prestera_rxtx_switch_init(sw);
839e1189d9aSVadym Kochan 	if (err)
840e1189d9aSVadym Kochan 		goto err_rxtx_register;
841e1189d9aSVadym Kochan 
842501ef306SVadym Kochan 	err = prestera_event_handlers_register(sw);
843501ef306SVadym Kochan 	if (err)
844501ef306SVadym Kochan 		goto err_handlers_register;
845501ef306SVadym Kochan 
8468b474a9fSSerhiy Boiko 	err = prestera_acl_init(sw);
8478b474a9fSSerhiy Boiko 	if (err)
8488b474a9fSSerhiy Boiko 		goto err_acl_init;
8498b474a9fSSerhiy Boiko 
85013defa27SSerhiy Boiko 	err = prestera_span_init(sw);
85113defa27SSerhiy Boiko 	if (err)
85213defa27SSerhiy Boiko 		goto err_span_init;
85313defa27SSerhiy Boiko 
8544beb0c24SLeon Romanovsky 	err = prestera_devlink_traps_register(sw);
85534dd1710SVadym Kochan 	if (err)
85634dd1710SVadym Kochan 		goto err_dl_register;
85734dd1710SVadym Kochan 
858255213caSSerhiy Boiko 	err = prestera_lag_init(sw);
859255213caSSerhiy Boiko 	if (err)
860255213caSSerhiy Boiko 		goto err_lag_init;
861255213caSSerhiy Boiko 
862501ef306SVadym Kochan 	err = prestera_create_ports(sw);
863501ef306SVadym Kochan 	if (err)
864501ef306SVadym Kochan 		goto err_ports_create;
865501ef306SVadym Kochan 
8664beb0c24SLeon Romanovsky 	prestera_devlink_register(sw);
867501ef306SVadym Kochan 	return 0;
868501ef306SVadym Kochan 
869501ef306SVadym Kochan err_ports_create:
870255213caSSerhiy Boiko 	prestera_lag_fini(sw);
871255213caSSerhiy Boiko err_lag_init:
8724beb0c24SLeon Romanovsky 	prestera_devlink_traps_unregister(sw);
87334dd1710SVadym Kochan err_dl_register:
87413defa27SSerhiy Boiko 	prestera_span_fini(sw);
87513defa27SSerhiy Boiko err_span_init:
8768b474a9fSSerhiy Boiko 	prestera_acl_fini(sw);
8778b474a9fSSerhiy Boiko err_acl_init:
878501ef306SVadym Kochan 	prestera_event_handlers_unregister(sw);
879501ef306SVadym Kochan err_handlers_register:
880501ef306SVadym Kochan 	prestera_rxtx_switch_fini(sw);
881e1189d9aSVadym Kochan err_rxtx_register:
882e1189d9aSVadym Kochan 	prestera_switchdev_fini(sw);
883e1189d9aSVadym Kochan err_swdev_register:
884e1189d9aSVadym Kochan 	prestera_netdev_event_handler_unregister(sw);
885501ef306SVadym Kochan 	prestera_hw_switch_fini(sw);
886501ef306SVadym Kochan 
887501ef306SVadym Kochan 	return err;
888501ef306SVadym Kochan }
889501ef306SVadym Kochan 
890501ef306SVadym Kochan static void prestera_switch_fini(struct prestera_switch *sw)
891501ef306SVadym Kochan {
8924beb0c24SLeon Romanovsky 	prestera_devlink_unregister(sw);
893501ef306SVadym Kochan 	prestera_destroy_ports(sw);
894255213caSSerhiy Boiko 	prestera_lag_fini(sw);
8954beb0c24SLeon Romanovsky 	prestera_devlink_traps_unregister(sw);
89613defa27SSerhiy Boiko 	prestera_span_fini(sw);
8978b474a9fSSerhiy Boiko 	prestera_acl_fini(sw);
898501ef306SVadym Kochan 	prestera_event_handlers_unregister(sw);
899501ef306SVadym Kochan 	prestera_rxtx_switch_fini(sw);
900e1189d9aSVadym Kochan 	prestera_switchdev_fini(sw);
901e1189d9aSVadym Kochan 	prestera_netdev_event_handler_unregister(sw);
902501ef306SVadym Kochan 	prestera_hw_switch_fini(sw);
903501ef306SVadym Kochan }
904501ef306SVadym Kochan 
905501ef306SVadym Kochan int prestera_device_register(struct prestera_device *dev)
906501ef306SVadym Kochan {
907501ef306SVadym Kochan 	struct prestera_switch *sw;
908501ef306SVadym Kochan 	int err;
909501ef306SVadym Kochan 
910919d13a7SLeon Romanovsky 	sw = prestera_devlink_alloc(dev);
911501ef306SVadym Kochan 	if (!sw)
912501ef306SVadym Kochan 		return -ENOMEM;
913501ef306SVadym Kochan 
914501ef306SVadym Kochan 	dev->priv = sw;
915501ef306SVadym Kochan 	sw->dev = dev;
916501ef306SVadym Kochan 
917501ef306SVadym Kochan 	err = prestera_switch_init(sw);
918501ef306SVadym Kochan 	if (err) {
91934dd1710SVadym Kochan 		prestera_devlink_free(sw);
920501ef306SVadym Kochan 		return err;
921501ef306SVadym Kochan 	}
922501ef306SVadym Kochan 
923501ef306SVadym Kochan 	return 0;
924501ef306SVadym Kochan }
925501ef306SVadym Kochan EXPORT_SYMBOL(prestera_device_register);
926501ef306SVadym Kochan 
927501ef306SVadym Kochan void prestera_device_unregister(struct prestera_device *dev)
928501ef306SVadym Kochan {
929501ef306SVadym Kochan 	struct prestera_switch *sw = dev->priv;
930501ef306SVadym Kochan 
931501ef306SVadym Kochan 	prestera_switch_fini(sw);
93234dd1710SVadym Kochan 	prestera_devlink_free(sw);
933501ef306SVadym Kochan }
934501ef306SVadym Kochan EXPORT_SYMBOL(prestera_device_unregister);
935501ef306SVadym Kochan 
936501ef306SVadym Kochan static int __init prestera_module_init(void)
937501ef306SVadym Kochan {
938501ef306SVadym Kochan 	prestera_wq = alloc_workqueue("prestera", 0, 0);
939501ef306SVadym Kochan 	if (!prestera_wq)
940501ef306SVadym Kochan 		return -ENOMEM;
941501ef306SVadym Kochan 
942501ef306SVadym Kochan 	return 0;
943501ef306SVadym Kochan }
944501ef306SVadym Kochan 
945501ef306SVadym Kochan static void __exit prestera_module_exit(void)
946501ef306SVadym Kochan {
947501ef306SVadym Kochan 	destroy_workqueue(prestera_wq);
948501ef306SVadym Kochan }
949501ef306SVadym Kochan 
950501ef306SVadym Kochan module_init(prestera_module_init);
951501ef306SVadym Kochan module_exit(prestera_module_exit);
952501ef306SVadym Kochan 
953501ef306SVadym Kochan MODULE_LICENSE("Dual BSD/GPL");
954501ef306SVadym Kochan MODULE_DESCRIPTION("Marvell Prestera switch driver");
955