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>
1252323ef7SOleksandr Mazur #include <linux/phylink.h>
13501ef306SVadym Kochan 
14501ef306SVadym Kochan #include "prestera.h"
15501ef306SVadym Kochan #include "prestera_hw.h"
168b474a9fSSerhiy Boiko #include "prestera_acl.h"
178b474a9fSSerhiy Boiko #include "prestera_flow.h"
1813defa27SSerhiy Boiko #include "prestera_span.h"
19501ef306SVadym Kochan #include "prestera_rxtx.h"
2034dd1710SVadym Kochan #include "prestera_devlink.h"
21a97d3c69SVadym Kochan #include "prestera_ethtool.h"
226e36c7bcSVolodymyr Mytnyk #include "prestera_counter.h"
23e1189d9aSVadym Kochan #include "prestera_switchdev.h"
24501ef306SVadym Kochan 
25501ef306SVadym Kochan #define PRESTERA_MTU_DEFAULT	1536
26501ef306SVadym Kochan 
27501ef306SVadym Kochan #define PRESTERA_STATS_DELAY_MS	1000
28501ef306SVadym Kochan 
29501ef306SVadym Kochan #define PRESTERA_MAC_ADDR_NUM_MAX	255
30501ef306SVadym Kochan 
31501ef306SVadym Kochan static struct workqueue_struct *prestera_wq;
324394fbcbSYevhen Orlov static struct workqueue_struct *prestera_owq;
334394fbcbSYevhen Orlov 
prestera_queue_work(struct work_struct * work)344394fbcbSYevhen Orlov void prestera_queue_work(struct work_struct *work)
354394fbcbSYevhen Orlov {
364394fbcbSYevhen Orlov 	queue_work(prestera_owq, work);
374394fbcbSYevhen Orlov }
38501ef306SVadym Kochan 
prestera_queue_delayed_work(struct delayed_work * work,unsigned long delay)3990b6f9c0SYevhen Orlov void prestera_queue_delayed_work(struct delayed_work *work, unsigned long delay)
4090b6f9c0SYevhen Orlov {
4190b6f9c0SYevhen Orlov 	queue_delayed_work(prestera_wq, work, delay);
4290b6f9c0SYevhen Orlov }
4390b6f9c0SYevhen Orlov 
prestera_queue_drain(void)4490b6f9c0SYevhen Orlov void prestera_queue_drain(void)
4590b6f9c0SYevhen Orlov {
4690b6f9c0SYevhen Orlov 	drain_workqueue(prestera_wq);
4790b6f9c0SYevhen Orlov 	drain_workqueue(prestera_owq);
4890b6f9c0SYevhen Orlov }
4990b6f9c0SYevhen Orlov 
prestera_port_learning_set(struct prestera_port * port,bool learn)50116f5af7SOleksandr Mazur int prestera_port_learning_set(struct prestera_port *port, bool learn)
51116f5af7SOleksandr Mazur {
52116f5af7SOleksandr Mazur 	return prestera_hw_port_learning_set(port, learn);
53116f5af7SOleksandr Mazur }
54116f5af7SOleksandr Mazur 
prestera_port_uc_flood_set(struct prestera_port * port,bool flood)55116f5af7SOleksandr Mazur int prestera_port_uc_flood_set(struct prestera_port *port, bool flood)
56116f5af7SOleksandr Mazur {
57116f5af7SOleksandr Mazur 	return prestera_hw_port_uc_flood_set(port, flood);
58116f5af7SOleksandr Mazur }
59116f5af7SOleksandr Mazur 
prestera_port_mc_flood_set(struct prestera_port * port,bool flood)60116f5af7SOleksandr Mazur int prestera_port_mc_flood_set(struct prestera_port *port, bool flood)
61116f5af7SOleksandr Mazur {
62116f5af7SOleksandr Mazur 	return prestera_hw_port_mc_flood_set(port, flood);
63116f5af7SOleksandr Mazur }
64116f5af7SOleksandr Mazur 
prestera_port_br_locked_set(struct prestera_port * port,bool br_locked)6573ef239cSOleksandr Mazur int prestera_port_br_locked_set(struct prestera_port *port, bool br_locked)
6673ef239cSOleksandr Mazur {
6773ef239cSOleksandr Mazur 	return prestera_hw_port_br_locked_set(port, br_locked);
6873ef239cSOleksandr Mazur }
6973ef239cSOleksandr Mazur 
prestera_port_pvid_set(struct prestera_port * port,u16 vid)70e1189d9aSVadym Kochan int prestera_port_pvid_set(struct prestera_port *port, u16 vid)
71e1189d9aSVadym Kochan {
72e1189d9aSVadym Kochan 	enum prestera_accept_frm_type frm_type;
73e1189d9aSVadym Kochan 	int err;
74e1189d9aSVadym Kochan 
75e1189d9aSVadym Kochan 	frm_type = PRESTERA_ACCEPT_FRAME_TYPE_TAGGED;
76e1189d9aSVadym Kochan 
77e1189d9aSVadym Kochan 	if (vid) {
78e1189d9aSVadym Kochan 		err = prestera_hw_vlan_port_vid_set(port, vid);
79e1189d9aSVadym Kochan 		if (err)
80e1189d9aSVadym Kochan 			return err;
81e1189d9aSVadym Kochan 
82e1189d9aSVadym Kochan 		frm_type = PRESTERA_ACCEPT_FRAME_TYPE_ALL;
83e1189d9aSVadym Kochan 	}
84e1189d9aSVadym Kochan 
85e1189d9aSVadym Kochan 	err = prestera_hw_port_accept_frm_type(port, frm_type);
86e1189d9aSVadym Kochan 	if (err && frm_type == PRESTERA_ACCEPT_FRAME_TYPE_ALL)
87e1189d9aSVadym Kochan 		prestera_hw_vlan_port_vid_set(port, port->pvid);
88e1189d9aSVadym Kochan 
89e1189d9aSVadym Kochan 	port->pvid = vid;
90e1189d9aSVadym Kochan 	return 0;
91e1189d9aSVadym Kochan }
92e1189d9aSVadym Kochan 
prestera_port_find_by_hwid(struct prestera_switch * sw,u32 dev_id,u32 hw_id)93501ef306SVadym Kochan struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
94501ef306SVadym Kochan 						 u32 dev_id, u32 hw_id)
95501ef306SVadym Kochan {
968b681bd7SYevhen Orlov 	struct prestera_port *port = NULL, *tmp;
97501ef306SVadym Kochan 
98501ef306SVadym Kochan 	read_lock(&sw->port_list_lock);
998b681bd7SYevhen Orlov 	list_for_each_entry(tmp, &sw->port_list, list) {
1008b681bd7SYevhen Orlov 		if (tmp->dev_id == dev_id && tmp->hw_id == hw_id) {
1018b681bd7SYevhen Orlov 			port = tmp;
102501ef306SVadym Kochan 			break;
103501ef306SVadym Kochan 		}
1048b681bd7SYevhen Orlov 	}
105501ef306SVadym Kochan 	read_unlock(&sw->port_list_lock);
106501ef306SVadym Kochan 
107501ef306SVadym Kochan 	return port;
108501ef306SVadym Kochan }
109501ef306SVadym Kochan 
prestera_find_port(struct prestera_switch * sw,u32 id)110e1189d9aSVadym Kochan struct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id)
111501ef306SVadym Kochan {
1128b681bd7SYevhen Orlov 	struct prestera_port *port = NULL, *tmp;
113501ef306SVadym Kochan 
114501ef306SVadym Kochan 	read_lock(&sw->port_list_lock);
1158b681bd7SYevhen Orlov 	list_for_each_entry(tmp, &sw->port_list, list) {
1168b681bd7SYevhen Orlov 		if (tmp->id == id) {
1178b681bd7SYevhen Orlov 			port = tmp;
118501ef306SVadym Kochan 			break;
119501ef306SVadym Kochan 		}
1208b681bd7SYevhen Orlov 	}
121501ef306SVadym Kochan 	read_unlock(&sw->port_list_lock);
122501ef306SVadym Kochan 
123501ef306SVadym Kochan 	return port;
124501ef306SVadym Kochan }
125501ef306SVadym Kochan 
prestera_switch_get(struct net_device * dev)126deef0d6aSOleksandr Mazur struct prestera_switch *prestera_switch_get(struct net_device *dev)
127deef0d6aSOleksandr Mazur {
128deef0d6aSOleksandr Mazur 	struct prestera_port *port;
129deef0d6aSOleksandr Mazur 
130deef0d6aSOleksandr Mazur 	port = prestera_port_dev_lower_find(dev);
131deef0d6aSOleksandr Mazur 	return port ? port->sw : NULL;
132deef0d6aSOleksandr Mazur }
133deef0d6aSOleksandr Mazur 
prestera_port_cfg_mac_read(struct prestera_port * port,struct prestera_port_mac_config * cfg)134bb5dbf2cSVolodymyr Mytnyk int prestera_port_cfg_mac_read(struct prestera_port *port,
135bb5dbf2cSVolodymyr Mytnyk 			       struct prestera_port_mac_config *cfg)
136501ef306SVadym Kochan {
137bb5dbf2cSVolodymyr Mytnyk 	*cfg = port->cfg_mac;
138bb5dbf2cSVolodymyr Mytnyk 	return 0;
139bb5dbf2cSVolodymyr Mytnyk }
140bb5dbf2cSVolodymyr Mytnyk 
prestera_port_cfg_mac_write(struct prestera_port * port,struct prestera_port_mac_config * cfg)141bb5dbf2cSVolodymyr Mytnyk int prestera_port_cfg_mac_write(struct prestera_port *port,
142bb5dbf2cSVolodymyr Mytnyk 				struct prestera_port_mac_config *cfg)
143bb5dbf2cSVolodymyr Mytnyk {
144501ef306SVadym Kochan 	int err;
145501ef306SVadym Kochan 
146bb5dbf2cSVolodymyr Mytnyk 	err = prestera_hw_port_mac_mode_set(port, cfg->admin,
147bb5dbf2cSVolodymyr Mytnyk 					    cfg->mode, cfg->inband, cfg->speed,
148bb5dbf2cSVolodymyr Mytnyk 					    cfg->duplex, cfg->fec);
149501ef306SVadym Kochan 	if (err)
150501ef306SVadym Kochan 		return err;
151501ef306SVadym Kochan 
152bb5dbf2cSVolodymyr Mytnyk 	port->cfg_mac = *cfg;
153bb5dbf2cSVolodymyr Mytnyk 	return 0;
154bb5dbf2cSVolodymyr Mytnyk }
155bb5dbf2cSVolodymyr Mytnyk 
prestera_port_open(struct net_device * dev)156bb5dbf2cSVolodymyr Mytnyk static int prestera_port_open(struct net_device *dev)
157bb5dbf2cSVolodymyr Mytnyk {
158bb5dbf2cSVolodymyr Mytnyk 	struct prestera_port *port = netdev_priv(dev);
159bb5dbf2cSVolodymyr Mytnyk 	struct prestera_port_mac_config cfg_mac;
160bb5dbf2cSVolodymyr Mytnyk 	int err = 0;
161bb5dbf2cSVolodymyr Mytnyk 
16252323ef7SOleksandr Mazur 	if (port->phy_link) {
16352323ef7SOleksandr Mazur 		phylink_start(port->phy_link);
16452323ef7SOleksandr Mazur 	} else {
165bb5dbf2cSVolodymyr Mytnyk 		if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) {
166bb5dbf2cSVolodymyr Mytnyk 			err = prestera_port_cfg_mac_read(port, &cfg_mac);
167bb5dbf2cSVolodymyr Mytnyk 			if (!err) {
168bb5dbf2cSVolodymyr Mytnyk 				cfg_mac.admin = true;
16952323ef7SOleksandr Mazur 				err = prestera_port_cfg_mac_write(port,
17052323ef7SOleksandr Mazur 								  &cfg_mac);
171bb5dbf2cSVolodymyr Mytnyk 			}
172bb5dbf2cSVolodymyr Mytnyk 		} else {
173bb5dbf2cSVolodymyr Mytnyk 			port->cfg_phy.admin = true;
17452323ef7SOleksandr Mazur 			err = prestera_hw_port_phy_mode_set(port, true,
17552323ef7SOleksandr Mazur 							    port->autoneg,
176bb5dbf2cSVolodymyr Mytnyk 							    port->cfg_phy.mode,
177bb5dbf2cSVolodymyr Mytnyk 							    port->adver_link_modes,
178bb5dbf2cSVolodymyr Mytnyk 							    port->cfg_phy.mdix);
179bb5dbf2cSVolodymyr Mytnyk 		}
18052323ef7SOleksandr Mazur 	}
181bb5dbf2cSVolodymyr Mytnyk 
182501ef306SVadym Kochan 	netif_start_queue(dev);
183501ef306SVadym Kochan 
184bb5dbf2cSVolodymyr Mytnyk 	return err;
185501ef306SVadym Kochan }
186501ef306SVadym Kochan 
prestera_port_close(struct net_device * dev)187501ef306SVadym Kochan static int prestera_port_close(struct net_device *dev)
188501ef306SVadym Kochan {
189501ef306SVadym Kochan 	struct prestera_port *port = netdev_priv(dev);
190bb5dbf2cSVolodymyr Mytnyk 	struct prestera_port_mac_config cfg_mac;
191bb5dbf2cSVolodymyr Mytnyk 	int err = 0;
192501ef306SVadym Kochan 
193501ef306SVadym Kochan 	netif_stop_queue(dev);
194501ef306SVadym Kochan 
19552323ef7SOleksandr Mazur 	if (port->phy_link) {
19652323ef7SOleksandr Mazur 		phylink_stop(port->phy_link);
19752323ef7SOleksandr Mazur 		phylink_disconnect_phy(port->phy_link);
19852323ef7SOleksandr Mazur 		err = prestera_port_cfg_mac_read(port, &cfg_mac);
19952323ef7SOleksandr Mazur 		if (!err) {
20052323ef7SOleksandr Mazur 			cfg_mac.admin = false;
20152323ef7SOleksandr Mazur 			prestera_port_cfg_mac_write(port, &cfg_mac);
20252323ef7SOleksandr Mazur 		}
20352323ef7SOleksandr Mazur 	} else {
204bb5dbf2cSVolodymyr Mytnyk 		if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) {
205bb5dbf2cSVolodymyr Mytnyk 			err = prestera_port_cfg_mac_read(port, &cfg_mac);
206bb5dbf2cSVolodymyr Mytnyk 			if (!err) {
207bb5dbf2cSVolodymyr Mytnyk 				cfg_mac.admin = false;
208bb5dbf2cSVolodymyr Mytnyk 				prestera_port_cfg_mac_write(port, &cfg_mac);
209bb5dbf2cSVolodymyr Mytnyk 			}
210bb5dbf2cSVolodymyr Mytnyk 		} else {
211bb5dbf2cSVolodymyr Mytnyk 			port->cfg_phy.admin = false;
212bb5dbf2cSVolodymyr Mytnyk 			err = prestera_hw_port_phy_mode_set(port, false, port->autoneg,
213bb5dbf2cSVolodymyr Mytnyk 							    port->cfg_phy.mode,
214bb5dbf2cSVolodymyr Mytnyk 							    port->adver_link_modes,
215bb5dbf2cSVolodymyr Mytnyk 							    port->cfg_phy.mdix);
216bb5dbf2cSVolodymyr Mytnyk 		}
21752323ef7SOleksandr Mazur 	}
218bb5dbf2cSVolodymyr Mytnyk 
219bb5dbf2cSVolodymyr Mytnyk 	return err;
220501ef306SVadym Kochan }
221501ef306SVadym Kochan 
22252323ef7SOleksandr Mazur static void
prestera_port_mac_state_cache_read(struct prestera_port * port,struct prestera_port_mac_state * state)22352323ef7SOleksandr Mazur prestera_port_mac_state_cache_read(struct prestera_port *port,
22452323ef7SOleksandr Mazur 				   struct prestera_port_mac_state *state)
22552323ef7SOleksandr Mazur {
22652323ef7SOleksandr Mazur 	spin_lock(&port->state_mac_lock);
22752323ef7SOleksandr Mazur 	*state = port->state_mac;
22852323ef7SOleksandr Mazur 	spin_unlock(&port->state_mac_lock);
22952323ef7SOleksandr Mazur }
23052323ef7SOleksandr Mazur 
23152323ef7SOleksandr Mazur static void
prestera_port_mac_state_cache_write(struct prestera_port * port,struct prestera_port_mac_state * state)23252323ef7SOleksandr Mazur prestera_port_mac_state_cache_write(struct prestera_port *port,
23352323ef7SOleksandr Mazur 				    struct prestera_port_mac_state *state)
23452323ef7SOleksandr Mazur {
23552323ef7SOleksandr Mazur 	spin_lock(&port->state_mac_lock);
23652323ef7SOleksandr Mazur 	port->state_mac = *state;
23752323ef7SOleksandr Mazur 	spin_unlock(&port->state_mac_lock);
23852323ef7SOleksandr Mazur }
23952323ef7SOleksandr Mazur 
prestera_pcs_to_port(struct phylink_pcs * pcs)24052323ef7SOleksandr Mazur static struct prestera_port *prestera_pcs_to_port(struct phylink_pcs *pcs)
24152323ef7SOleksandr Mazur {
24252323ef7SOleksandr Mazur 	return container_of(pcs, struct prestera_port, phylink_pcs);
24352323ef7SOleksandr Mazur }
24452323ef7SOleksandr Mazur 
prestera_mac_config(struct phylink_config * config,unsigned int an_mode,const struct phylink_link_state * state)24552323ef7SOleksandr Mazur static void prestera_mac_config(struct phylink_config *config,
24652323ef7SOleksandr Mazur 				unsigned int an_mode,
24752323ef7SOleksandr Mazur 				const struct phylink_link_state *state)
24852323ef7SOleksandr Mazur {
24952323ef7SOleksandr Mazur }
25052323ef7SOleksandr Mazur 
prestera_mac_link_down(struct phylink_config * config,unsigned int mode,phy_interface_t interface)25152323ef7SOleksandr Mazur static void prestera_mac_link_down(struct phylink_config *config,
25252323ef7SOleksandr Mazur 				   unsigned int mode, phy_interface_t interface)
25352323ef7SOleksandr Mazur {
25452323ef7SOleksandr Mazur 	struct net_device *ndev = to_net_dev(config->dev);
25552323ef7SOleksandr Mazur 	struct prestera_port *port = netdev_priv(ndev);
25652323ef7SOleksandr Mazur 	struct prestera_port_mac_state state_mac;
25752323ef7SOleksandr Mazur 
25852323ef7SOleksandr Mazur 	/* Invalidate. Parameters will update on next link event. */
25952323ef7SOleksandr Mazur 	memset(&state_mac, 0, sizeof(state_mac));
26052323ef7SOleksandr Mazur 	state_mac.valid = false;
26152323ef7SOleksandr Mazur 	prestera_port_mac_state_cache_write(port, &state_mac);
26252323ef7SOleksandr Mazur }
26352323ef7SOleksandr Mazur 
prestera_mac_link_up(struct phylink_config * config,struct phy_device * phy,unsigned int mode,phy_interface_t interface,int speed,int duplex,bool tx_pause,bool rx_pause)26452323ef7SOleksandr Mazur static void prestera_mac_link_up(struct phylink_config *config,
26552323ef7SOleksandr Mazur 				 struct phy_device *phy,
26652323ef7SOleksandr Mazur 				 unsigned int mode, phy_interface_t interface,
26752323ef7SOleksandr Mazur 				 int speed, int duplex,
26852323ef7SOleksandr Mazur 				 bool tx_pause, bool rx_pause)
26952323ef7SOleksandr Mazur {
27052323ef7SOleksandr Mazur }
27152323ef7SOleksandr Mazur 
27252323ef7SOleksandr Mazur static struct phylink_pcs *
prestera_mac_select_pcs(struct phylink_config * config,phy_interface_t interface)27352323ef7SOleksandr Mazur prestera_mac_select_pcs(struct phylink_config *config,
27452323ef7SOleksandr Mazur 			phy_interface_t interface)
27552323ef7SOleksandr Mazur {
27652323ef7SOleksandr Mazur 	struct net_device *dev = to_net_dev(config->dev);
27752323ef7SOleksandr Mazur 	struct prestera_port *port = netdev_priv(dev);
27852323ef7SOleksandr Mazur 
27952323ef7SOleksandr Mazur 	return &port->phylink_pcs;
28052323ef7SOleksandr Mazur }
28152323ef7SOleksandr Mazur 
prestera_pcs_get_state(struct phylink_pcs * pcs,struct phylink_link_state * state)28252323ef7SOleksandr Mazur static void prestera_pcs_get_state(struct phylink_pcs *pcs,
28352323ef7SOleksandr Mazur 				   struct phylink_link_state *state)
28452323ef7SOleksandr Mazur {
28552323ef7SOleksandr Mazur 	struct prestera_port *port = container_of(pcs, struct prestera_port,
28652323ef7SOleksandr Mazur 						  phylink_pcs);
28752323ef7SOleksandr Mazur 	struct prestera_port_mac_state smac;
28852323ef7SOleksandr Mazur 
28952323ef7SOleksandr Mazur 	prestera_port_mac_state_cache_read(port, &smac);
29052323ef7SOleksandr Mazur 
29152323ef7SOleksandr Mazur 	if (smac.valid) {
29252323ef7SOleksandr Mazur 		state->link = smac.oper ? 1 : 0;
29352323ef7SOleksandr Mazur 		/* AN is completed, when port is up */
29452323ef7SOleksandr Mazur 		state->an_complete = (smac.oper && port->autoneg) ? 1 : 0;
29552323ef7SOleksandr Mazur 		state->speed = smac.speed;
29652323ef7SOleksandr Mazur 		state->duplex = smac.duplex;
29752323ef7SOleksandr Mazur 	} else {
29852323ef7SOleksandr Mazur 		state->link = 0;
29952323ef7SOleksandr Mazur 		state->an_complete = 0;
30052323ef7SOleksandr Mazur 	}
30152323ef7SOleksandr Mazur }
30252323ef7SOleksandr Mazur 
prestera_pcs_config(struct phylink_pcs * pcs,unsigned int neg_mode,phy_interface_t interface,const unsigned long * advertising,bool permit_pause_to_mac)303*d5a05299SRussell King (Oracle) static int prestera_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
30452323ef7SOleksandr Mazur 			       phy_interface_t interface,
30552323ef7SOleksandr Mazur 			       const unsigned long *advertising,
30652323ef7SOleksandr Mazur 			       bool permit_pause_to_mac)
30752323ef7SOleksandr Mazur {
308969e26c6SSebin Sebastian 	struct prestera_port *port = prestera_pcs_to_port(pcs);
30952323ef7SOleksandr Mazur 	struct prestera_port_mac_config cfg_mac;
31052323ef7SOleksandr Mazur 	int err;
31152323ef7SOleksandr Mazur 
31252323ef7SOleksandr Mazur 	err = prestera_port_cfg_mac_read(port, &cfg_mac);
31352323ef7SOleksandr Mazur 	if (err)
31452323ef7SOleksandr Mazur 		return err;
31552323ef7SOleksandr Mazur 
31652323ef7SOleksandr Mazur 	cfg_mac.admin = true;
31752323ef7SOleksandr Mazur 	cfg_mac.fec = PRESTERA_PORT_FEC_OFF;
318*d5a05299SRussell King (Oracle) 	cfg_mac.inband = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
31952323ef7SOleksandr Mazur 
32052323ef7SOleksandr Mazur 	switch (interface) {
32152323ef7SOleksandr Mazur 	case PHY_INTERFACE_MODE_10GBASER:
32252323ef7SOleksandr Mazur 		cfg_mac.speed = SPEED_10000;
32352323ef7SOleksandr Mazur 		cfg_mac.mode = PRESTERA_MAC_MODE_SR_LR;
32452323ef7SOleksandr Mazur 		break;
32552323ef7SOleksandr Mazur 	case PHY_INTERFACE_MODE_2500BASEX:
32652323ef7SOleksandr Mazur 		cfg_mac.speed = SPEED_2500;
32752323ef7SOleksandr Mazur 		cfg_mac.duplex = DUPLEX_FULL;
32852323ef7SOleksandr Mazur 		cfg_mac.mode = PRESTERA_MAC_MODE_SGMII;
32952323ef7SOleksandr Mazur 		break;
33052323ef7SOleksandr Mazur 	case PHY_INTERFACE_MODE_SGMII:
33152323ef7SOleksandr Mazur 		cfg_mac.mode = PRESTERA_MAC_MODE_SGMII;
33252323ef7SOleksandr Mazur 		break;
33352323ef7SOleksandr Mazur 	case PHY_INTERFACE_MODE_1000BASEX:
33452323ef7SOleksandr Mazur 	default:
33552323ef7SOleksandr Mazur 		cfg_mac.speed = SPEED_1000;
33652323ef7SOleksandr Mazur 		cfg_mac.duplex = DUPLEX_FULL;
33752323ef7SOleksandr Mazur 		cfg_mac.mode = PRESTERA_MAC_MODE_1000BASE_X;
33852323ef7SOleksandr Mazur 		break;
33952323ef7SOleksandr Mazur 	}
34052323ef7SOleksandr Mazur 
34152323ef7SOleksandr Mazur 	err = prestera_port_cfg_mac_write(port, &cfg_mac);
34252323ef7SOleksandr Mazur 	if (err)
34352323ef7SOleksandr Mazur 		return err;
34452323ef7SOleksandr Mazur 
34552323ef7SOleksandr Mazur 	return 0;
34652323ef7SOleksandr Mazur }
34752323ef7SOleksandr Mazur 
prestera_pcs_an_restart(struct phylink_pcs * pcs)34852323ef7SOleksandr Mazur static void prestera_pcs_an_restart(struct phylink_pcs *pcs)
34952323ef7SOleksandr Mazur {
35052323ef7SOleksandr Mazur 	/* TODO: add 1000basex AN restart support
35152323ef7SOleksandr Mazur 	 * (Currently FW has no support for 1000baseX AN restart, but it will in the future,
35252323ef7SOleksandr Mazur 	 * so as for now the function would stay empty.)
35352323ef7SOleksandr Mazur 	 */
35452323ef7SOleksandr Mazur }
35552323ef7SOleksandr Mazur 
35652323ef7SOleksandr Mazur static const struct phylink_mac_ops prestera_mac_ops = {
35752323ef7SOleksandr Mazur 	.mac_select_pcs = prestera_mac_select_pcs,
35852323ef7SOleksandr Mazur 	.mac_config = prestera_mac_config,
35952323ef7SOleksandr Mazur 	.mac_link_down = prestera_mac_link_down,
36052323ef7SOleksandr Mazur 	.mac_link_up = prestera_mac_link_up,
36152323ef7SOleksandr Mazur };
36252323ef7SOleksandr Mazur 
36352323ef7SOleksandr Mazur static const struct phylink_pcs_ops prestera_pcs_ops = {
36452323ef7SOleksandr Mazur 	.pcs_get_state = prestera_pcs_get_state,
36552323ef7SOleksandr Mazur 	.pcs_config = prestera_pcs_config,
36652323ef7SOleksandr Mazur 	.pcs_an_restart = prestera_pcs_an_restart,
36752323ef7SOleksandr Mazur };
36852323ef7SOleksandr Mazur 
prestera_port_sfp_bind(struct prestera_port * port)36952323ef7SOleksandr Mazur static int prestera_port_sfp_bind(struct prestera_port *port)
37052323ef7SOleksandr Mazur {
37152323ef7SOleksandr Mazur 	struct prestera_switch *sw = port->sw;
37252323ef7SOleksandr Mazur 	struct device_node *ports, *node;
37352323ef7SOleksandr Mazur 	struct fwnode_handle *fwnode;
37452323ef7SOleksandr Mazur 	struct phylink *phy_link;
37552323ef7SOleksandr Mazur 	int err;
37652323ef7SOleksandr Mazur 
37752323ef7SOleksandr Mazur 	if (!sw->np)
37852323ef7SOleksandr Mazur 		return 0;
37952323ef7SOleksandr Mazur 
3803aac7adaSLiang He 	of_node_get(sw->np);
38152323ef7SOleksandr Mazur 	ports = of_find_node_by_name(sw->np, "ports");
38252323ef7SOleksandr Mazur 
38352323ef7SOleksandr Mazur 	for_each_child_of_node(ports, node) {
38452323ef7SOleksandr Mazur 		int num;
38552323ef7SOleksandr Mazur 
38652323ef7SOleksandr Mazur 		err = of_property_read_u32(node, "prestera,port-num", &num);
38752323ef7SOleksandr Mazur 		if (err) {
38852323ef7SOleksandr Mazur 			dev_err(sw->dev->dev,
38952323ef7SOleksandr Mazur 				"device node %pOF has no valid reg property: %d\n",
39052323ef7SOleksandr Mazur 				node, err);
39152323ef7SOleksandr Mazur 			goto out;
39252323ef7SOleksandr Mazur 		}
39352323ef7SOleksandr Mazur 
39452323ef7SOleksandr Mazur 		if (port->fp_id != num)
39552323ef7SOleksandr Mazur 			continue;
39652323ef7SOleksandr Mazur 
39752323ef7SOleksandr Mazur 		port->phylink_pcs.ops = &prestera_pcs_ops;
398*d5a05299SRussell King (Oracle) 		port->phylink_pcs.neg_mode = true;
39952323ef7SOleksandr Mazur 
40052323ef7SOleksandr Mazur 		port->phy_config.dev = &port->dev->dev;
40152323ef7SOleksandr Mazur 		port->phy_config.type = PHYLINK_NETDEV;
40252323ef7SOleksandr Mazur 
40352323ef7SOleksandr Mazur 		fwnode = of_fwnode_handle(node);
40452323ef7SOleksandr Mazur 
40552323ef7SOleksandr Mazur 		__set_bit(PHY_INTERFACE_MODE_10GBASER,
40652323ef7SOleksandr Mazur 			  port->phy_config.supported_interfaces);
40752323ef7SOleksandr Mazur 		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
40852323ef7SOleksandr Mazur 			  port->phy_config.supported_interfaces);
40952323ef7SOleksandr Mazur 		__set_bit(PHY_INTERFACE_MODE_SGMII,
41052323ef7SOleksandr Mazur 			  port->phy_config.supported_interfaces);
41152323ef7SOleksandr Mazur 		__set_bit(PHY_INTERFACE_MODE_1000BASEX,
41252323ef7SOleksandr Mazur 			  port->phy_config.supported_interfaces);
41352323ef7SOleksandr Mazur 
41452323ef7SOleksandr Mazur 		port->phy_config.mac_capabilities =
41552323ef7SOleksandr Mazur 			MAC_1000 | MAC_2500FD | MAC_10000FD;
41652323ef7SOleksandr Mazur 
41752323ef7SOleksandr Mazur 		phy_link = phylink_create(&port->phy_config, fwnode,
41852323ef7SOleksandr Mazur 					  PHY_INTERFACE_MODE_INTERNAL,
41952323ef7SOleksandr Mazur 					  &prestera_mac_ops);
42052323ef7SOleksandr Mazur 		if (IS_ERR(phy_link)) {
42152323ef7SOleksandr Mazur 			netdev_err(port->dev, "failed to create phylink\n");
42252323ef7SOleksandr Mazur 			err = PTR_ERR(phy_link);
42352323ef7SOleksandr Mazur 			goto out;
42452323ef7SOleksandr Mazur 		}
42552323ef7SOleksandr Mazur 
42652323ef7SOleksandr Mazur 		port->phy_link = phy_link;
42752323ef7SOleksandr Mazur 		break;
42852323ef7SOleksandr Mazur 	}
42952323ef7SOleksandr Mazur 
43052323ef7SOleksandr Mazur out:
4313aac7adaSLiang He 	of_node_put(node);
43252323ef7SOleksandr Mazur 	of_node_put(ports);
43352323ef7SOleksandr Mazur 	return err;
43452323ef7SOleksandr Mazur }
43552323ef7SOleksandr Mazur 
prestera_port_sfp_unbind(struct prestera_port * port)43652323ef7SOleksandr Mazur static int prestera_port_sfp_unbind(struct prestera_port *port)
43752323ef7SOleksandr Mazur {
43852323ef7SOleksandr Mazur 	if (port->phy_link)
43952323ef7SOleksandr Mazur 		phylink_destroy(port->phy_link);
44052323ef7SOleksandr Mazur 
44152323ef7SOleksandr Mazur 	return 0;
44252323ef7SOleksandr Mazur }
44352323ef7SOleksandr Mazur 
prestera_port_xmit(struct sk_buff * skb,struct net_device * dev)444501ef306SVadym Kochan static netdev_tx_t prestera_port_xmit(struct sk_buff *skb,
445501ef306SVadym Kochan 				      struct net_device *dev)
446501ef306SVadym Kochan {
447501ef306SVadym Kochan 	return prestera_rxtx_xmit(netdev_priv(dev), skb);
448501ef306SVadym Kochan }
449501ef306SVadym Kochan 
prestera_is_valid_mac_addr(struct prestera_port * port,const u8 * addr)450da3c1639SYevhen Orlov int prestera_is_valid_mac_addr(struct prestera_port *port, const u8 *addr)
451501ef306SVadym Kochan {
452501ef306SVadym Kochan 	if (!is_valid_ether_addr(addr))
453501ef306SVadym Kochan 		return -EADDRNOTAVAIL;
454501ef306SVadym Kochan 
455501ef306SVadym Kochan 	/* firmware requires that port's MAC address contains first 5 bytes
456501ef306SVadym Kochan 	 * of the base MAC address
457501ef306SVadym Kochan 	 */
458501ef306SVadym Kochan 	if (memcmp(port->sw->base_mac, addr, ETH_ALEN - 1))
459501ef306SVadym Kochan 		return -EINVAL;
460501ef306SVadym Kochan 
461501ef306SVadym Kochan 	return 0;
462501ef306SVadym Kochan }
463501ef306SVadym Kochan 
prestera_port_set_mac_address(struct net_device * dev,void * p)464501ef306SVadym Kochan static int prestera_port_set_mac_address(struct net_device *dev, void *p)
465501ef306SVadym Kochan {
466501ef306SVadym Kochan 	struct prestera_port *port = netdev_priv(dev);
467501ef306SVadym Kochan 	struct sockaddr *addr = p;
468501ef306SVadym Kochan 	int err;
469501ef306SVadym Kochan 
470501ef306SVadym Kochan 	err = prestera_is_valid_mac_addr(port, addr->sa_data);
471501ef306SVadym Kochan 	if (err)
472501ef306SVadym Kochan 		return err;
473501ef306SVadym Kochan 
474501ef306SVadym Kochan 	err = prestera_hw_port_mac_set(port, addr->sa_data);
475501ef306SVadym Kochan 	if (err)
476501ef306SVadym Kochan 		return err;
477501ef306SVadym Kochan 
478f3956ebbSJakub Kicinski 	eth_hw_addr_set(dev, addr->sa_data);
479501ef306SVadym Kochan 
480501ef306SVadym Kochan 	return 0;
481501ef306SVadym Kochan }
482501ef306SVadym Kochan 
prestera_port_change_mtu(struct net_device * dev,int mtu)483501ef306SVadym Kochan static int prestera_port_change_mtu(struct net_device *dev, int mtu)
484501ef306SVadym Kochan {
485501ef306SVadym Kochan 	struct prestera_port *port = netdev_priv(dev);
486501ef306SVadym Kochan 	int err;
487501ef306SVadym Kochan 
488501ef306SVadym Kochan 	err = prestera_hw_port_mtu_set(port, mtu);
489501ef306SVadym Kochan 	if (err)
490501ef306SVadym Kochan 		return err;
491501ef306SVadym Kochan 
492501ef306SVadym Kochan 	dev->mtu = mtu;
493501ef306SVadym Kochan 
494501ef306SVadym Kochan 	return 0;
495501ef306SVadym Kochan }
496501ef306SVadym Kochan 
prestera_port_get_stats64(struct net_device * dev,struct rtnl_link_stats64 * stats)497501ef306SVadym Kochan static void prestera_port_get_stats64(struct net_device *dev,
498501ef306SVadym Kochan 				      struct rtnl_link_stats64 *stats)
499501ef306SVadym Kochan {
500501ef306SVadym Kochan 	struct prestera_port *port = netdev_priv(dev);
501501ef306SVadym Kochan 	struct prestera_port_stats *port_stats = &port->cached_hw_stats.stats;
502501ef306SVadym Kochan 
503501ef306SVadym Kochan 	stats->rx_packets = port_stats->broadcast_frames_received +
504501ef306SVadym Kochan 				port_stats->multicast_frames_received +
505501ef306SVadym Kochan 				port_stats->unicast_frames_received;
506501ef306SVadym Kochan 
507501ef306SVadym Kochan 	stats->tx_packets = port_stats->broadcast_frames_sent +
508501ef306SVadym Kochan 				port_stats->multicast_frames_sent +
509501ef306SVadym Kochan 				port_stats->unicast_frames_sent;
510501ef306SVadym Kochan 
511501ef306SVadym Kochan 	stats->rx_bytes = port_stats->good_octets_received;
512501ef306SVadym Kochan 
513501ef306SVadym Kochan 	stats->tx_bytes = port_stats->good_octets_sent;
514501ef306SVadym Kochan 
515501ef306SVadym Kochan 	stats->rx_errors = port_stats->rx_error_frame_received;
516501ef306SVadym Kochan 	stats->tx_errors = port_stats->mac_trans_error;
517501ef306SVadym Kochan 
518501ef306SVadym Kochan 	stats->rx_dropped = port_stats->buffer_overrun;
519501ef306SVadym Kochan 	stats->tx_dropped = 0;
520501ef306SVadym Kochan 
521501ef306SVadym Kochan 	stats->multicast = port_stats->multicast_frames_received;
522501ef306SVadym Kochan 	stats->collisions = port_stats->excessive_collision;
523501ef306SVadym Kochan 
524501ef306SVadym Kochan 	stats->rx_crc_errors = port_stats->bad_crc;
525501ef306SVadym Kochan }
526501ef306SVadym Kochan 
prestera_port_get_hw_stats(struct prestera_port * port)527501ef306SVadym Kochan static void prestera_port_get_hw_stats(struct prestera_port *port)
528501ef306SVadym Kochan {
529501ef306SVadym Kochan 	prestera_hw_port_stats_get(port, &port->cached_hw_stats.stats);
530501ef306SVadym Kochan }
531501ef306SVadym Kochan 
prestera_port_stats_update(struct work_struct * work)532501ef306SVadym Kochan static void prestera_port_stats_update(struct work_struct *work)
533501ef306SVadym Kochan {
534501ef306SVadym Kochan 	struct prestera_port *port =
535501ef306SVadym Kochan 		container_of(work, struct prestera_port,
536501ef306SVadym Kochan 			     cached_hw_stats.caching_dw.work);
537501ef306SVadym Kochan 
538501ef306SVadym Kochan 	prestera_port_get_hw_stats(port);
539501ef306SVadym Kochan 
540501ef306SVadym Kochan 	queue_delayed_work(prestera_wq, &port->cached_hw_stats.caching_dw,
541501ef306SVadym Kochan 			   msecs_to_jiffies(PRESTERA_STATS_DELAY_MS));
542501ef306SVadym Kochan }
543501ef306SVadym Kochan 
prestera_port_setup_tc(struct net_device * dev,enum tc_setup_type type,void * type_data)5448b474a9fSSerhiy Boiko static int prestera_port_setup_tc(struct net_device *dev,
5458b474a9fSSerhiy Boiko 				  enum tc_setup_type type,
5468b474a9fSSerhiy Boiko 				  void *type_data)
5478b474a9fSSerhiy Boiko {
5488b474a9fSSerhiy Boiko 	struct prestera_port *port = netdev_priv(dev);
5498b474a9fSSerhiy Boiko 
5508b474a9fSSerhiy Boiko 	switch (type) {
5518b474a9fSSerhiy Boiko 	case TC_SETUP_BLOCK:
5528b474a9fSSerhiy Boiko 		return prestera_flow_block_setup(port, type_data);
5538b474a9fSSerhiy Boiko 	default:
5548b474a9fSSerhiy Boiko 		return -EOPNOTSUPP;
5558b474a9fSSerhiy Boiko 	}
5568b474a9fSSerhiy Boiko }
5578b474a9fSSerhiy Boiko 
558501ef306SVadym Kochan static const struct net_device_ops prestera_netdev_ops = {
559501ef306SVadym Kochan 	.ndo_open = prestera_port_open,
560501ef306SVadym Kochan 	.ndo_stop = prestera_port_close,
561501ef306SVadym Kochan 	.ndo_start_xmit = prestera_port_xmit,
5628b474a9fSSerhiy Boiko 	.ndo_setup_tc = prestera_port_setup_tc,
563501ef306SVadym Kochan 	.ndo_change_mtu = prestera_port_change_mtu,
564501ef306SVadym Kochan 	.ndo_get_stats64 = prestera_port_get_stats64,
565501ef306SVadym Kochan 	.ndo_set_mac_address = prestera_port_set_mac_address,
566501ef306SVadym Kochan };
567501ef306SVadym Kochan 
prestera_port_autoneg_set(struct prestera_port * port,u64 link_modes)568bb5dbf2cSVolodymyr Mytnyk int prestera_port_autoneg_set(struct prestera_port *port, u64 link_modes)
569501ef306SVadym Kochan {
570501ef306SVadym Kochan 	int err;
571501ef306SVadym Kochan 
572bb5dbf2cSVolodymyr Mytnyk 	if (port->autoneg && port->adver_link_modes == link_modes)
573501ef306SVadym Kochan 		return 0;
574501ef306SVadym Kochan 
575bb5dbf2cSVolodymyr Mytnyk 	err = prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin,
576bb5dbf2cSVolodymyr Mytnyk 					    true, 0, link_modes,
577bb5dbf2cSVolodymyr Mytnyk 					    port->cfg_phy.mdix);
578501ef306SVadym Kochan 	if (err)
579501ef306SVadym Kochan 		return err;
580501ef306SVadym Kochan 
581bb5dbf2cSVolodymyr Mytnyk 	port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF);
582bb5dbf2cSVolodymyr Mytnyk 	port->adver_link_modes = link_modes;
583bb5dbf2cSVolodymyr Mytnyk 	port->cfg_phy.mode = 0;
584bb5dbf2cSVolodymyr Mytnyk 	port->autoneg = true;
585501ef306SVadym Kochan 
586501ef306SVadym Kochan 	return 0;
587501ef306SVadym Kochan }
588501ef306SVadym Kochan 
prestera_port_list_add(struct prestera_port * port)589501ef306SVadym Kochan static void prestera_port_list_add(struct prestera_port *port)
590501ef306SVadym Kochan {
591501ef306SVadym Kochan 	write_lock(&port->sw->port_list_lock);
592501ef306SVadym Kochan 	list_add(&port->list, &port->sw->port_list);
593501ef306SVadym Kochan 	write_unlock(&port->sw->port_list_lock);
594501ef306SVadym Kochan }
595501ef306SVadym Kochan 
prestera_port_list_del(struct prestera_port * port)596501ef306SVadym Kochan static void prestera_port_list_del(struct prestera_port *port)
597501ef306SVadym Kochan {
598501ef306SVadym Kochan 	write_lock(&port->sw->port_list_lock);
599501ef306SVadym Kochan 	list_del(&port->list);
600501ef306SVadym Kochan 	write_unlock(&port->sw->port_list_lock);
601501ef306SVadym Kochan }
602501ef306SVadym Kochan 
prestera_port_create(struct prestera_switch * sw,u32 id)603501ef306SVadym Kochan static int prestera_port_create(struct prestera_switch *sw, u32 id)
604501ef306SVadym Kochan {
605bb5dbf2cSVolodymyr Mytnyk 	struct prestera_port_mac_config cfg_mac;
606501ef306SVadym Kochan 	struct prestera_port *port;
607501ef306SVadym Kochan 	struct net_device *dev;
608501ef306SVadym Kochan 	int err;
609501ef306SVadym Kochan 
610501ef306SVadym Kochan 	dev = alloc_etherdev(sizeof(*port));
611501ef306SVadym Kochan 	if (!dev)
612501ef306SVadym Kochan 		return -ENOMEM;
613501ef306SVadym Kochan 
614501ef306SVadym Kochan 	port = netdev_priv(dev);
615501ef306SVadym Kochan 
616e1189d9aSVadym Kochan 	INIT_LIST_HEAD(&port->vlans_list);
617e1189d9aSVadym Kochan 	port->pvid = PRESTERA_DEFAULT_VID;
618255213caSSerhiy Boiko 	port->lag = NULL;
619501ef306SVadym Kochan 	port->dev = dev;
620501ef306SVadym Kochan 	port->id = id;
621501ef306SVadym Kochan 	port->sw = sw;
622501ef306SVadym Kochan 
62352323ef7SOleksandr Mazur 	spin_lock_init(&port->state_mac_lock);
62452323ef7SOleksandr Mazur 
625501ef306SVadym Kochan 	err = prestera_hw_port_info_get(port, &port->dev_id, &port->hw_id,
626501ef306SVadym Kochan 					&port->fp_id);
627501ef306SVadym Kochan 	if (err) {
628501ef306SVadym Kochan 		dev_err(prestera_dev(sw), "Failed to get port(%u) info\n", id);
62934dd1710SVadym Kochan 		goto err_port_info_get;
630501ef306SVadym Kochan 	}
631501ef306SVadym Kochan 
63234dd1710SVadym Kochan 	err = prestera_devlink_port_register(port);
63334dd1710SVadym Kochan 	if (err)
63434dd1710SVadym Kochan 		goto err_dl_port_register;
63534dd1710SVadym Kochan 
6368b474a9fSSerhiy Boiko 	dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_HW_TC;
637501ef306SVadym Kochan 	dev->netdev_ops = &prestera_netdev_ops;
638a97d3c69SVadym Kochan 	dev->ethtool_ops = &prestera_ethtool_ops;
63952323ef7SOleksandr Mazur 	SET_NETDEV_DEV(dev, sw->dev->dev);
640ac73d4bfSJiri Pirko 	SET_NETDEV_DEVLINK_PORT(dev, &port->dl_port);
641501ef306SVadym Kochan 
64252323ef7SOleksandr Mazur 	if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP)
643501ef306SVadym Kochan 		netif_carrier_off(dev);
644501ef306SVadym Kochan 
645501ef306SVadym Kochan 	dev->mtu = min_t(unsigned int, sw->mtu_max, PRESTERA_MTU_DEFAULT);
646501ef306SVadym Kochan 	dev->min_mtu = sw->mtu_min;
647501ef306SVadym Kochan 	dev->max_mtu = sw->mtu_max;
648501ef306SVadym Kochan 
649501ef306SVadym Kochan 	err = prestera_hw_port_mtu_set(port, dev->mtu);
650501ef306SVadym Kochan 	if (err) {
651501ef306SVadym Kochan 		dev_err(prestera_dev(sw), "Failed to set port(%u) mtu(%d)\n",
652501ef306SVadym Kochan 			id, dev->mtu);
653501ef306SVadym Kochan 		goto err_port_init;
654501ef306SVadym Kochan 	}
655501ef306SVadym Kochan 
6564de377b6SZhang Changzhong 	if (port->fp_id >= PRESTERA_MAC_ADDR_NUM_MAX) {
6574de377b6SZhang Changzhong 		err = -EINVAL;
658501ef306SVadym Kochan 		goto err_port_init;
6594de377b6SZhang Changzhong 	}
660501ef306SVadym Kochan 
6618eb8192eSJakub Kicinski 	eth_hw_addr_gen(dev, sw->base_mac, port->fp_id);
662501ef306SVadym Kochan 	/* firmware requires that port's MAC address consist of the first
663501ef306SVadym Kochan 	 * 5 bytes of the base MAC address
664501ef306SVadym Kochan 	 */
6658eb8192eSJakub Kicinski 	if (memcmp(dev->dev_addr, sw->base_mac, ETH_ALEN - 1)) {
6668eb8192eSJakub Kicinski 		dev_warn(prestera_dev(sw), "Port MAC address wraps for port(%u)\n", id);
6678eb8192eSJakub Kicinski 		dev_addr_mod(dev, 0, sw->base_mac, ETH_ALEN - 1);
6688eb8192eSJakub Kicinski 	}
669501ef306SVadym Kochan 
670501ef306SVadym Kochan 	err = prestera_hw_port_mac_set(port, dev->dev_addr);
671501ef306SVadym Kochan 	if (err) {
672501ef306SVadym Kochan 		dev_err(prestera_dev(sw), "Failed to set port(%u) mac addr\n", id);
673501ef306SVadym Kochan 		goto err_port_init;
674501ef306SVadym Kochan 	}
675501ef306SVadym Kochan 
676501ef306SVadym Kochan 	err = prestera_hw_port_cap_get(port, &port->caps);
677501ef306SVadym Kochan 	if (err) {
678501ef306SVadym Kochan 		dev_err(prestera_dev(sw), "Failed to get port(%u) caps\n", id);
679501ef306SVadym Kochan 		goto err_port_init;
680501ef306SVadym Kochan 	}
681501ef306SVadym Kochan 
682bb5dbf2cSVolodymyr Mytnyk 	port->adver_link_modes = port->caps.supp_link_modes;
683bb5dbf2cSVolodymyr Mytnyk 	port->adver_fec = 0;
684bb5dbf2cSVolodymyr Mytnyk 	port->autoneg = true;
685501ef306SVadym Kochan 
686bb5dbf2cSVolodymyr Mytnyk 	/* initialize config mac */
687bb5dbf2cSVolodymyr Mytnyk 	if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP) {
688bb5dbf2cSVolodymyr Mytnyk 		cfg_mac.admin = true;
689bb5dbf2cSVolodymyr Mytnyk 		cfg_mac.mode = PRESTERA_MAC_MODE_INTERNAL;
690bb5dbf2cSVolodymyr Mytnyk 	} else {
691bb5dbf2cSVolodymyr Mytnyk 		cfg_mac.admin = false;
692bb5dbf2cSVolodymyr Mytnyk 		cfg_mac.mode = PRESTERA_MAC_MODE_MAX;
693bb5dbf2cSVolodymyr Mytnyk 	}
69452323ef7SOleksandr Mazur 	cfg_mac.inband = 0;
695bb5dbf2cSVolodymyr Mytnyk 	cfg_mac.speed = 0;
696bb5dbf2cSVolodymyr Mytnyk 	cfg_mac.duplex = DUPLEX_UNKNOWN;
697bb5dbf2cSVolodymyr Mytnyk 	cfg_mac.fec = PRESTERA_PORT_FEC_OFF;
698bb5dbf2cSVolodymyr Mytnyk 
699bb5dbf2cSVolodymyr Mytnyk 	err = prestera_port_cfg_mac_write(port, &cfg_mac);
700501ef306SVadym Kochan 	if (err) {
701a46a5036SVolodymyr Mytnyk 		dev_err(prestera_dev(sw),
702a46a5036SVolodymyr Mytnyk 			"Failed to set port(%u) mac mode\n", id);
703501ef306SVadym Kochan 		goto err_port_init;
704501ef306SVadym Kochan 	}
705501ef306SVadym Kochan 
706bb5dbf2cSVolodymyr Mytnyk 	/* initialize config phy (if this is inegral) */
707bb5dbf2cSVolodymyr Mytnyk 	if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP) {
708bb5dbf2cSVolodymyr Mytnyk 		port->cfg_phy.mdix = ETH_TP_MDI_AUTO;
709bb5dbf2cSVolodymyr Mytnyk 		port->cfg_phy.admin = false;
710bb5dbf2cSVolodymyr Mytnyk 		err = prestera_hw_port_phy_mode_set(port,
711bb5dbf2cSVolodymyr Mytnyk 						    port->cfg_phy.admin,
712bb5dbf2cSVolodymyr Mytnyk 						    false, 0, 0,
713bb5dbf2cSVolodymyr Mytnyk 						    port->cfg_phy.mdix);
714bb5dbf2cSVolodymyr Mytnyk 		if (err) {
715a46a5036SVolodymyr Mytnyk 			dev_err(prestera_dev(sw),
716a46a5036SVolodymyr Mytnyk 				"Failed to set port(%u) phy mode\n", id);
717bb5dbf2cSVolodymyr Mytnyk 			goto err_port_init;
718bb5dbf2cSVolodymyr Mytnyk 		}
719bb5dbf2cSVolodymyr Mytnyk 	}
720bb5dbf2cSVolodymyr Mytnyk 
721501ef306SVadym Kochan 	err = prestera_rxtx_port_init(port);
722501ef306SVadym Kochan 	if (err)
723501ef306SVadym Kochan 		goto err_port_init;
724501ef306SVadym Kochan 
725501ef306SVadym Kochan 	INIT_DELAYED_WORK(&port->cached_hw_stats.caching_dw,
726501ef306SVadym Kochan 			  &prestera_port_stats_update);
727501ef306SVadym Kochan 
728501ef306SVadym Kochan 	prestera_port_list_add(port);
729501ef306SVadym Kochan 
730501ef306SVadym Kochan 	err = register_netdev(dev);
731501ef306SVadym Kochan 	if (err)
732501ef306SVadym Kochan 		goto err_register_netdev;
733501ef306SVadym Kochan 
73452323ef7SOleksandr Mazur 	err = prestera_port_sfp_bind(port);
73552323ef7SOleksandr Mazur 	if (err)
73652323ef7SOleksandr Mazur 		goto err_sfp_bind;
73752323ef7SOleksandr Mazur 
738501ef306SVadym Kochan 	return 0;
739501ef306SVadym Kochan 
74052323ef7SOleksandr Mazur err_sfp_bind:
7419a234a2aSZhang Changzhong 	unregister_netdev(dev);
742501ef306SVadym Kochan err_register_netdev:
743501ef306SVadym Kochan 	prestera_port_list_del(port);
744501ef306SVadym Kochan err_port_init:
74534dd1710SVadym Kochan 	prestera_devlink_port_unregister(port);
74634dd1710SVadym Kochan err_dl_port_register:
74734dd1710SVadym Kochan err_port_info_get:
748501ef306SVadym Kochan 	free_netdev(dev);
749501ef306SVadym Kochan 	return err;
750501ef306SVadym Kochan }
751501ef306SVadym Kochan 
prestera_port_destroy(struct prestera_port * port)752501ef306SVadym Kochan static void prestera_port_destroy(struct prestera_port *port)
753501ef306SVadym Kochan {
754501ef306SVadym Kochan 	struct net_device *dev = port->dev;
755501ef306SVadym Kochan 
756501ef306SVadym Kochan 	cancel_delayed_work_sync(&port->cached_hw_stats.caching_dw);
757501ef306SVadym Kochan 	unregister_netdev(dev);
758501ef306SVadym Kochan 	prestera_port_list_del(port);
75934dd1710SVadym Kochan 	prestera_devlink_port_unregister(port);
760501ef306SVadym Kochan 	free_netdev(dev);
761501ef306SVadym Kochan }
762501ef306SVadym Kochan 
prestera_destroy_ports(struct prestera_switch * sw)763501ef306SVadym Kochan static void prestera_destroy_ports(struct prestera_switch *sw)
764501ef306SVadym Kochan {
765501ef306SVadym Kochan 	struct prestera_port *port, *tmp;
766501ef306SVadym Kochan 
767501ef306SVadym Kochan 	list_for_each_entry_safe(port, tmp, &sw->port_list, list)
768501ef306SVadym Kochan 		prestera_port_destroy(port);
769501ef306SVadym Kochan }
770501ef306SVadym Kochan 
prestera_create_ports(struct prestera_switch * sw)771501ef306SVadym Kochan static int prestera_create_ports(struct prestera_switch *sw)
772501ef306SVadym Kochan {
773501ef306SVadym Kochan 	struct prestera_port *port, *tmp;
774501ef306SVadym Kochan 	u32 port_idx;
775501ef306SVadym Kochan 	int err;
776501ef306SVadym Kochan 
777501ef306SVadym Kochan 	for (port_idx = 0; port_idx < sw->port_count; port_idx++) {
778501ef306SVadym Kochan 		err = prestera_port_create(sw, port_idx);
779501ef306SVadym Kochan 		if (err)
780501ef306SVadym Kochan 			goto err_port_create;
781501ef306SVadym Kochan 	}
782501ef306SVadym Kochan 
783501ef306SVadym Kochan 	return 0;
784501ef306SVadym Kochan 
785501ef306SVadym Kochan err_port_create:
78652323ef7SOleksandr Mazur 	list_for_each_entry_safe(port, tmp, &sw->port_list, list) {
78752323ef7SOleksandr Mazur 		prestera_port_sfp_unbind(port);
788501ef306SVadym Kochan 		prestera_port_destroy(port);
78952323ef7SOleksandr Mazur 	}
790501ef306SVadym Kochan 
791501ef306SVadym Kochan 	return err;
792501ef306SVadym Kochan }
793501ef306SVadym Kochan 
prestera_port_handle_event(struct prestera_switch * sw,struct prestera_event * evt,void * arg)794501ef306SVadym Kochan static void prestera_port_handle_event(struct prestera_switch *sw,
795501ef306SVadym Kochan 				       struct prestera_event *evt, void *arg)
796501ef306SVadym Kochan {
79752323ef7SOleksandr Mazur 	struct prestera_port_mac_state smac;
79852323ef7SOleksandr Mazur 	struct prestera_port_event *pevt;
799501ef306SVadym Kochan 	struct delayed_work *caching_dw;
800501ef306SVadym Kochan 	struct prestera_port *port;
801501ef306SVadym Kochan 
80252323ef7SOleksandr Mazur 	if (evt->id == PRESTERA_PORT_EVENT_MAC_STATE_CHANGED) {
80352323ef7SOleksandr Mazur 		pevt = &evt->port_evt;
80452323ef7SOleksandr Mazur 		port = prestera_find_port(sw, pevt->port_id);
805501ef306SVadym Kochan 		if (!port || !port->dev)
806501ef306SVadym Kochan 			return;
807501ef306SVadym Kochan 
808501ef306SVadym Kochan 		caching_dw = &port->cached_hw_stats.caching_dw;
809501ef306SVadym Kochan 
81052323ef7SOleksandr Mazur 		memset(&smac, 0, sizeof(smac));
81152323ef7SOleksandr Mazur 		smac.valid = true;
81252323ef7SOleksandr Mazur 		smac.oper = pevt->data.mac.oper;
81352323ef7SOleksandr Mazur 		if (smac.oper) {
81452323ef7SOleksandr Mazur 			smac.mode = pevt->data.mac.mode;
81552323ef7SOleksandr Mazur 			smac.speed = pevt->data.mac.speed;
81652323ef7SOleksandr Mazur 			smac.duplex = pevt->data.mac.duplex;
81752323ef7SOleksandr Mazur 			smac.fc = pevt->data.mac.fc;
81852323ef7SOleksandr Mazur 			smac.fec = pevt->data.mac.fec;
81952323ef7SOleksandr Mazur 		}
82052323ef7SOleksandr Mazur 		prestera_port_mac_state_cache_write(port, &smac);
821bb5dbf2cSVolodymyr Mytnyk 
822bb5dbf2cSVolodymyr Mytnyk 		if (port->state_mac.oper) {
823704438ddSMaksym Glubokiy 			if (port->phy_link)
824704438ddSMaksym Glubokiy 				phylink_mac_change(port->phy_link, true);
825704438ddSMaksym Glubokiy 			else
826501ef306SVadym Kochan 				netif_carrier_on(port->dev);
82752323ef7SOleksandr Mazur 
828501ef306SVadym Kochan 			if (!delayed_work_pending(caching_dw))
829501ef306SVadym Kochan 				queue_delayed_work(prestera_wq, caching_dw, 0);
830704438ddSMaksym Glubokiy 		} else {
831704438ddSMaksym Glubokiy 			if (port->phy_link)
832704438ddSMaksym Glubokiy 				phylink_mac_change(port->phy_link, false);
833704438ddSMaksym Glubokiy 			else if (netif_running(port->dev) && netif_carrier_ok(port->dev))
834501ef306SVadym Kochan 				netif_carrier_off(port->dev);
83552323ef7SOleksandr Mazur 
836501ef306SVadym Kochan 			if (delayed_work_pending(caching_dw))
837501ef306SVadym Kochan 				cancel_delayed_work(caching_dw);
838501ef306SVadym Kochan 		}
839501ef306SVadym Kochan 	}
840501ef306SVadym Kochan }
841501ef306SVadym Kochan 
prestera_event_handlers_register(struct prestera_switch * sw)842501ef306SVadym Kochan static int prestera_event_handlers_register(struct prestera_switch *sw)
843501ef306SVadym Kochan {
844501ef306SVadym Kochan 	return prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_PORT,
845501ef306SVadym Kochan 						  prestera_port_handle_event,
846501ef306SVadym Kochan 						  NULL);
847501ef306SVadym Kochan }
848501ef306SVadym Kochan 
prestera_event_handlers_unregister(struct prestera_switch * sw)849501ef306SVadym Kochan static void prestera_event_handlers_unregister(struct prestera_switch *sw)
850501ef306SVadym Kochan {
851501ef306SVadym Kochan 	prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_PORT,
852501ef306SVadym Kochan 					     prestera_port_handle_event);
853501ef306SVadym Kochan }
854501ef306SVadym Kochan 
prestera_switch_set_base_mac_addr(struct prestera_switch * sw)855501ef306SVadym Kochan static int prestera_switch_set_base_mac_addr(struct prestera_switch *sw)
856501ef306SVadym Kochan {
857a48acad7SMiquel Raynal 	int ret;
858501ef306SVadym Kochan 
859a48acad7SMiquel Raynal 	if (sw->np)
860a48acad7SMiquel Raynal 		ret = of_get_mac_address(sw->np, sw->base_mac);
86152323ef7SOleksandr Mazur 	if (!is_valid_ether_addr(sw->base_mac) || ret) {
862501ef306SVadym Kochan 		eth_random_addr(sw->base_mac);
863501ef306SVadym Kochan 		dev_info(prestera_dev(sw), "using random base mac address\n");
864501ef306SVadym Kochan 	}
865501ef306SVadym Kochan 
866501ef306SVadym Kochan 	return prestera_hw_switch_mac_set(sw, sw->base_mac);
867501ef306SVadym Kochan }
868501ef306SVadym Kochan 
prestera_lag_by_id(struct prestera_switch * sw,u16 id)869255213caSSerhiy Boiko struct prestera_lag *prestera_lag_by_id(struct prestera_switch *sw, u16 id)
870255213caSSerhiy Boiko {
871255213caSSerhiy Boiko 	return id < sw->lag_max ? &sw->lags[id] : NULL;
872255213caSSerhiy Boiko }
873255213caSSerhiy Boiko 
prestera_lag_by_dev(struct prestera_switch * sw,struct net_device * dev)874255213caSSerhiy Boiko static struct prestera_lag *prestera_lag_by_dev(struct prestera_switch *sw,
875255213caSSerhiy Boiko 						struct net_device *dev)
876255213caSSerhiy Boiko {
877255213caSSerhiy Boiko 	struct prestera_lag *lag;
878255213caSSerhiy Boiko 	u16 id;
879255213caSSerhiy Boiko 
880255213caSSerhiy Boiko 	for (id = 0; id < sw->lag_max; id++) {
881255213caSSerhiy Boiko 		lag = &sw->lags[id];
882255213caSSerhiy Boiko 		if (lag->dev == dev)
883255213caSSerhiy Boiko 			return lag;
884255213caSSerhiy Boiko 	}
885255213caSSerhiy Boiko 
886255213caSSerhiy Boiko 	return NULL;
887255213caSSerhiy Boiko }
888255213caSSerhiy Boiko 
prestera_lag_id(struct prestera_switch * sw,struct net_device * lag_dev,u16 * lag_id)889fec7c9c7SOleksandr Mazur int prestera_lag_id(struct prestera_switch *sw,
890fec7c9c7SOleksandr Mazur 		    struct net_device *lag_dev, u16 *lag_id)
891fec7c9c7SOleksandr Mazur {
892fec7c9c7SOleksandr Mazur 	struct prestera_lag *lag;
893fec7c9c7SOleksandr Mazur 	int free_id = -1;
894fec7c9c7SOleksandr Mazur 	int id;
895fec7c9c7SOleksandr Mazur 
896fec7c9c7SOleksandr Mazur 	for (id = 0; id < sw->lag_max; id++) {
897fec7c9c7SOleksandr Mazur 		lag = prestera_lag_by_id(sw, id);
898fec7c9c7SOleksandr Mazur 		if (lag->member_count) {
899fec7c9c7SOleksandr Mazur 			if (lag->dev == lag_dev) {
900fec7c9c7SOleksandr Mazur 				*lag_id = id;
901fec7c9c7SOleksandr Mazur 				return 0;
902fec7c9c7SOleksandr Mazur 			}
903fec7c9c7SOleksandr Mazur 		} else if (free_id < 0) {
904fec7c9c7SOleksandr Mazur 			free_id = id;
905fec7c9c7SOleksandr Mazur 		}
906fec7c9c7SOleksandr Mazur 	}
907fec7c9c7SOleksandr Mazur 	if (free_id < 0)
908fec7c9c7SOleksandr Mazur 		return -ENOSPC;
909fec7c9c7SOleksandr Mazur 	*lag_id = free_id;
910fec7c9c7SOleksandr Mazur 	return 0;
911fec7c9c7SOleksandr Mazur }
912fec7c9c7SOleksandr Mazur 
prestera_lag_create(struct prestera_switch * sw,struct net_device * lag_dev)913255213caSSerhiy Boiko static struct prestera_lag *prestera_lag_create(struct prestera_switch *sw,
914255213caSSerhiy Boiko 						struct net_device *lag_dev)
915255213caSSerhiy Boiko {
916255213caSSerhiy Boiko 	struct prestera_lag *lag = NULL;
917255213caSSerhiy Boiko 	u16 id;
918255213caSSerhiy Boiko 
919255213caSSerhiy Boiko 	for (id = 0; id < sw->lag_max; id++) {
920255213caSSerhiy Boiko 		lag = &sw->lags[id];
921255213caSSerhiy Boiko 		if (!lag->dev)
922255213caSSerhiy Boiko 			break;
923255213caSSerhiy Boiko 	}
924255213caSSerhiy Boiko 	if (lag) {
925255213caSSerhiy Boiko 		INIT_LIST_HEAD(&lag->members);
926255213caSSerhiy Boiko 		lag->dev = lag_dev;
927255213caSSerhiy Boiko 	}
928255213caSSerhiy Boiko 
929255213caSSerhiy Boiko 	return lag;
930255213caSSerhiy Boiko }
931255213caSSerhiy Boiko 
prestera_lag_destroy(struct prestera_switch * sw,struct prestera_lag * lag)932255213caSSerhiy Boiko static void prestera_lag_destroy(struct prestera_switch *sw,
933255213caSSerhiy Boiko 				 struct prestera_lag *lag)
934255213caSSerhiy Boiko {
935255213caSSerhiy Boiko 	WARN_ON(!list_empty(&lag->members));
936255213caSSerhiy Boiko 	lag->member_count = 0;
937255213caSSerhiy Boiko 	lag->dev = NULL;
938255213caSSerhiy Boiko }
939255213caSSerhiy Boiko 
prestera_lag_port_add(struct prestera_port * port,struct net_device * lag_dev)940255213caSSerhiy Boiko static int prestera_lag_port_add(struct prestera_port *port,
941255213caSSerhiy Boiko 				 struct net_device *lag_dev)
942255213caSSerhiy Boiko {
943255213caSSerhiy Boiko 	struct prestera_switch *sw = port->sw;
944255213caSSerhiy Boiko 	struct prestera_lag *lag;
945255213caSSerhiy Boiko 	int err;
946255213caSSerhiy Boiko 
947255213caSSerhiy Boiko 	lag = prestera_lag_by_dev(sw, lag_dev);
948255213caSSerhiy Boiko 	if (!lag) {
949255213caSSerhiy Boiko 		lag = prestera_lag_create(sw, lag_dev);
950255213caSSerhiy Boiko 		if (!lag)
951255213caSSerhiy Boiko 			return -ENOSPC;
952255213caSSerhiy Boiko 	}
953255213caSSerhiy Boiko 
954255213caSSerhiy Boiko 	if (lag->member_count >= sw->lag_member_max)
955255213caSSerhiy Boiko 		return -ENOSPC;
956255213caSSerhiy Boiko 
957255213caSSerhiy Boiko 	err = prestera_hw_lag_member_add(port, lag->lag_id);
958255213caSSerhiy Boiko 	if (err) {
959255213caSSerhiy Boiko 		if (!lag->member_count)
960255213caSSerhiy Boiko 			prestera_lag_destroy(sw, lag);
961255213caSSerhiy Boiko 		return err;
962255213caSSerhiy Boiko 	}
963255213caSSerhiy Boiko 
964255213caSSerhiy Boiko 	list_add(&port->lag_member, &lag->members);
965255213caSSerhiy Boiko 	lag->member_count++;
966255213caSSerhiy Boiko 	port->lag = lag;
967255213caSSerhiy Boiko 
968255213caSSerhiy Boiko 	return 0;
969255213caSSerhiy Boiko }
970255213caSSerhiy Boiko 
prestera_lag_port_del(struct prestera_port * port)971255213caSSerhiy Boiko static int prestera_lag_port_del(struct prestera_port *port)
972255213caSSerhiy Boiko {
973255213caSSerhiy Boiko 	struct prestera_switch *sw = port->sw;
974255213caSSerhiy Boiko 	struct prestera_lag *lag = port->lag;
975255213caSSerhiy Boiko 	int err;
976255213caSSerhiy Boiko 
977255213caSSerhiy Boiko 	if (!lag || !lag->member_count)
978255213caSSerhiy Boiko 		return -EINVAL;
979255213caSSerhiy Boiko 
980255213caSSerhiy Boiko 	err = prestera_hw_lag_member_del(port, lag->lag_id);
981255213caSSerhiy Boiko 	if (err)
982255213caSSerhiy Boiko 		return err;
983255213caSSerhiy Boiko 
984255213caSSerhiy Boiko 	list_del(&port->lag_member);
985255213caSSerhiy Boiko 	lag->member_count--;
986255213caSSerhiy Boiko 	port->lag = NULL;
987255213caSSerhiy Boiko 
988255213caSSerhiy Boiko 	if (netif_is_bridge_port(lag->dev)) {
989255213caSSerhiy Boiko 		struct net_device *br_dev;
990255213caSSerhiy Boiko 
991255213caSSerhiy Boiko 		br_dev = netdev_master_upper_dev_get(lag->dev);
992255213caSSerhiy Boiko 
993255213caSSerhiy Boiko 		prestera_bridge_port_leave(br_dev, port);
994255213caSSerhiy Boiko 	}
995255213caSSerhiy Boiko 
996255213caSSerhiy Boiko 	if (!lag->member_count)
997255213caSSerhiy Boiko 		prestera_lag_destroy(sw, lag);
998255213caSSerhiy Boiko 
999255213caSSerhiy Boiko 	return 0;
1000255213caSSerhiy Boiko }
1001255213caSSerhiy Boiko 
prestera_port_is_lag_member(const struct prestera_port * port)1002255213caSSerhiy Boiko bool prestera_port_is_lag_member(const struct prestera_port *port)
1003255213caSSerhiy Boiko {
1004255213caSSerhiy Boiko 	return !!port->lag;
1005255213caSSerhiy Boiko }
1006255213caSSerhiy Boiko 
prestera_port_lag_id(const struct prestera_port * port)1007255213caSSerhiy Boiko u16 prestera_port_lag_id(const struct prestera_port *port)
1008255213caSSerhiy Boiko {
1009255213caSSerhiy Boiko 	return port->lag->lag_id;
1010255213caSSerhiy Boiko }
1011255213caSSerhiy Boiko 
prestera_lag_init(struct prestera_switch * sw)1012255213caSSerhiy Boiko static int prestera_lag_init(struct prestera_switch *sw)
1013255213caSSerhiy Boiko {
1014255213caSSerhiy Boiko 	u16 id;
1015255213caSSerhiy Boiko 
1016255213caSSerhiy Boiko 	sw->lags = kcalloc(sw->lag_max, sizeof(*sw->lags), GFP_KERNEL);
1017255213caSSerhiy Boiko 	if (!sw->lags)
1018255213caSSerhiy Boiko 		return -ENOMEM;
1019255213caSSerhiy Boiko 
1020255213caSSerhiy Boiko 	for (id = 0; id < sw->lag_max; id++)
1021255213caSSerhiy Boiko 		sw->lags[id].lag_id = id;
1022255213caSSerhiy Boiko 
1023255213caSSerhiy Boiko 	return 0;
1024255213caSSerhiy Boiko }
1025255213caSSerhiy Boiko 
prestera_lag_fini(struct prestera_switch * sw)1026255213caSSerhiy Boiko static void prestera_lag_fini(struct prestera_switch *sw)
1027255213caSSerhiy Boiko {
1028255213caSSerhiy Boiko 	u8 idx;
1029255213caSSerhiy Boiko 
1030255213caSSerhiy Boiko 	for (idx = 0; idx < sw->lag_max; idx++)
1031255213caSSerhiy Boiko 		WARN_ON(sw->lags[idx].member_count);
1032255213caSSerhiy Boiko 
1033255213caSSerhiy Boiko 	kfree(sw->lags);
1034255213caSSerhiy Boiko }
1035255213caSSerhiy Boiko 
prestera_netdev_check(const struct net_device * dev)1036e1189d9aSVadym Kochan bool prestera_netdev_check(const struct net_device *dev)
1037e1189d9aSVadym Kochan {
1038e1189d9aSVadym Kochan 	return dev->netdev_ops == &prestera_netdev_ops;
1039e1189d9aSVadym Kochan }
1040e1189d9aSVadym Kochan 
prestera_lower_dev_walk(struct net_device * dev,struct netdev_nested_priv * priv)10418b0308feSDavid S. Miller static int prestera_lower_dev_walk(struct net_device *dev,
10428b0308feSDavid S. Miller 				   struct netdev_nested_priv *priv)
1043e1189d9aSVadym Kochan {
10448b0308feSDavid S. Miller 	struct prestera_port **pport = (struct prestera_port **)priv->data;
1045e1189d9aSVadym Kochan 
1046e1189d9aSVadym Kochan 	if (prestera_netdev_check(dev)) {
1047e1189d9aSVadym Kochan 		*pport = netdev_priv(dev);
1048e1189d9aSVadym Kochan 		return 1;
1049e1189d9aSVadym Kochan 	}
1050e1189d9aSVadym Kochan 
1051e1189d9aSVadym Kochan 	return 0;
1052e1189d9aSVadym Kochan }
1053e1189d9aSVadym Kochan 
prestera_port_dev_lower_find(struct net_device * dev)1054e1189d9aSVadym Kochan struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev)
1055e1189d9aSVadym Kochan {
1056e1189d9aSVadym Kochan 	struct prestera_port *port = NULL;
10578b0308feSDavid S. Miller 	struct netdev_nested_priv priv = {
10588b0308feSDavid S. Miller 		.data = (void *)&port,
10598b0308feSDavid S. Miller 	};
1060e1189d9aSVadym Kochan 
1061e1189d9aSVadym Kochan 	if (prestera_netdev_check(dev))
1062e1189d9aSVadym Kochan 		return netdev_priv(dev);
1063e1189d9aSVadym Kochan 
10648b0308feSDavid S. Miller 	netdev_walk_all_lower_dev(dev, prestera_lower_dev_walk, &priv);
1065e1189d9aSVadym Kochan 
1066e1189d9aSVadym Kochan 	return port;
1067e1189d9aSVadym Kochan }
1068e1189d9aSVadym Kochan 
prestera_netdev_port_lower_event(struct net_device * dev,unsigned long event,void * ptr)1069255213caSSerhiy Boiko static int prestera_netdev_port_lower_event(struct net_device *dev,
1070255213caSSerhiy Boiko 					    unsigned long event, void *ptr)
1071255213caSSerhiy Boiko {
1072255213caSSerhiy Boiko 	struct netdev_notifier_changelowerstate_info *info = ptr;
1073255213caSSerhiy Boiko 	struct netdev_lag_lower_state_info *lower_state_info;
1074255213caSSerhiy Boiko 	struct prestera_port *port = netdev_priv(dev);
1075255213caSSerhiy Boiko 	bool enabled;
1076255213caSSerhiy Boiko 
1077255213caSSerhiy Boiko 	if (!netif_is_lag_port(dev))
1078255213caSSerhiy Boiko 		return 0;
1079255213caSSerhiy Boiko 	if (!prestera_port_is_lag_member(port))
1080255213caSSerhiy Boiko 		return 0;
1081255213caSSerhiy Boiko 
1082255213caSSerhiy Boiko 	lower_state_info = info->lower_state_info;
1083255213caSSerhiy Boiko 	enabled = lower_state_info->link_up && lower_state_info->tx_enabled;
1084255213caSSerhiy Boiko 
1085255213caSSerhiy Boiko 	return prestera_hw_lag_member_enable(port, port->lag->lag_id, enabled);
1086255213caSSerhiy Boiko }
1087255213caSSerhiy Boiko 
prestera_lag_master_check(struct net_device * lag_dev,struct netdev_lag_upper_info * info,struct netlink_ext_ack * ext_ack)1088255213caSSerhiy Boiko static bool prestera_lag_master_check(struct net_device *lag_dev,
1089255213caSSerhiy Boiko 				      struct netdev_lag_upper_info *info,
1090255213caSSerhiy Boiko 				      struct netlink_ext_ack *ext_ack)
1091255213caSSerhiy Boiko {
1092255213caSSerhiy Boiko 	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
1093255213caSSerhiy Boiko 		NL_SET_ERR_MSG_MOD(ext_ack, "Unsupported LAG Tx type");
1094255213caSSerhiy Boiko 		return false;
1095255213caSSerhiy Boiko 	}
1096255213caSSerhiy Boiko 
1097255213caSSerhiy Boiko 	return true;
1098255213caSSerhiy Boiko }
1099255213caSSerhiy Boiko 
prestera_netdev_port_event(struct net_device * lower,struct net_device * dev,unsigned long event,void * ptr)1100255213caSSerhiy Boiko static int prestera_netdev_port_event(struct net_device *lower,
1101255213caSSerhiy Boiko 				      struct net_device *dev,
1102e1189d9aSVadym Kochan 				      unsigned long event, void *ptr)
1103e1189d9aSVadym Kochan {
11042efc2256SYevhen Orlov 	struct netdev_notifier_info *info = ptr;
11052efc2256SYevhen Orlov 	struct netdev_notifier_changeupper_info *cu_info;
110682bbaa05SVadym Kochan 	struct prestera_port *port = netdev_priv(dev);
11073d5048ccSVadym Kochan 	struct netlink_ext_ack *extack;
11083d5048ccSVadym Kochan 	struct net_device *upper;
11093d5048ccSVadym Kochan 
11102efc2256SYevhen Orlov 	extack = netdev_notifier_info_to_extack(info);
11112efc2256SYevhen Orlov 	cu_info = container_of(info,
11122efc2256SYevhen Orlov 			       struct netdev_notifier_changeupper_info,
11132efc2256SYevhen Orlov 			       info);
11143d5048ccSVadym Kochan 
1115e1189d9aSVadym Kochan 	switch (event) {
1116e1189d9aSVadym Kochan 	case NETDEV_PRECHANGEUPPER:
11172efc2256SYevhen Orlov 		upper = cu_info->upper_dev;
1118255213caSSerhiy Boiko 		if (!netif_is_bridge_master(upper) &&
1119255213caSSerhiy Boiko 		    !netif_is_lag_master(upper)) {
11203d5048ccSVadym Kochan 			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
11213d5048ccSVadym Kochan 			return -EINVAL;
1122e1189d9aSVadym Kochan 		}
11233d5048ccSVadym Kochan 
11242efc2256SYevhen Orlov 		if (!cu_info->linking)
11253d5048ccSVadym Kochan 			break;
11263d5048ccSVadym Kochan 
11273d5048ccSVadym Kochan 		if (netdev_has_any_upper_dev(upper)) {
11283d5048ccSVadym Kochan 			NL_SET_ERR_MSG_MOD(extack, "Upper device is already enslaved");
11293d5048ccSVadym Kochan 			return -EINVAL;
11303d5048ccSVadym Kochan 		}
1131255213caSSerhiy Boiko 
1132255213caSSerhiy Boiko 		if (netif_is_lag_master(upper) &&
11332efc2256SYevhen Orlov 		    !prestera_lag_master_check(upper, cu_info->upper_info, extack))
1134255213caSSerhiy Boiko 			return -EOPNOTSUPP;
1135255213caSSerhiy Boiko 		if (netif_is_lag_master(upper) && vlan_uses_dev(dev)) {
1136255213caSSerhiy Boiko 			NL_SET_ERR_MSG_MOD(extack,
1137255213caSSerhiy Boiko 					   "Master device is a LAG master and port has a VLAN");
1138255213caSSerhiy Boiko 			return -EINVAL;
1139255213caSSerhiy Boiko 		}
1140255213caSSerhiy Boiko 		if (netif_is_lag_port(dev) && is_vlan_dev(upper) &&
1141255213caSSerhiy Boiko 		    !netif_is_lag_master(vlan_dev_real_dev(upper))) {
1142255213caSSerhiy Boiko 			NL_SET_ERR_MSG_MOD(extack,
1143255213caSSerhiy Boiko 					   "Can not put a VLAN on a LAG port");
1144255213caSSerhiy Boiko 			return -EINVAL;
1145255213caSSerhiy Boiko 		}
11463d5048ccSVadym Kochan 		break;
11473d5048ccSVadym Kochan 
11483d5048ccSVadym Kochan 	case NETDEV_CHANGEUPPER:
11492efc2256SYevhen Orlov 		upper = cu_info->upper_dev;
115082bbaa05SVadym Kochan 		if (netif_is_bridge_master(upper)) {
11512efc2256SYevhen Orlov 			if (cu_info->linking)
11522f5dc00fSVladimir Oltean 				return prestera_bridge_port_join(upper, port,
11532f5dc00fSVladimir Oltean 								 extack);
115482bbaa05SVadym Kochan 			else
115582bbaa05SVadym Kochan 				prestera_bridge_port_leave(upper, port);
1156255213caSSerhiy Boiko 		} else if (netif_is_lag_master(upper)) {
11572efc2256SYevhen Orlov 			if (cu_info->linking)
1158255213caSSerhiy Boiko 				return prestera_lag_port_add(port, upper);
1159255213caSSerhiy Boiko 			else
1160255213caSSerhiy Boiko 				prestera_lag_port_del(port);
116182bbaa05SVadym Kochan 		}
11623d5048ccSVadym Kochan 		break;
1163255213caSSerhiy Boiko 
1164255213caSSerhiy Boiko 	case NETDEV_CHANGELOWERSTATE:
1165255213caSSerhiy Boiko 		return prestera_netdev_port_lower_event(dev, event, ptr);
1166255213caSSerhiy Boiko 	}
1167255213caSSerhiy Boiko 
1168255213caSSerhiy Boiko 	return 0;
1169255213caSSerhiy Boiko }
1170255213caSSerhiy Boiko 
prestera_netdevice_lag_event(struct net_device * lag_dev,unsigned long event,void * ptr)1171255213caSSerhiy Boiko static int prestera_netdevice_lag_event(struct net_device *lag_dev,
1172255213caSSerhiy Boiko 					unsigned long event, void *ptr)
1173255213caSSerhiy Boiko {
1174255213caSSerhiy Boiko 	struct net_device *dev;
1175255213caSSerhiy Boiko 	struct list_head *iter;
1176255213caSSerhiy Boiko 	int err;
1177255213caSSerhiy Boiko 
1178255213caSSerhiy Boiko 	netdev_for_each_lower_dev(lag_dev, dev, iter) {
1179255213caSSerhiy Boiko 		if (prestera_netdev_check(dev)) {
1180255213caSSerhiy Boiko 			err = prestera_netdev_port_event(lag_dev, dev, event,
1181255213caSSerhiy Boiko 							 ptr);
1182255213caSSerhiy Boiko 			if (err)
1183255213caSSerhiy Boiko 				return err;
1184255213caSSerhiy Boiko 		}
11853d5048ccSVadym Kochan 	}
11863d5048ccSVadym Kochan 
11873d5048ccSVadym Kochan 	return 0;
1188e1189d9aSVadym Kochan }
1189e1189d9aSVadym Kochan 
prestera_netdev_event_handler(struct notifier_block * nb,unsigned long event,void * ptr)1190e1189d9aSVadym Kochan static int prestera_netdev_event_handler(struct notifier_block *nb,
1191e1189d9aSVadym Kochan 					 unsigned long event, void *ptr)
1192e1189d9aSVadym Kochan {
1193e1189d9aSVadym Kochan 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1194e1189d9aSVadym Kochan 	int err = 0;
1195e1189d9aSVadym Kochan 
1196e1189d9aSVadym Kochan 	if (prestera_netdev_check(dev))
1197255213caSSerhiy Boiko 		err = prestera_netdev_port_event(dev, dev, event, ptr);
1198255213caSSerhiy Boiko 	else if (netif_is_lag_master(dev))
1199255213caSSerhiy Boiko 		err = prestera_netdevice_lag_event(dev, event, ptr);
1200e1189d9aSVadym Kochan 
1201e1189d9aSVadym Kochan 	return notifier_from_errno(err);
1202e1189d9aSVadym Kochan }
1203e1189d9aSVadym Kochan 
12047950b214SOleksandr Mazur struct prestera_mdb_entry *
prestera_mdb_entry_create(struct prestera_switch * sw,const unsigned char * addr,u16 vid)12057950b214SOleksandr Mazur prestera_mdb_entry_create(struct prestera_switch *sw,
12067950b214SOleksandr Mazur 			  const unsigned char *addr, u16 vid)
12077950b214SOleksandr Mazur {
12087950b214SOleksandr Mazur 	struct prestera_flood_domain *flood_domain;
12097950b214SOleksandr Mazur 	struct prestera_mdb_entry *mdb_entry;
12107950b214SOleksandr Mazur 
12117950b214SOleksandr Mazur 	mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
12127950b214SOleksandr Mazur 	if (!mdb_entry)
12137950b214SOleksandr Mazur 		goto err_mdb_alloc;
12147950b214SOleksandr Mazur 
12157950b214SOleksandr Mazur 	flood_domain = prestera_flood_domain_create(sw);
12167950b214SOleksandr Mazur 	if (!flood_domain)
12177950b214SOleksandr Mazur 		goto err_flood_domain_create;
12187950b214SOleksandr Mazur 
12197950b214SOleksandr Mazur 	mdb_entry->sw = sw;
12207950b214SOleksandr Mazur 	mdb_entry->vid = vid;
12217950b214SOleksandr Mazur 	mdb_entry->flood_domain = flood_domain;
12227950b214SOleksandr Mazur 	ether_addr_copy(mdb_entry->addr, addr);
12237950b214SOleksandr Mazur 
12247950b214SOleksandr Mazur 	if (prestera_hw_mdb_create(mdb_entry))
12257950b214SOleksandr Mazur 		goto err_mdb_hw_create;
12267950b214SOleksandr Mazur 
12277950b214SOleksandr Mazur 	return mdb_entry;
12287950b214SOleksandr Mazur 
12297950b214SOleksandr Mazur err_mdb_hw_create:
12307950b214SOleksandr Mazur 	prestera_flood_domain_destroy(flood_domain);
12317950b214SOleksandr Mazur err_flood_domain_create:
12327950b214SOleksandr Mazur 	kfree(mdb_entry);
12337950b214SOleksandr Mazur err_mdb_alloc:
12347950b214SOleksandr Mazur 	return NULL;
12357950b214SOleksandr Mazur }
12367950b214SOleksandr Mazur 
prestera_mdb_entry_destroy(struct prestera_mdb_entry * mdb_entry)12377950b214SOleksandr Mazur void prestera_mdb_entry_destroy(struct prestera_mdb_entry *mdb_entry)
12387950b214SOleksandr Mazur {
12397950b214SOleksandr Mazur 	prestera_hw_mdb_destroy(mdb_entry);
12407950b214SOleksandr Mazur 	prestera_flood_domain_destroy(mdb_entry->flood_domain);
12417950b214SOleksandr Mazur 	kfree(mdb_entry);
12427950b214SOleksandr Mazur }
12437950b214SOleksandr Mazur 
12447950b214SOleksandr Mazur struct prestera_flood_domain *
prestera_flood_domain_create(struct prestera_switch * sw)12457950b214SOleksandr Mazur prestera_flood_domain_create(struct prestera_switch *sw)
12467950b214SOleksandr Mazur {
12477950b214SOleksandr Mazur 	struct prestera_flood_domain *domain;
12487950b214SOleksandr Mazur 
12497950b214SOleksandr Mazur 	domain = kzalloc(sizeof(*domain), GFP_KERNEL);
12507950b214SOleksandr Mazur 	if (!domain)
12517950b214SOleksandr Mazur 		return NULL;
12527950b214SOleksandr Mazur 
12537950b214SOleksandr Mazur 	domain->sw = sw;
12547950b214SOleksandr Mazur 
12557950b214SOleksandr Mazur 	if (prestera_hw_flood_domain_create(domain)) {
12567950b214SOleksandr Mazur 		kfree(domain);
12577950b214SOleksandr Mazur 		return NULL;
12587950b214SOleksandr Mazur 	}
12597950b214SOleksandr Mazur 
12607950b214SOleksandr Mazur 	INIT_LIST_HEAD(&domain->flood_domain_port_list);
12617950b214SOleksandr Mazur 
12627950b214SOleksandr Mazur 	return domain;
12637950b214SOleksandr Mazur }
12647950b214SOleksandr Mazur 
prestera_flood_domain_destroy(struct prestera_flood_domain * flood_domain)12657950b214SOleksandr Mazur void prestera_flood_domain_destroy(struct prestera_flood_domain *flood_domain)
12667950b214SOleksandr Mazur {
12677950b214SOleksandr Mazur 	WARN_ON(!list_empty(&flood_domain->flood_domain_port_list));
12687950b214SOleksandr Mazur 	WARN_ON_ONCE(prestera_hw_flood_domain_destroy(flood_domain));
12697950b214SOleksandr Mazur 	kfree(flood_domain);
12707950b214SOleksandr Mazur }
12717950b214SOleksandr Mazur 
12727950b214SOleksandr Mazur int
prestera_flood_domain_port_create(struct prestera_flood_domain * flood_domain,struct net_device * dev,u16 vid)12737950b214SOleksandr Mazur prestera_flood_domain_port_create(struct prestera_flood_domain *flood_domain,
12747950b214SOleksandr Mazur 				  struct net_device *dev,
12757950b214SOleksandr Mazur 				  u16 vid)
12767950b214SOleksandr Mazur {
12777950b214SOleksandr Mazur 	struct prestera_flood_domain_port *flood_domain_port;
12787950b214SOleksandr Mazur 	bool is_first_port_in_list = false;
12797950b214SOleksandr Mazur 	int err;
12807950b214SOleksandr Mazur 
12817950b214SOleksandr Mazur 	flood_domain_port = kzalloc(sizeof(*flood_domain_port), GFP_KERNEL);
12827950b214SOleksandr Mazur 	if (!flood_domain_port) {
12837950b214SOleksandr Mazur 		err = -ENOMEM;
12847950b214SOleksandr Mazur 		goto err_port_alloc;
12857950b214SOleksandr Mazur 	}
12867950b214SOleksandr Mazur 
12877950b214SOleksandr Mazur 	flood_domain_port->vid = vid;
12887950b214SOleksandr Mazur 
12897950b214SOleksandr Mazur 	if (list_empty(&flood_domain->flood_domain_port_list))
12907950b214SOleksandr Mazur 		is_first_port_in_list = true;
12917950b214SOleksandr Mazur 
12927950b214SOleksandr Mazur 	list_add(&flood_domain_port->flood_domain_port_node,
12937950b214SOleksandr Mazur 		 &flood_domain->flood_domain_port_list);
12947950b214SOleksandr Mazur 
12957950b214SOleksandr Mazur 	flood_domain_port->flood_domain = flood_domain;
12967950b214SOleksandr Mazur 	flood_domain_port->dev = dev;
12977950b214SOleksandr Mazur 
12987950b214SOleksandr Mazur 	if (!is_first_port_in_list) {
12997950b214SOleksandr Mazur 		err = prestera_hw_flood_domain_ports_reset(flood_domain);
13007950b214SOleksandr Mazur 		if (err)
13017950b214SOleksandr Mazur 			goto err_prestera_mdb_port_create_hw;
13027950b214SOleksandr Mazur 	}
13037950b214SOleksandr Mazur 
13047950b214SOleksandr Mazur 	err = prestera_hw_flood_domain_ports_set(flood_domain);
13057950b214SOleksandr Mazur 	if (err)
13067950b214SOleksandr Mazur 		goto err_prestera_mdb_port_create_hw;
13077950b214SOleksandr Mazur 
13087950b214SOleksandr Mazur 	return 0;
13097950b214SOleksandr Mazur 
13107950b214SOleksandr Mazur err_prestera_mdb_port_create_hw:
13117950b214SOleksandr Mazur 	list_del(&flood_domain_port->flood_domain_port_node);
13127950b214SOleksandr Mazur 	kfree(flood_domain_port);
13137950b214SOleksandr Mazur err_port_alloc:
13147950b214SOleksandr Mazur 	return err;
13157950b214SOleksandr Mazur }
13167950b214SOleksandr Mazur 
13177950b214SOleksandr Mazur void
prestera_flood_domain_port_destroy(struct prestera_flood_domain_port * port)13187950b214SOleksandr Mazur prestera_flood_domain_port_destroy(struct prestera_flood_domain_port *port)
13197950b214SOleksandr Mazur {
13207950b214SOleksandr Mazur 	struct prestera_flood_domain *flood_domain = port->flood_domain;
13217950b214SOleksandr Mazur 
13227950b214SOleksandr Mazur 	list_del(&port->flood_domain_port_node);
13237950b214SOleksandr Mazur 
13247950b214SOleksandr Mazur 	WARN_ON_ONCE(prestera_hw_flood_domain_ports_reset(flood_domain));
13257950b214SOleksandr Mazur 
13267950b214SOleksandr Mazur 	if (!list_empty(&flood_domain->flood_domain_port_list))
13277950b214SOleksandr Mazur 		WARN_ON_ONCE(prestera_hw_flood_domain_ports_set(flood_domain));
13287950b214SOleksandr Mazur 
13297950b214SOleksandr Mazur 	kfree(port);
13307950b214SOleksandr Mazur }
13317950b214SOleksandr Mazur 
13327950b214SOleksandr Mazur struct prestera_flood_domain_port *
prestera_flood_domain_port_find(struct prestera_flood_domain * flood_domain,struct net_device * dev,u16 vid)13337950b214SOleksandr Mazur prestera_flood_domain_port_find(struct prestera_flood_domain *flood_domain,
13347950b214SOleksandr Mazur 				struct net_device *dev, u16 vid)
13357950b214SOleksandr Mazur {
13367950b214SOleksandr Mazur 	struct prestera_flood_domain_port *flood_domain_port;
13377950b214SOleksandr Mazur 
13387950b214SOleksandr Mazur 	list_for_each_entry(flood_domain_port,
13397950b214SOleksandr Mazur 			    &flood_domain->flood_domain_port_list,
13407950b214SOleksandr Mazur 			    flood_domain_port_node)
13417950b214SOleksandr Mazur 		if (flood_domain_port->dev == dev &&
13427950b214SOleksandr Mazur 		    vid == flood_domain_port->vid)
13437950b214SOleksandr Mazur 			return flood_domain_port;
13447950b214SOleksandr Mazur 
13457950b214SOleksandr Mazur 	return NULL;
13467950b214SOleksandr Mazur }
13477950b214SOleksandr Mazur 
prestera_netdev_event_handler_register(struct prestera_switch * sw)1348e1189d9aSVadym Kochan static int prestera_netdev_event_handler_register(struct prestera_switch *sw)
1349e1189d9aSVadym Kochan {
1350e1189d9aSVadym Kochan 	sw->netdev_nb.notifier_call = prestera_netdev_event_handler;
1351e1189d9aSVadym Kochan 
1352e1189d9aSVadym Kochan 	return register_netdevice_notifier(&sw->netdev_nb);
1353e1189d9aSVadym Kochan }
1354e1189d9aSVadym Kochan 
prestera_netdev_event_handler_unregister(struct prestera_switch * sw)1355e1189d9aSVadym Kochan static void prestera_netdev_event_handler_unregister(struct prestera_switch *sw)
1356e1189d9aSVadym Kochan {
1357e1189d9aSVadym Kochan 	unregister_netdevice_notifier(&sw->netdev_nb);
1358e1189d9aSVadym Kochan }
1359e1189d9aSVadym Kochan 
prestera_switch_init(struct prestera_switch * sw)1360501ef306SVadym Kochan static int prestera_switch_init(struct prestera_switch *sw)
1361501ef306SVadym Kochan {
1362501ef306SVadym Kochan 	int err;
1363501ef306SVadym Kochan 
1364a48acad7SMiquel Raynal 	sw->np = sw->dev->dev->of_node;
136552323ef7SOleksandr Mazur 
1366501ef306SVadym Kochan 	err = prestera_hw_switch_init(sw);
1367501ef306SVadym Kochan 	if (err) {
1368501ef306SVadym Kochan 		dev_err(prestera_dev(sw), "Failed to init Switch device\n");
1369501ef306SVadym Kochan 		return err;
1370501ef306SVadym Kochan 	}
1371501ef306SVadym Kochan 
1372501ef306SVadym Kochan 	rwlock_init(&sw->port_list_lock);
1373501ef306SVadym Kochan 	INIT_LIST_HEAD(&sw->port_list);
1374501ef306SVadym Kochan 
1375501ef306SVadym Kochan 	err = prestera_switch_set_base_mac_addr(sw);
1376501ef306SVadym Kochan 	if (err)
1377501ef306SVadym Kochan 		return err;
1378501ef306SVadym Kochan 
1379e1189d9aSVadym Kochan 	err = prestera_netdev_event_handler_register(sw);
1380501ef306SVadym Kochan 	if (err)
1381501ef306SVadym Kochan 		return err;
1382501ef306SVadym Kochan 
138369204174SYevhen Orlov 	err = prestera_router_init(sw);
138469204174SYevhen Orlov 	if (err)
138569204174SYevhen Orlov 		goto err_router_init;
138669204174SYevhen Orlov 
1387e1189d9aSVadym Kochan 	err = prestera_switchdev_init(sw);
1388e1189d9aSVadym Kochan 	if (err)
1389e1189d9aSVadym Kochan 		goto err_swdev_register;
1390e1189d9aSVadym Kochan 
1391e1189d9aSVadym Kochan 	err = prestera_rxtx_switch_init(sw);
1392e1189d9aSVadym Kochan 	if (err)
1393e1189d9aSVadym Kochan 		goto err_rxtx_register;
1394e1189d9aSVadym Kochan 
1395501ef306SVadym Kochan 	err = prestera_event_handlers_register(sw);
1396501ef306SVadym Kochan 	if (err)
1397501ef306SVadym Kochan 		goto err_handlers_register;
1398501ef306SVadym Kochan 
13996e36c7bcSVolodymyr Mytnyk 	err = prestera_counter_init(sw);
14006e36c7bcSVolodymyr Mytnyk 	if (err)
14016e36c7bcSVolodymyr Mytnyk 		goto err_counter_init;
14026e36c7bcSVolodymyr Mytnyk 
14038b474a9fSSerhiy Boiko 	err = prestera_acl_init(sw);
14048b474a9fSSerhiy Boiko 	if (err)
14058b474a9fSSerhiy Boiko 		goto err_acl_init;
14068b474a9fSSerhiy Boiko 
140713defa27SSerhiy Boiko 	err = prestera_span_init(sw);
140813defa27SSerhiy Boiko 	if (err)
140913defa27SSerhiy Boiko 		goto err_span_init;
141013defa27SSerhiy Boiko 
14114beb0c24SLeon Romanovsky 	err = prestera_devlink_traps_register(sw);
141234dd1710SVadym Kochan 	if (err)
141334dd1710SVadym Kochan 		goto err_dl_register;
141434dd1710SVadym Kochan 
1415255213caSSerhiy Boiko 	err = prestera_lag_init(sw);
1416255213caSSerhiy Boiko 	if (err)
1417255213caSSerhiy Boiko 		goto err_lag_init;
1418255213caSSerhiy Boiko 
1419501ef306SVadym Kochan 	err = prestera_create_ports(sw);
1420501ef306SVadym Kochan 	if (err)
1421501ef306SVadym Kochan 		goto err_ports_create;
1422501ef306SVadym Kochan 
14234beb0c24SLeon Romanovsky 	prestera_devlink_register(sw);
1424501ef306SVadym Kochan 	return 0;
1425501ef306SVadym Kochan 
1426501ef306SVadym Kochan err_ports_create:
1427255213caSSerhiy Boiko 	prestera_lag_fini(sw);
1428255213caSSerhiy Boiko err_lag_init:
14294beb0c24SLeon Romanovsky 	prestera_devlink_traps_unregister(sw);
143034dd1710SVadym Kochan err_dl_register:
143113defa27SSerhiy Boiko 	prestera_span_fini(sw);
143213defa27SSerhiy Boiko err_span_init:
14338b474a9fSSerhiy Boiko 	prestera_acl_fini(sw);
14348b474a9fSSerhiy Boiko err_acl_init:
14356e36c7bcSVolodymyr Mytnyk 	prestera_counter_fini(sw);
14366e36c7bcSVolodymyr Mytnyk err_counter_init:
1437501ef306SVadym Kochan 	prestera_event_handlers_unregister(sw);
1438501ef306SVadym Kochan err_handlers_register:
1439501ef306SVadym Kochan 	prestera_rxtx_switch_fini(sw);
1440e1189d9aSVadym Kochan err_rxtx_register:
1441e1189d9aSVadym Kochan 	prestera_switchdev_fini(sw);
1442e1189d9aSVadym Kochan err_swdev_register:
144369204174SYevhen Orlov 	prestera_router_fini(sw);
144469204174SYevhen Orlov err_router_init:
1445e1189d9aSVadym Kochan 	prestera_netdev_event_handler_unregister(sw);
1446501ef306SVadym Kochan 	prestera_hw_switch_fini(sw);
1447501ef306SVadym Kochan 
1448501ef306SVadym Kochan 	return err;
1449501ef306SVadym Kochan }
1450501ef306SVadym Kochan 
prestera_switch_fini(struct prestera_switch * sw)1451501ef306SVadym Kochan static void prestera_switch_fini(struct prestera_switch *sw)
1452501ef306SVadym Kochan {
14534beb0c24SLeon Romanovsky 	prestera_devlink_unregister(sw);
1454501ef306SVadym Kochan 	prestera_destroy_ports(sw);
1455255213caSSerhiy Boiko 	prestera_lag_fini(sw);
14564beb0c24SLeon Romanovsky 	prestera_devlink_traps_unregister(sw);
145713defa27SSerhiy Boiko 	prestera_span_fini(sw);
14588b474a9fSSerhiy Boiko 	prestera_acl_fini(sw);
14596e36c7bcSVolodymyr Mytnyk 	prestera_counter_fini(sw);
1460501ef306SVadym Kochan 	prestera_event_handlers_unregister(sw);
1461501ef306SVadym Kochan 	prestera_rxtx_switch_fini(sw);
1462e1189d9aSVadym Kochan 	prestera_switchdev_fini(sw);
1463e179f045SYevhen Orlov 	prestera_router_fini(sw);
1464e1189d9aSVadym Kochan 	prestera_netdev_event_handler_unregister(sw);
1465501ef306SVadym Kochan 	prestera_hw_switch_fini(sw);
146652323ef7SOleksandr Mazur 	of_node_put(sw->np);
1467501ef306SVadym Kochan }
1468501ef306SVadym Kochan 
prestera_device_register(struct prestera_device * dev)1469501ef306SVadym Kochan int prestera_device_register(struct prestera_device *dev)
1470501ef306SVadym Kochan {
1471501ef306SVadym Kochan 	struct prestera_switch *sw;
1472501ef306SVadym Kochan 	int err;
1473501ef306SVadym Kochan 
1474919d13a7SLeon Romanovsky 	sw = prestera_devlink_alloc(dev);
1475501ef306SVadym Kochan 	if (!sw)
1476501ef306SVadym Kochan 		return -ENOMEM;
1477501ef306SVadym Kochan 
1478501ef306SVadym Kochan 	dev->priv = sw;
1479501ef306SVadym Kochan 	sw->dev = dev;
1480501ef306SVadym Kochan 
1481501ef306SVadym Kochan 	err = prestera_switch_init(sw);
1482501ef306SVadym Kochan 	if (err) {
148334dd1710SVadym Kochan 		prestera_devlink_free(sw);
1484501ef306SVadym Kochan 		return err;
1485501ef306SVadym Kochan 	}
1486501ef306SVadym Kochan 
1487501ef306SVadym Kochan 	return 0;
1488501ef306SVadym Kochan }
1489501ef306SVadym Kochan EXPORT_SYMBOL(prestera_device_register);
1490501ef306SVadym Kochan 
prestera_device_unregister(struct prestera_device * dev)1491501ef306SVadym Kochan void prestera_device_unregister(struct prestera_device *dev)
1492501ef306SVadym Kochan {
1493501ef306SVadym Kochan 	struct prestera_switch *sw = dev->priv;
1494501ef306SVadym Kochan 
1495501ef306SVadym Kochan 	prestera_switch_fini(sw);
149634dd1710SVadym Kochan 	prestera_devlink_free(sw);
1497501ef306SVadym Kochan }
1498501ef306SVadym Kochan EXPORT_SYMBOL(prestera_device_unregister);
1499501ef306SVadym Kochan 
prestera_module_init(void)1500501ef306SVadym Kochan static int __init prestera_module_init(void)
1501501ef306SVadym Kochan {
1502501ef306SVadym Kochan 	prestera_wq = alloc_workqueue("prestera", 0, 0);
1503501ef306SVadym Kochan 	if (!prestera_wq)
1504501ef306SVadym Kochan 		return -ENOMEM;
1505501ef306SVadym Kochan 
15064394fbcbSYevhen Orlov 	prestera_owq = alloc_ordered_workqueue("prestera_ordered", 0);
15074a6806cfSYang Yingliang 	if (!prestera_owq) {
15084a6806cfSYang Yingliang 		destroy_workqueue(prestera_wq);
15094394fbcbSYevhen Orlov 		return -ENOMEM;
15104a6806cfSYang Yingliang 	}
15114394fbcbSYevhen Orlov 
1512501ef306SVadym Kochan 	return 0;
1513501ef306SVadym Kochan }
1514501ef306SVadym Kochan 
prestera_module_exit(void)1515501ef306SVadym Kochan static void __exit prestera_module_exit(void)
1516501ef306SVadym Kochan {
1517501ef306SVadym Kochan 	destroy_workqueue(prestera_wq);
15184394fbcbSYevhen Orlov 	destroy_workqueue(prestera_owq);
1519501ef306SVadym Kochan }
1520501ef306SVadym Kochan 
1521501ef306SVadym Kochan module_init(prestera_module_init);
1522501ef306SVadym Kochan module_exit(prestera_module_exit);
1523501ef306SVadym Kochan 
1524501ef306SVadym Kochan MODULE_LICENSE("Dual BSD/GPL");
1525501ef306SVadym Kochan MODULE_DESCRIPTION("Marvell Prestera switch driver");
1526