162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/etherdevice.h>
562306a36Sopenharmony_ci#include <linux/jiffies.h>
662306a36Sopenharmony_ci#include <linux/list.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/netdev_features.h>
962306a36Sopenharmony_ci#include <linux/of.h>
1062306a36Sopenharmony_ci#include <linux/of_net.h>
1162306a36Sopenharmony_ci#include <linux/if_vlan.h>
1262306a36Sopenharmony_ci#include <linux/phylink.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "prestera.h"
1562306a36Sopenharmony_ci#include "prestera_hw.h"
1662306a36Sopenharmony_ci#include "prestera_acl.h"
1762306a36Sopenharmony_ci#include "prestera_flow.h"
1862306a36Sopenharmony_ci#include "prestera_span.h"
1962306a36Sopenharmony_ci#include "prestera_rxtx.h"
2062306a36Sopenharmony_ci#include "prestera_devlink.h"
2162306a36Sopenharmony_ci#include "prestera_ethtool.h"
2262306a36Sopenharmony_ci#include "prestera_counter.h"
2362306a36Sopenharmony_ci#include "prestera_switchdev.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define PRESTERA_MTU_DEFAULT	1536
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define PRESTERA_STATS_DELAY_MS	1000
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define PRESTERA_MAC_ADDR_NUM_MAX	255
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic struct workqueue_struct *prestera_wq;
3262306a36Sopenharmony_cistatic struct workqueue_struct *prestera_owq;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_civoid prestera_queue_work(struct work_struct *work)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	queue_work(prestera_owq, work);
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_civoid prestera_queue_delayed_work(struct delayed_work *work, unsigned long delay)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	queue_delayed_work(prestera_wq, work, delay);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_civoid prestera_queue_drain(void)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	drain_workqueue(prestera_wq);
4762306a36Sopenharmony_ci	drain_workqueue(prestera_owq);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ciint prestera_port_learning_set(struct prestera_port *port, bool learn)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	return prestera_hw_port_learning_set(port, learn);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ciint prestera_port_uc_flood_set(struct prestera_port *port, bool flood)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	return prestera_hw_port_uc_flood_set(port, flood);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciint prestera_port_mc_flood_set(struct prestera_port *port, bool flood)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	return prestera_hw_port_mc_flood_set(port, flood);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciint prestera_port_br_locked_set(struct prestera_port *port, bool br_locked)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	return prestera_hw_port_br_locked_set(port, br_locked);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciint prestera_port_pvid_set(struct prestera_port *port, u16 vid)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	enum prestera_accept_frm_type frm_type;
7362306a36Sopenharmony_ci	int err;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	frm_type = PRESTERA_ACCEPT_FRAME_TYPE_TAGGED;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (vid) {
7862306a36Sopenharmony_ci		err = prestera_hw_vlan_port_vid_set(port, vid);
7962306a36Sopenharmony_ci		if (err)
8062306a36Sopenharmony_ci			return err;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		frm_type = PRESTERA_ACCEPT_FRAME_TYPE_ALL;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	err = prestera_hw_port_accept_frm_type(port, frm_type);
8662306a36Sopenharmony_ci	if (err && frm_type == PRESTERA_ACCEPT_FRAME_TYPE_ALL)
8762306a36Sopenharmony_ci		prestera_hw_vlan_port_vid_set(port, port->pvid);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	port->pvid = vid;
9062306a36Sopenharmony_ci	return 0;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistruct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
9462306a36Sopenharmony_ci						 u32 dev_id, u32 hw_id)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct prestera_port *port = NULL, *tmp;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	read_lock(&sw->port_list_lock);
9962306a36Sopenharmony_ci	list_for_each_entry(tmp, &sw->port_list, list) {
10062306a36Sopenharmony_ci		if (tmp->dev_id == dev_id && tmp->hw_id == hw_id) {
10162306a36Sopenharmony_ci			port = tmp;
10262306a36Sopenharmony_ci			break;
10362306a36Sopenharmony_ci		}
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci	read_unlock(&sw->port_list_lock);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return port;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistruct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct prestera_port *port = NULL, *tmp;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	read_lock(&sw->port_list_lock);
11562306a36Sopenharmony_ci	list_for_each_entry(tmp, &sw->port_list, list) {
11662306a36Sopenharmony_ci		if (tmp->id == id) {
11762306a36Sopenharmony_ci			port = tmp;
11862306a36Sopenharmony_ci			break;
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	read_unlock(&sw->port_list_lock);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return port;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistruct prestera_switch *prestera_switch_get(struct net_device *dev)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct prestera_port *port;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	port = prestera_port_dev_lower_find(dev);
13162306a36Sopenharmony_ci	return port ? port->sw : NULL;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ciint prestera_port_cfg_mac_read(struct prestera_port *port,
13562306a36Sopenharmony_ci			       struct prestera_port_mac_config *cfg)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	*cfg = port->cfg_mac;
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciint prestera_port_cfg_mac_write(struct prestera_port *port,
14262306a36Sopenharmony_ci				struct prestera_port_mac_config *cfg)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	int err;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	err = prestera_hw_port_mac_mode_set(port, cfg->admin,
14762306a36Sopenharmony_ci					    cfg->mode, cfg->inband, cfg->speed,
14862306a36Sopenharmony_ci					    cfg->duplex, cfg->fec);
14962306a36Sopenharmony_ci	if (err)
15062306a36Sopenharmony_ci		return err;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	port->cfg_mac = *cfg;
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int prestera_port_open(struct net_device *dev)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
15962306a36Sopenharmony_ci	struct prestera_port_mac_config cfg_mac;
16062306a36Sopenharmony_ci	int err = 0;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (port->phy_link) {
16362306a36Sopenharmony_ci		phylink_start(port->phy_link);
16462306a36Sopenharmony_ci	} else {
16562306a36Sopenharmony_ci		if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) {
16662306a36Sopenharmony_ci			err = prestera_port_cfg_mac_read(port, &cfg_mac);
16762306a36Sopenharmony_ci			if (!err) {
16862306a36Sopenharmony_ci				cfg_mac.admin = true;
16962306a36Sopenharmony_ci				err = prestera_port_cfg_mac_write(port,
17062306a36Sopenharmony_ci								  &cfg_mac);
17162306a36Sopenharmony_ci			}
17262306a36Sopenharmony_ci		} else {
17362306a36Sopenharmony_ci			port->cfg_phy.admin = true;
17462306a36Sopenharmony_ci			err = prestera_hw_port_phy_mode_set(port, true,
17562306a36Sopenharmony_ci							    port->autoneg,
17662306a36Sopenharmony_ci							    port->cfg_phy.mode,
17762306a36Sopenharmony_ci							    port->adver_link_modes,
17862306a36Sopenharmony_ci							    port->cfg_phy.mdix);
17962306a36Sopenharmony_ci		}
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	netif_start_queue(dev);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return err;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic int prestera_port_close(struct net_device *dev)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
19062306a36Sopenharmony_ci	struct prestera_port_mac_config cfg_mac;
19162306a36Sopenharmony_ci	int err = 0;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	netif_stop_queue(dev);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (port->phy_link) {
19662306a36Sopenharmony_ci		phylink_stop(port->phy_link);
19762306a36Sopenharmony_ci		phylink_disconnect_phy(port->phy_link);
19862306a36Sopenharmony_ci		err = prestera_port_cfg_mac_read(port, &cfg_mac);
19962306a36Sopenharmony_ci		if (!err) {
20062306a36Sopenharmony_ci			cfg_mac.admin = false;
20162306a36Sopenharmony_ci			prestera_port_cfg_mac_write(port, &cfg_mac);
20262306a36Sopenharmony_ci		}
20362306a36Sopenharmony_ci	} else {
20462306a36Sopenharmony_ci		if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) {
20562306a36Sopenharmony_ci			err = prestera_port_cfg_mac_read(port, &cfg_mac);
20662306a36Sopenharmony_ci			if (!err) {
20762306a36Sopenharmony_ci				cfg_mac.admin = false;
20862306a36Sopenharmony_ci				prestera_port_cfg_mac_write(port, &cfg_mac);
20962306a36Sopenharmony_ci			}
21062306a36Sopenharmony_ci		} else {
21162306a36Sopenharmony_ci			port->cfg_phy.admin = false;
21262306a36Sopenharmony_ci			err = prestera_hw_port_phy_mode_set(port, false, port->autoneg,
21362306a36Sopenharmony_ci							    port->cfg_phy.mode,
21462306a36Sopenharmony_ci							    port->adver_link_modes,
21562306a36Sopenharmony_ci							    port->cfg_phy.mdix);
21662306a36Sopenharmony_ci		}
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return err;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic void
22362306a36Sopenharmony_ciprestera_port_mac_state_cache_read(struct prestera_port *port,
22462306a36Sopenharmony_ci				   struct prestera_port_mac_state *state)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	spin_lock(&port->state_mac_lock);
22762306a36Sopenharmony_ci	*state = port->state_mac;
22862306a36Sopenharmony_ci	spin_unlock(&port->state_mac_lock);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void
23262306a36Sopenharmony_ciprestera_port_mac_state_cache_write(struct prestera_port *port,
23362306a36Sopenharmony_ci				    struct prestera_port_mac_state *state)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	spin_lock(&port->state_mac_lock);
23662306a36Sopenharmony_ci	port->state_mac = *state;
23762306a36Sopenharmony_ci	spin_unlock(&port->state_mac_lock);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic struct prestera_port *prestera_pcs_to_port(struct phylink_pcs *pcs)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	return container_of(pcs, struct prestera_port, phylink_pcs);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic void prestera_mac_config(struct phylink_config *config,
24662306a36Sopenharmony_ci				unsigned int an_mode,
24762306a36Sopenharmony_ci				const struct phylink_link_state *state)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void prestera_mac_link_down(struct phylink_config *config,
25262306a36Sopenharmony_ci				   unsigned int mode, phy_interface_t interface)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct net_device *ndev = to_net_dev(config->dev);
25562306a36Sopenharmony_ci	struct prestera_port *port = netdev_priv(ndev);
25662306a36Sopenharmony_ci	struct prestera_port_mac_state state_mac;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* Invalidate. Parameters will update on next link event. */
25962306a36Sopenharmony_ci	memset(&state_mac, 0, sizeof(state_mac));
26062306a36Sopenharmony_ci	state_mac.valid = false;
26162306a36Sopenharmony_ci	prestera_port_mac_state_cache_write(port, &state_mac);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void prestera_mac_link_up(struct phylink_config *config,
26562306a36Sopenharmony_ci				 struct phy_device *phy,
26662306a36Sopenharmony_ci				 unsigned int mode, phy_interface_t interface,
26762306a36Sopenharmony_ci				 int speed, int duplex,
26862306a36Sopenharmony_ci				 bool tx_pause, bool rx_pause)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic struct phylink_pcs *
27362306a36Sopenharmony_ciprestera_mac_select_pcs(struct phylink_config *config,
27462306a36Sopenharmony_ci			phy_interface_t interface)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct net_device *dev = to_net_dev(config->dev);
27762306a36Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return &port->phylink_pcs;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic void prestera_pcs_get_state(struct phylink_pcs *pcs,
28362306a36Sopenharmony_ci				   struct phylink_link_state *state)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct prestera_port *port = container_of(pcs, struct prestera_port,
28662306a36Sopenharmony_ci						  phylink_pcs);
28762306a36Sopenharmony_ci	struct prestera_port_mac_state smac;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	prestera_port_mac_state_cache_read(port, &smac);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (smac.valid) {
29262306a36Sopenharmony_ci		state->link = smac.oper ? 1 : 0;
29362306a36Sopenharmony_ci		/* AN is completed, when port is up */
29462306a36Sopenharmony_ci		state->an_complete = (smac.oper && port->autoneg) ? 1 : 0;
29562306a36Sopenharmony_ci		state->speed = smac.speed;
29662306a36Sopenharmony_ci		state->duplex = smac.duplex;
29762306a36Sopenharmony_ci	} else {
29862306a36Sopenharmony_ci		state->link = 0;
29962306a36Sopenharmony_ci		state->an_complete = 0;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int prestera_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
30462306a36Sopenharmony_ci			       phy_interface_t interface,
30562306a36Sopenharmony_ci			       const unsigned long *advertising,
30662306a36Sopenharmony_ci			       bool permit_pause_to_mac)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct prestera_port *port = prestera_pcs_to_port(pcs);
30962306a36Sopenharmony_ci	struct prestera_port_mac_config cfg_mac;
31062306a36Sopenharmony_ci	int err;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	err = prestera_port_cfg_mac_read(port, &cfg_mac);
31362306a36Sopenharmony_ci	if (err)
31462306a36Sopenharmony_ci		return err;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	cfg_mac.admin = true;
31762306a36Sopenharmony_ci	cfg_mac.fec = PRESTERA_PORT_FEC_OFF;
31862306a36Sopenharmony_ci	cfg_mac.inband = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	switch (interface) {
32162306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_10GBASER:
32262306a36Sopenharmony_ci		cfg_mac.speed = SPEED_10000;
32362306a36Sopenharmony_ci		cfg_mac.mode = PRESTERA_MAC_MODE_SR_LR;
32462306a36Sopenharmony_ci		break;
32562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_2500BASEX:
32662306a36Sopenharmony_ci		cfg_mac.speed = SPEED_2500;
32762306a36Sopenharmony_ci		cfg_mac.duplex = DUPLEX_FULL;
32862306a36Sopenharmony_ci		cfg_mac.mode = PRESTERA_MAC_MODE_SGMII;
32962306a36Sopenharmony_ci		break;
33062306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_SGMII:
33162306a36Sopenharmony_ci		cfg_mac.mode = PRESTERA_MAC_MODE_SGMII;
33262306a36Sopenharmony_ci		break;
33362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_1000BASEX:
33462306a36Sopenharmony_ci	default:
33562306a36Sopenharmony_ci		cfg_mac.speed = SPEED_1000;
33662306a36Sopenharmony_ci		cfg_mac.duplex = DUPLEX_FULL;
33762306a36Sopenharmony_ci		cfg_mac.mode = PRESTERA_MAC_MODE_1000BASE_X;
33862306a36Sopenharmony_ci		break;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	err = prestera_port_cfg_mac_write(port, &cfg_mac);
34262306a36Sopenharmony_ci	if (err)
34362306a36Sopenharmony_ci		return err;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic void prestera_pcs_an_restart(struct phylink_pcs *pcs)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	/* TODO: add 1000basex AN restart support
35162306a36Sopenharmony_ci	 * (Currently FW has no support for 1000baseX AN restart, but it will in the future,
35262306a36Sopenharmony_ci	 * so as for now the function would stay empty.)
35362306a36Sopenharmony_ci	 */
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic const struct phylink_mac_ops prestera_mac_ops = {
35762306a36Sopenharmony_ci	.mac_select_pcs = prestera_mac_select_pcs,
35862306a36Sopenharmony_ci	.mac_config = prestera_mac_config,
35962306a36Sopenharmony_ci	.mac_link_down = prestera_mac_link_down,
36062306a36Sopenharmony_ci	.mac_link_up = prestera_mac_link_up,
36162306a36Sopenharmony_ci};
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic const struct phylink_pcs_ops prestera_pcs_ops = {
36462306a36Sopenharmony_ci	.pcs_get_state = prestera_pcs_get_state,
36562306a36Sopenharmony_ci	.pcs_config = prestera_pcs_config,
36662306a36Sopenharmony_ci	.pcs_an_restart = prestera_pcs_an_restart,
36762306a36Sopenharmony_ci};
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int prestera_port_sfp_bind(struct prestera_port *port)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct prestera_switch *sw = port->sw;
37262306a36Sopenharmony_ci	struct device_node *ports, *node;
37362306a36Sopenharmony_ci	struct fwnode_handle *fwnode;
37462306a36Sopenharmony_ci	struct phylink *phy_link;
37562306a36Sopenharmony_ci	int err;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (!sw->np)
37862306a36Sopenharmony_ci		return 0;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	of_node_get(sw->np);
38162306a36Sopenharmony_ci	ports = of_find_node_by_name(sw->np, "ports");
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	for_each_child_of_node(ports, node) {
38462306a36Sopenharmony_ci		int num;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		err = of_property_read_u32(node, "prestera,port-num", &num);
38762306a36Sopenharmony_ci		if (err) {
38862306a36Sopenharmony_ci			dev_err(sw->dev->dev,
38962306a36Sopenharmony_ci				"device node %pOF has no valid reg property: %d\n",
39062306a36Sopenharmony_ci				node, err);
39162306a36Sopenharmony_ci			goto out;
39262306a36Sopenharmony_ci		}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		if (port->fp_id != num)
39562306a36Sopenharmony_ci			continue;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		port->phylink_pcs.ops = &prestera_pcs_ops;
39862306a36Sopenharmony_ci		port->phylink_pcs.neg_mode = true;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		port->phy_config.dev = &port->dev->dev;
40162306a36Sopenharmony_ci		port->phy_config.type = PHYLINK_NETDEV;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		fwnode = of_fwnode_handle(node);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		__set_bit(PHY_INTERFACE_MODE_10GBASER,
40662306a36Sopenharmony_ci			  port->phy_config.supported_interfaces);
40762306a36Sopenharmony_ci		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
40862306a36Sopenharmony_ci			  port->phy_config.supported_interfaces);
40962306a36Sopenharmony_ci		__set_bit(PHY_INTERFACE_MODE_SGMII,
41062306a36Sopenharmony_ci			  port->phy_config.supported_interfaces);
41162306a36Sopenharmony_ci		__set_bit(PHY_INTERFACE_MODE_1000BASEX,
41262306a36Sopenharmony_ci			  port->phy_config.supported_interfaces);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		port->phy_config.mac_capabilities =
41562306a36Sopenharmony_ci			MAC_1000 | MAC_2500FD | MAC_10000FD;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		phy_link = phylink_create(&port->phy_config, fwnode,
41862306a36Sopenharmony_ci					  PHY_INTERFACE_MODE_INTERNAL,
41962306a36Sopenharmony_ci					  &prestera_mac_ops);
42062306a36Sopenharmony_ci		if (IS_ERR(phy_link)) {
42162306a36Sopenharmony_ci			netdev_err(port->dev, "failed to create phylink\n");
42262306a36Sopenharmony_ci			err = PTR_ERR(phy_link);
42362306a36Sopenharmony_ci			goto out;
42462306a36Sopenharmony_ci		}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		port->phy_link = phy_link;
42762306a36Sopenharmony_ci		break;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ciout:
43162306a36Sopenharmony_ci	of_node_put(node);
43262306a36Sopenharmony_ci	of_node_put(ports);
43362306a36Sopenharmony_ci	return err;
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic int prestera_port_sfp_unbind(struct prestera_port *port)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	if (port->phy_link)
43962306a36Sopenharmony_ci		phylink_destroy(port->phy_link);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic netdev_tx_t prestera_port_xmit(struct sk_buff *skb,
44562306a36Sopenharmony_ci				      struct net_device *dev)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	return prestera_rxtx_xmit(netdev_priv(dev), skb);
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ciint prestera_is_valid_mac_addr(struct prestera_port *port, const u8 *addr)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr))
45362306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* firmware requires that port's MAC address contains first 5 bytes
45662306a36Sopenharmony_ci	 * of the base MAC address
45762306a36Sopenharmony_ci	 */
45862306a36Sopenharmony_ci	if (memcmp(port->sw->base_mac, addr, ETH_ALEN - 1))
45962306a36Sopenharmony_ci		return -EINVAL;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	return 0;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic int prestera_port_set_mac_address(struct net_device *dev, void *p)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
46762306a36Sopenharmony_ci	struct sockaddr *addr = p;
46862306a36Sopenharmony_ci	int err;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	err = prestera_is_valid_mac_addr(port, addr->sa_data);
47162306a36Sopenharmony_ci	if (err)
47262306a36Sopenharmony_ci		return err;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	err = prestera_hw_port_mac_set(port, addr->sa_data);
47562306a36Sopenharmony_ci	if (err)
47662306a36Sopenharmony_ci		return err;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr->sa_data);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return 0;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic int prestera_port_change_mtu(struct net_device *dev, int mtu)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
48662306a36Sopenharmony_ci	int err;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	err = prestera_hw_port_mtu_set(port, mtu);
48962306a36Sopenharmony_ci	if (err)
49062306a36Sopenharmony_ci		return err;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	dev->mtu = mtu;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return 0;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic void prestera_port_get_stats64(struct net_device *dev,
49862306a36Sopenharmony_ci				      struct rtnl_link_stats64 *stats)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
50162306a36Sopenharmony_ci	struct prestera_port_stats *port_stats = &port->cached_hw_stats.stats;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	stats->rx_packets = port_stats->broadcast_frames_received +
50462306a36Sopenharmony_ci				port_stats->multicast_frames_received +
50562306a36Sopenharmony_ci				port_stats->unicast_frames_received;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	stats->tx_packets = port_stats->broadcast_frames_sent +
50862306a36Sopenharmony_ci				port_stats->multicast_frames_sent +
50962306a36Sopenharmony_ci				port_stats->unicast_frames_sent;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	stats->rx_bytes = port_stats->good_octets_received;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	stats->tx_bytes = port_stats->good_octets_sent;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	stats->rx_errors = port_stats->rx_error_frame_received;
51662306a36Sopenharmony_ci	stats->tx_errors = port_stats->mac_trans_error;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	stats->rx_dropped = port_stats->buffer_overrun;
51962306a36Sopenharmony_ci	stats->tx_dropped = 0;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	stats->multicast = port_stats->multicast_frames_received;
52262306a36Sopenharmony_ci	stats->collisions = port_stats->excessive_collision;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	stats->rx_crc_errors = port_stats->bad_crc;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic void prestera_port_get_hw_stats(struct prestera_port *port)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	prestera_hw_port_stats_get(port, &port->cached_hw_stats.stats);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic void prestera_port_stats_update(struct work_struct *work)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct prestera_port *port =
53562306a36Sopenharmony_ci		container_of(work, struct prestera_port,
53662306a36Sopenharmony_ci			     cached_hw_stats.caching_dw.work);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	prestera_port_get_hw_stats(port);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	queue_delayed_work(prestera_wq, &port->cached_hw_stats.caching_dw,
54162306a36Sopenharmony_ci			   msecs_to_jiffies(PRESTERA_STATS_DELAY_MS));
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic int prestera_port_setup_tc(struct net_device *dev,
54562306a36Sopenharmony_ci				  enum tc_setup_type type,
54662306a36Sopenharmony_ci				  void *type_data)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	switch (type) {
55162306a36Sopenharmony_ci	case TC_SETUP_BLOCK:
55262306a36Sopenharmony_ci		return prestera_flow_block_setup(port, type_data);
55362306a36Sopenharmony_ci	default:
55462306a36Sopenharmony_ci		return -EOPNOTSUPP;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic const struct net_device_ops prestera_netdev_ops = {
55962306a36Sopenharmony_ci	.ndo_open = prestera_port_open,
56062306a36Sopenharmony_ci	.ndo_stop = prestera_port_close,
56162306a36Sopenharmony_ci	.ndo_start_xmit = prestera_port_xmit,
56262306a36Sopenharmony_ci	.ndo_setup_tc = prestera_port_setup_tc,
56362306a36Sopenharmony_ci	.ndo_change_mtu = prestera_port_change_mtu,
56462306a36Sopenharmony_ci	.ndo_get_stats64 = prestera_port_get_stats64,
56562306a36Sopenharmony_ci	.ndo_set_mac_address = prestera_port_set_mac_address,
56662306a36Sopenharmony_ci};
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ciint prestera_port_autoneg_set(struct prestera_port *port, u64 link_modes)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	int err;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (port->autoneg && port->adver_link_modes == link_modes)
57362306a36Sopenharmony_ci		return 0;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	err = prestera_hw_port_phy_mode_set(port, port->cfg_phy.admin,
57662306a36Sopenharmony_ci					    true, 0, link_modes,
57762306a36Sopenharmony_ci					    port->cfg_phy.mdix);
57862306a36Sopenharmony_ci	if (err)
57962306a36Sopenharmony_ci		return err;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF);
58262306a36Sopenharmony_ci	port->adver_link_modes = link_modes;
58362306a36Sopenharmony_ci	port->cfg_phy.mode = 0;
58462306a36Sopenharmony_ci	port->autoneg = true;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	return 0;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic void prestera_port_list_add(struct prestera_port *port)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	write_lock(&port->sw->port_list_lock);
59262306a36Sopenharmony_ci	list_add(&port->list, &port->sw->port_list);
59362306a36Sopenharmony_ci	write_unlock(&port->sw->port_list_lock);
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic void prestera_port_list_del(struct prestera_port *port)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	write_lock(&port->sw->port_list_lock);
59962306a36Sopenharmony_ci	list_del(&port->list);
60062306a36Sopenharmony_ci	write_unlock(&port->sw->port_list_lock);
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic int prestera_port_create(struct prestera_switch *sw, u32 id)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct prestera_port_mac_config cfg_mac;
60662306a36Sopenharmony_ci	struct prestera_port *port;
60762306a36Sopenharmony_ci	struct net_device *dev;
60862306a36Sopenharmony_ci	int err;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(*port));
61162306a36Sopenharmony_ci	if (!dev)
61262306a36Sopenharmony_ci		return -ENOMEM;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	port = netdev_priv(dev);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	INIT_LIST_HEAD(&port->vlans_list);
61762306a36Sopenharmony_ci	port->pvid = PRESTERA_DEFAULT_VID;
61862306a36Sopenharmony_ci	port->lag = NULL;
61962306a36Sopenharmony_ci	port->dev = dev;
62062306a36Sopenharmony_ci	port->id = id;
62162306a36Sopenharmony_ci	port->sw = sw;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	spin_lock_init(&port->state_mac_lock);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	err = prestera_hw_port_info_get(port, &port->dev_id, &port->hw_id,
62662306a36Sopenharmony_ci					&port->fp_id);
62762306a36Sopenharmony_ci	if (err) {
62862306a36Sopenharmony_ci		dev_err(prestera_dev(sw), "Failed to get port(%u) info\n", id);
62962306a36Sopenharmony_ci		goto err_port_info_get;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	err = prestera_devlink_port_register(port);
63362306a36Sopenharmony_ci	if (err)
63462306a36Sopenharmony_ci		goto err_dl_port_register;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_HW_TC;
63762306a36Sopenharmony_ci	dev->netdev_ops = &prestera_netdev_ops;
63862306a36Sopenharmony_ci	dev->ethtool_ops = &prestera_ethtool_ops;
63962306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, sw->dev->dev);
64062306a36Sopenharmony_ci	SET_NETDEV_DEVLINK_PORT(dev, &port->dl_port);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP)
64362306a36Sopenharmony_ci		netif_carrier_off(dev);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	dev->mtu = min_t(unsigned int, sw->mtu_max, PRESTERA_MTU_DEFAULT);
64662306a36Sopenharmony_ci	dev->min_mtu = sw->mtu_min;
64762306a36Sopenharmony_ci	dev->max_mtu = sw->mtu_max;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	err = prestera_hw_port_mtu_set(port, dev->mtu);
65062306a36Sopenharmony_ci	if (err) {
65162306a36Sopenharmony_ci		dev_err(prestera_dev(sw), "Failed to set port(%u) mtu(%d)\n",
65262306a36Sopenharmony_ci			id, dev->mtu);
65362306a36Sopenharmony_ci		goto err_port_init;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (port->fp_id >= PRESTERA_MAC_ADDR_NUM_MAX) {
65762306a36Sopenharmony_ci		err = -EINVAL;
65862306a36Sopenharmony_ci		goto err_port_init;
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	eth_hw_addr_gen(dev, sw->base_mac, port->fp_id);
66262306a36Sopenharmony_ci	/* firmware requires that port's MAC address consist of the first
66362306a36Sopenharmony_ci	 * 5 bytes of the base MAC address
66462306a36Sopenharmony_ci	 */
66562306a36Sopenharmony_ci	if (memcmp(dev->dev_addr, sw->base_mac, ETH_ALEN - 1)) {
66662306a36Sopenharmony_ci		dev_warn(prestera_dev(sw), "Port MAC address wraps for port(%u)\n", id);
66762306a36Sopenharmony_ci		dev_addr_mod(dev, 0, sw->base_mac, ETH_ALEN - 1);
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	err = prestera_hw_port_mac_set(port, dev->dev_addr);
67162306a36Sopenharmony_ci	if (err) {
67262306a36Sopenharmony_ci		dev_err(prestera_dev(sw), "Failed to set port(%u) mac addr\n", id);
67362306a36Sopenharmony_ci		goto err_port_init;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	err = prestera_hw_port_cap_get(port, &port->caps);
67762306a36Sopenharmony_ci	if (err) {
67862306a36Sopenharmony_ci		dev_err(prestera_dev(sw), "Failed to get port(%u) caps\n", id);
67962306a36Sopenharmony_ci		goto err_port_init;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	port->adver_link_modes = port->caps.supp_link_modes;
68362306a36Sopenharmony_ci	port->adver_fec = 0;
68462306a36Sopenharmony_ci	port->autoneg = true;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* initialize config mac */
68762306a36Sopenharmony_ci	if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP) {
68862306a36Sopenharmony_ci		cfg_mac.admin = true;
68962306a36Sopenharmony_ci		cfg_mac.mode = PRESTERA_MAC_MODE_INTERNAL;
69062306a36Sopenharmony_ci	} else {
69162306a36Sopenharmony_ci		cfg_mac.admin = false;
69262306a36Sopenharmony_ci		cfg_mac.mode = PRESTERA_MAC_MODE_MAX;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci	cfg_mac.inband = 0;
69562306a36Sopenharmony_ci	cfg_mac.speed = 0;
69662306a36Sopenharmony_ci	cfg_mac.duplex = DUPLEX_UNKNOWN;
69762306a36Sopenharmony_ci	cfg_mac.fec = PRESTERA_PORT_FEC_OFF;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	err = prestera_port_cfg_mac_write(port, &cfg_mac);
70062306a36Sopenharmony_ci	if (err) {
70162306a36Sopenharmony_ci		dev_err(prestera_dev(sw),
70262306a36Sopenharmony_ci			"Failed to set port(%u) mac mode\n", id);
70362306a36Sopenharmony_ci		goto err_port_init;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	/* initialize config phy (if this is inegral) */
70762306a36Sopenharmony_ci	if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP) {
70862306a36Sopenharmony_ci		port->cfg_phy.mdix = ETH_TP_MDI_AUTO;
70962306a36Sopenharmony_ci		port->cfg_phy.admin = false;
71062306a36Sopenharmony_ci		err = prestera_hw_port_phy_mode_set(port,
71162306a36Sopenharmony_ci						    port->cfg_phy.admin,
71262306a36Sopenharmony_ci						    false, 0, 0,
71362306a36Sopenharmony_ci						    port->cfg_phy.mdix);
71462306a36Sopenharmony_ci		if (err) {
71562306a36Sopenharmony_ci			dev_err(prestera_dev(sw),
71662306a36Sopenharmony_ci				"Failed to set port(%u) phy mode\n", id);
71762306a36Sopenharmony_ci			goto err_port_init;
71862306a36Sopenharmony_ci		}
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	err = prestera_rxtx_port_init(port);
72262306a36Sopenharmony_ci	if (err)
72362306a36Sopenharmony_ci		goto err_port_init;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	INIT_DELAYED_WORK(&port->cached_hw_stats.caching_dw,
72662306a36Sopenharmony_ci			  &prestera_port_stats_update);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	prestera_port_list_add(port);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	err = register_netdev(dev);
73162306a36Sopenharmony_ci	if (err)
73262306a36Sopenharmony_ci		goto err_register_netdev;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	err = prestera_port_sfp_bind(port);
73562306a36Sopenharmony_ci	if (err)
73662306a36Sopenharmony_ci		goto err_sfp_bind;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	return 0;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cierr_sfp_bind:
74162306a36Sopenharmony_ci	unregister_netdev(dev);
74262306a36Sopenharmony_cierr_register_netdev:
74362306a36Sopenharmony_ci	prestera_port_list_del(port);
74462306a36Sopenharmony_cierr_port_init:
74562306a36Sopenharmony_ci	prestera_devlink_port_unregister(port);
74662306a36Sopenharmony_cierr_dl_port_register:
74762306a36Sopenharmony_cierr_port_info_get:
74862306a36Sopenharmony_ci	free_netdev(dev);
74962306a36Sopenharmony_ci	return err;
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cistatic void prestera_port_destroy(struct prestera_port *port)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	struct net_device *dev = port->dev;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	cancel_delayed_work_sync(&port->cached_hw_stats.caching_dw);
75762306a36Sopenharmony_ci	unregister_netdev(dev);
75862306a36Sopenharmony_ci	prestera_port_list_del(port);
75962306a36Sopenharmony_ci	prestera_devlink_port_unregister(port);
76062306a36Sopenharmony_ci	free_netdev(dev);
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_cistatic void prestera_destroy_ports(struct prestera_switch *sw)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct prestera_port *port, *tmp;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	list_for_each_entry_safe(port, tmp, &sw->port_list, list)
76862306a36Sopenharmony_ci		prestera_port_destroy(port);
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic int prestera_create_ports(struct prestera_switch *sw)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	struct prestera_port *port, *tmp;
77462306a36Sopenharmony_ci	u32 port_idx;
77562306a36Sopenharmony_ci	int err;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	for (port_idx = 0; port_idx < sw->port_count; port_idx++) {
77862306a36Sopenharmony_ci		err = prestera_port_create(sw, port_idx);
77962306a36Sopenharmony_ci		if (err)
78062306a36Sopenharmony_ci			goto err_port_create;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	return 0;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cierr_port_create:
78662306a36Sopenharmony_ci	list_for_each_entry_safe(port, tmp, &sw->port_list, list) {
78762306a36Sopenharmony_ci		prestera_port_sfp_unbind(port);
78862306a36Sopenharmony_ci		prestera_port_destroy(port);
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	return err;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic void prestera_port_handle_event(struct prestera_switch *sw,
79562306a36Sopenharmony_ci				       struct prestera_event *evt, void *arg)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	struct prestera_port_mac_state smac;
79862306a36Sopenharmony_ci	struct prestera_port_event *pevt;
79962306a36Sopenharmony_ci	struct delayed_work *caching_dw;
80062306a36Sopenharmony_ci	struct prestera_port *port;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	if (evt->id == PRESTERA_PORT_EVENT_MAC_STATE_CHANGED) {
80362306a36Sopenharmony_ci		pevt = &evt->port_evt;
80462306a36Sopenharmony_ci		port = prestera_find_port(sw, pevt->port_id);
80562306a36Sopenharmony_ci		if (!port || !port->dev)
80662306a36Sopenharmony_ci			return;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci		caching_dw = &port->cached_hw_stats.caching_dw;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		memset(&smac, 0, sizeof(smac));
81162306a36Sopenharmony_ci		smac.valid = true;
81262306a36Sopenharmony_ci		smac.oper = pevt->data.mac.oper;
81362306a36Sopenharmony_ci		if (smac.oper) {
81462306a36Sopenharmony_ci			smac.mode = pevt->data.mac.mode;
81562306a36Sopenharmony_ci			smac.speed = pevt->data.mac.speed;
81662306a36Sopenharmony_ci			smac.duplex = pevt->data.mac.duplex;
81762306a36Sopenharmony_ci			smac.fc = pevt->data.mac.fc;
81862306a36Sopenharmony_ci			smac.fec = pevt->data.mac.fec;
81962306a36Sopenharmony_ci		}
82062306a36Sopenharmony_ci		prestera_port_mac_state_cache_write(port, &smac);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci		if (port->state_mac.oper) {
82362306a36Sopenharmony_ci			if (port->phy_link)
82462306a36Sopenharmony_ci				phylink_mac_change(port->phy_link, true);
82562306a36Sopenharmony_ci			else
82662306a36Sopenharmony_ci				netif_carrier_on(port->dev);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci			if (!delayed_work_pending(caching_dw))
82962306a36Sopenharmony_ci				queue_delayed_work(prestera_wq, caching_dw, 0);
83062306a36Sopenharmony_ci		} else {
83162306a36Sopenharmony_ci			if (port->phy_link)
83262306a36Sopenharmony_ci				phylink_mac_change(port->phy_link, false);
83362306a36Sopenharmony_ci			else if (netif_running(port->dev) && netif_carrier_ok(port->dev))
83462306a36Sopenharmony_ci				netif_carrier_off(port->dev);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci			if (delayed_work_pending(caching_dw))
83762306a36Sopenharmony_ci				cancel_delayed_work(caching_dw);
83862306a36Sopenharmony_ci		}
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic int prestera_event_handlers_register(struct prestera_switch *sw)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	return prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_PORT,
84562306a36Sopenharmony_ci						  prestera_port_handle_event,
84662306a36Sopenharmony_ci						  NULL);
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic void prestera_event_handlers_unregister(struct prestera_switch *sw)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_PORT,
85262306a36Sopenharmony_ci					     prestera_port_handle_event);
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic int prestera_switch_set_base_mac_addr(struct prestera_switch *sw)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	int ret;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (sw->np)
86062306a36Sopenharmony_ci		ret = of_get_mac_address(sw->np, sw->base_mac);
86162306a36Sopenharmony_ci	if (!is_valid_ether_addr(sw->base_mac) || ret) {
86262306a36Sopenharmony_ci		eth_random_addr(sw->base_mac);
86362306a36Sopenharmony_ci		dev_info(prestera_dev(sw), "using random base mac address\n");
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	return prestera_hw_switch_mac_set(sw, sw->base_mac);
86762306a36Sopenharmony_ci}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistruct prestera_lag *prestera_lag_by_id(struct prestera_switch *sw, u16 id)
87062306a36Sopenharmony_ci{
87162306a36Sopenharmony_ci	return id < sw->lag_max ? &sw->lags[id] : NULL;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic struct prestera_lag *prestera_lag_by_dev(struct prestera_switch *sw,
87562306a36Sopenharmony_ci						struct net_device *dev)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	struct prestera_lag *lag;
87862306a36Sopenharmony_ci	u16 id;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	for (id = 0; id < sw->lag_max; id++) {
88162306a36Sopenharmony_ci		lag = &sw->lags[id];
88262306a36Sopenharmony_ci		if (lag->dev == dev)
88362306a36Sopenharmony_ci			return lag;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	return NULL;
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ciint prestera_lag_id(struct prestera_switch *sw,
89062306a36Sopenharmony_ci		    struct net_device *lag_dev, u16 *lag_id)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct prestera_lag *lag;
89362306a36Sopenharmony_ci	int free_id = -1;
89462306a36Sopenharmony_ci	int id;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	for (id = 0; id < sw->lag_max; id++) {
89762306a36Sopenharmony_ci		lag = prestera_lag_by_id(sw, id);
89862306a36Sopenharmony_ci		if (lag->member_count) {
89962306a36Sopenharmony_ci			if (lag->dev == lag_dev) {
90062306a36Sopenharmony_ci				*lag_id = id;
90162306a36Sopenharmony_ci				return 0;
90262306a36Sopenharmony_ci			}
90362306a36Sopenharmony_ci		} else if (free_id < 0) {
90462306a36Sopenharmony_ci			free_id = id;
90562306a36Sopenharmony_ci		}
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci	if (free_id < 0)
90862306a36Sopenharmony_ci		return -ENOSPC;
90962306a36Sopenharmony_ci	*lag_id = free_id;
91062306a36Sopenharmony_ci	return 0;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic struct prestera_lag *prestera_lag_create(struct prestera_switch *sw,
91462306a36Sopenharmony_ci						struct net_device *lag_dev)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct prestera_lag *lag = NULL;
91762306a36Sopenharmony_ci	u16 id;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	for (id = 0; id < sw->lag_max; id++) {
92062306a36Sopenharmony_ci		lag = &sw->lags[id];
92162306a36Sopenharmony_ci		if (!lag->dev)
92262306a36Sopenharmony_ci			break;
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci	if (lag) {
92562306a36Sopenharmony_ci		INIT_LIST_HEAD(&lag->members);
92662306a36Sopenharmony_ci		lag->dev = lag_dev;
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	return lag;
93062306a36Sopenharmony_ci}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_cistatic void prestera_lag_destroy(struct prestera_switch *sw,
93362306a36Sopenharmony_ci				 struct prestera_lag *lag)
93462306a36Sopenharmony_ci{
93562306a36Sopenharmony_ci	WARN_ON(!list_empty(&lag->members));
93662306a36Sopenharmony_ci	lag->member_count = 0;
93762306a36Sopenharmony_ci	lag->dev = NULL;
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic int prestera_lag_port_add(struct prestera_port *port,
94162306a36Sopenharmony_ci				 struct net_device *lag_dev)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	struct prestera_switch *sw = port->sw;
94462306a36Sopenharmony_ci	struct prestera_lag *lag;
94562306a36Sopenharmony_ci	int err;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	lag = prestera_lag_by_dev(sw, lag_dev);
94862306a36Sopenharmony_ci	if (!lag) {
94962306a36Sopenharmony_ci		lag = prestera_lag_create(sw, lag_dev);
95062306a36Sopenharmony_ci		if (!lag)
95162306a36Sopenharmony_ci			return -ENOSPC;
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	if (lag->member_count >= sw->lag_member_max)
95562306a36Sopenharmony_ci		return -ENOSPC;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	err = prestera_hw_lag_member_add(port, lag->lag_id);
95862306a36Sopenharmony_ci	if (err) {
95962306a36Sopenharmony_ci		if (!lag->member_count)
96062306a36Sopenharmony_ci			prestera_lag_destroy(sw, lag);
96162306a36Sopenharmony_ci		return err;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	list_add(&port->lag_member, &lag->members);
96562306a36Sopenharmony_ci	lag->member_count++;
96662306a36Sopenharmony_ci	port->lag = lag;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	return 0;
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cistatic int prestera_lag_port_del(struct prestera_port *port)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	struct prestera_switch *sw = port->sw;
97462306a36Sopenharmony_ci	struct prestera_lag *lag = port->lag;
97562306a36Sopenharmony_ci	int err;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (!lag || !lag->member_count)
97862306a36Sopenharmony_ci		return -EINVAL;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	err = prestera_hw_lag_member_del(port, lag->lag_id);
98162306a36Sopenharmony_ci	if (err)
98262306a36Sopenharmony_ci		return err;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	list_del(&port->lag_member);
98562306a36Sopenharmony_ci	lag->member_count--;
98662306a36Sopenharmony_ci	port->lag = NULL;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	if (netif_is_bridge_port(lag->dev)) {
98962306a36Sopenharmony_ci		struct net_device *br_dev;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		br_dev = netdev_master_upper_dev_get(lag->dev);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci		prestera_bridge_port_leave(br_dev, port);
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (!lag->member_count)
99762306a36Sopenharmony_ci		prestera_lag_destroy(sw, lag);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	return 0;
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_cibool prestera_port_is_lag_member(const struct prestera_port *port)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	return !!port->lag;
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ciu16 prestera_port_lag_id(const struct prestera_port *port)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	return port->lag->lag_id;
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cistatic int prestera_lag_init(struct prestera_switch *sw)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	u16 id;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	sw->lags = kcalloc(sw->lag_max, sizeof(*sw->lags), GFP_KERNEL);
101762306a36Sopenharmony_ci	if (!sw->lags)
101862306a36Sopenharmony_ci		return -ENOMEM;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	for (id = 0; id < sw->lag_max; id++)
102162306a36Sopenharmony_ci		sw->lags[id].lag_id = id;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	return 0;
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_cistatic void prestera_lag_fini(struct prestera_switch *sw)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	u8 idx;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	for (idx = 0; idx < sw->lag_max; idx++)
103162306a36Sopenharmony_ci		WARN_ON(sw->lags[idx].member_count);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	kfree(sw->lags);
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_cibool prestera_netdev_check(const struct net_device *dev)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	return dev->netdev_ops == &prestera_netdev_ops;
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistatic int prestera_lower_dev_walk(struct net_device *dev,
104262306a36Sopenharmony_ci				   struct netdev_nested_priv *priv)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci	struct prestera_port **pport = (struct prestera_port **)priv->data;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	if (prestera_netdev_check(dev)) {
104762306a36Sopenharmony_ci		*pport = netdev_priv(dev);
104862306a36Sopenharmony_ci		return 1;
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	return 0;
105262306a36Sopenharmony_ci}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_cistruct prestera_port *prestera_port_dev_lower_find(struct net_device *dev)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	struct prestera_port *port = NULL;
105762306a36Sopenharmony_ci	struct netdev_nested_priv priv = {
105862306a36Sopenharmony_ci		.data = (void *)&port,
105962306a36Sopenharmony_ci	};
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if (prestera_netdev_check(dev))
106262306a36Sopenharmony_ci		return netdev_priv(dev);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	netdev_walk_all_lower_dev(dev, prestera_lower_dev_walk, &priv);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	return port;
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cistatic int prestera_netdev_port_lower_event(struct net_device *dev,
107062306a36Sopenharmony_ci					    unsigned long event, void *ptr)
107162306a36Sopenharmony_ci{
107262306a36Sopenharmony_ci	struct netdev_notifier_changelowerstate_info *info = ptr;
107362306a36Sopenharmony_ci	struct netdev_lag_lower_state_info *lower_state_info;
107462306a36Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
107562306a36Sopenharmony_ci	bool enabled;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	if (!netif_is_lag_port(dev))
107862306a36Sopenharmony_ci		return 0;
107962306a36Sopenharmony_ci	if (!prestera_port_is_lag_member(port))
108062306a36Sopenharmony_ci		return 0;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	lower_state_info = info->lower_state_info;
108362306a36Sopenharmony_ci	enabled = lower_state_info->link_up && lower_state_info->tx_enabled;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	return prestera_hw_lag_member_enable(port, port->lag->lag_id, enabled);
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_cistatic bool prestera_lag_master_check(struct net_device *lag_dev,
108962306a36Sopenharmony_ci				      struct netdev_lag_upper_info *info,
109062306a36Sopenharmony_ci				      struct netlink_ext_ack *ext_ack)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
109362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(ext_ack, "Unsupported LAG Tx type");
109462306a36Sopenharmony_ci		return false;
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	return true;
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_cistatic int prestera_netdev_port_event(struct net_device *lower,
110162306a36Sopenharmony_ci				      struct net_device *dev,
110262306a36Sopenharmony_ci				      unsigned long event, void *ptr)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	struct netdev_notifier_info *info = ptr;
110562306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *cu_info;
110662306a36Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
110762306a36Sopenharmony_ci	struct netlink_ext_ack *extack;
110862306a36Sopenharmony_ci	struct net_device *upper;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	extack = netdev_notifier_info_to_extack(info);
111162306a36Sopenharmony_ci	cu_info = container_of(info,
111262306a36Sopenharmony_ci			       struct netdev_notifier_changeupper_info,
111362306a36Sopenharmony_ci			       info);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	switch (event) {
111662306a36Sopenharmony_ci	case NETDEV_PRECHANGEUPPER:
111762306a36Sopenharmony_ci		upper = cu_info->upper_dev;
111862306a36Sopenharmony_ci		if (!netif_is_bridge_master(upper) &&
111962306a36Sopenharmony_ci		    !netif_is_lag_master(upper)) {
112062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
112162306a36Sopenharmony_ci			return -EINVAL;
112262306a36Sopenharmony_ci		}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci		if (!cu_info->linking)
112562306a36Sopenharmony_ci			break;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci		if (netdev_has_any_upper_dev(upper)) {
112862306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Upper device is already enslaved");
112962306a36Sopenharmony_ci			return -EINVAL;
113062306a36Sopenharmony_ci		}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci		if (netif_is_lag_master(upper) &&
113362306a36Sopenharmony_ci		    !prestera_lag_master_check(upper, cu_info->upper_info, extack))
113462306a36Sopenharmony_ci			return -EOPNOTSUPP;
113562306a36Sopenharmony_ci		if (netif_is_lag_master(upper) && vlan_uses_dev(dev)) {
113662306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
113762306a36Sopenharmony_ci					   "Master device is a LAG master and port has a VLAN");
113862306a36Sopenharmony_ci			return -EINVAL;
113962306a36Sopenharmony_ci		}
114062306a36Sopenharmony_ci		if (netif_is_lag_port(dev) && is_vlan_dev(upper) &&
114162306a36Sopenharmony_ci		    !netif_is_lag_master(vlan_dev_real_dev(upper))) {
114262306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
114362306a36Sopenharmony_ci					   "Can not put a VLAN on a LAG port");
114462306a36Sopenharmony_ci			return -EINVAL;
114562306a36Sopenharmony_ci		}
114662306a36Sopenharmony_ci		break;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
114962306a36Sopenharmony_ci		upper = cu_info->upper_dev;
115062306a36Sopenharmony_ci		if (netif_is_bridge_master(upper)) {
115162306a36Sopenharmony_ci			if (cu_info->linking)
115262306a36Sopenharmony_ci				return prestera_bridge_port_join(upper, port,
115362306a36Sopenharmony_ci								 extack);
115462306a36Sopenharmony_ci			else
115562306a36Sopenharmony_ci				prestera_bridge_port_leave(upper, port);
115662306a36Sopenharmony_ci		} else if (netif_is_lag_master(upper)) {
115762306a36Sopenharmony_ci			if (cu_info->linking)
115862306a36Sopenharmony_ci				return prestera_lag_port_add(port, upper);
115962306a36Sopenharmony_ci			else
116062306a36Sopenharmony_ci				prestera_lag_port_del(port);
116162306a36Sopenharmony_ci		}
116262306a36Sopenharmony_ci		break;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	case NETDEV_CHANGELOWERSTATE:
116562306a36Sopenharmony_ci		return prestera_netdev_port_lower_event(dev, event, ptr);
116662306a36Sopenharmony_ci	}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	return 0;
116962306a36Sopenharmony_ci}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_cistatic int prestera_netdevice_lag_event(struct net_device *lag_dev,
117262306a36Sopenharmony_ci					unsigned long event, void *ptr)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	struct net_device *dev;
117562306a36Sopenharmony_ci	struct list_head *iter;
117662306a36Sopenharmony_ci	int err;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	netdev_for_each_lower_dev(lag_dev, dev, iter) {
117962306a36Sopenharmony_ci		if (prestera_netdev_check(dev)) {
118062306a36Sopenharmony_ci			err = prestera_netdev_port_event(lag_dev, dev, event,
118162306a36Sopenharmony_ci							 ptr);
118262306a36Sopenharmony_ci			if (err)
118362306a36Sopenharmony_ci				return err;
118462306a36Sopenharmony_ci		}
118562306a36Sopenharmony_ci	}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	return 0;
118862306a36Sopenharmony_ci}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_cistatic int prestera_netdev_event_handler(struct notifier_block *nb,
119162306a36Sopenharmony_ci					 unsigned long event, void *ptr)
119262306a36Sopenharmony_ci{
119362306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
119462306a36Sopenharmony_ci	int err = 0;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	if (prestera_netdev_check(dev))
119762306a36Sopenharmony_ci		err = prestera_netdev_port_event(dev, dev, event, ptr);
119862306a36Sopenharmony_ci	else if (netif_is_lag_master(dev))
119962306a36Sopenharmony_ci		err = prestera_netdevice_lag_event(dev, event, ptr);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	return notifier_from_errno(err);
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_cistruct prestera_mdb_entry *
120562306a36Sopenharmony_ciprestera_mdb_entry_create(struct prestera_switch *sw,
120662306a36Sopenharmony_ci			  const unsigned char *addr, u16 vid)
120762306a36Sopenharmony_ci{
120862306a36Sopenharmony_ci	struct prestera_flood_domain *flood_domain;
120962306a36Sopenharmony_ci	struct prestera_mdb_entry *mdb_entry;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
121262306a36Sopenharmony_ci	if (!mdb_entry)
121362306a36Sopenharmony_ci		goto err_mdb_alloc;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	flood_domain = prestera_flood_domain_create(sw);
121662306a36Sopenharmony_ci	if (!flood_domain)
121762306a36Sopenharmony_ci		goto err_flood_domain_create;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	mdb_entry->sw = sw;
122062306a36Sopenharmony_ci	mdb_entry->vid = vid;
122162306a36Sopenharmony_ci	mdb_entry->flood_domain = flood_domain;
122262306a36Sopenharmony_ci	ether_addr_copy(mdb_entry->addr, addr);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (prestera_hw_mdb_create(mdb_entry))
122562306a36Sopenharmony_ci		goto err_mdb_hw_create;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	return mdb_entry;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_cierr_mdb_hw_create:
123062306a36Sopenharmony_ci	prestera_flood_domain_destroy(flood_domain);
123162306a36Sopenharmony_cierr_flood_domain_create:
123262306a36Sopenharmony_ci	kfree(mdb_entry);
123362306a36Sopenharmony_cierr_mdb_alloc:
123462306a36Sopenharmony_ci	return NULL;
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_civoid prestera_mdb_entry_destroy(struct prestera_mdb_entry *mdb_entry)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	prestera_hw_mdb_destroy(mdb_entry);
124062306a36Sopenharmony_ci	prestera_flood_domain_destroy(mdb_entry->flood_domain);
124162306a36Sopenharmony_ci	kfree(mdb_entry);
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistruct prestera_flood_domain *
124562306a36Sopenharmony_ciprestera_flood_domain_create(struct prestera_switch *sw)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci	struct prestera_flood_domain *domain;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	domain = kzalloc(sizeof(*domain), GFP_KERNEL);
125062306a36Sopenharmony_ci	if (!domain)
125162306a36Sopenharmony_ci		return NULL;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	domain->sw = sw;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	if (prestera_hw_flood_domain_create(domain)) {
125662306a36Sopenharmony_ci		kfree(domain);
125762306a36Sopenharmony_ci		return NULL;
125862306a36Sopenharmony_ci	}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	INIT_LIST_HEAD(&domain->flood_domain_port_list);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	return domain;
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_civoid prestera_flood_domain_destroy(struct prestera_flood_domain *flood_domain)
126662306a36Sopenharmony_ci{
126762306a36Sopenharmony_ci	WARN_ON(!list_empty(&flood_domain->flood_domain_port_list));
126862306a36Sopenharmony_ci	WARN_ON_ONCE(prestera_hw_flood_domain_destroy(flood_domain));
126962306a36Sopenharmony_ci	kfree(flood_domain);
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ciint
127362306a36Sopenharmony_ciprestera_flood_domain_port_create(struct prestera_flood_domain *flood_domain,
127462306a36Sopenharmony_ci				  struct net_device *dev,
127562306a36Sopenharmony_ci				  u16 vid)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	struct prestera_flood_domain_port *flood_domain_port;
127862306a36Sopenharmony_ci	bool is_first_port_in_list = false;
127962306a36Sopenharmony_ci	int err;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	flood_domain_port = kzalloc(sizeof(*flood_domain_port), GFP_KERNEL);
128262306a36Sopenharmony_ci	if (!flood_domain_port) {
128362306a36Sopenharmony_ci		err = -ENOMEM;
128462306a36Sopenharmony_ci		goto err_port_alloc;
128562306a36Sopenharmony_ci	}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	flood_domain_port->vid = vid;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	if (list_empty(&flood_domain->flood_domain_port_list))
129062306a36Sopenharmony_ci		is_first_port_in_list = true;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	list_add(&flood_domain_port->flood_domain_port_node,
129362306a36Sopenharmony_ci		 &flood_domain->flood_domain_port_list);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	flood_domain_port->flood_domain = flood_domain;
129662306a36Sopenharmony_ci	flood_domain_port->dev = dev;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	if (!is_first_port_in_list) {
129962306a36Sopenharmony_ci		err = prestera_hw_flood_domain_ports_reset(flood_domain);
130062306a36Sopenharmony_ci		if (err)
130162306a36Sopenharmony_ci			goto err_prestera_mdb_port_create_hw;
130262306a36Sopenharmony_ci	}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	err = prestera_hw_flood_domain_ports_set(flood_domain);
130562306a36Sopenharmony_ci	if (err)
130662306a36Sopenharmony_ci		goto err_prestera_mdb_port_create_hw;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	return 0;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_cierr_prestera_mdb_port_create_hw:
131162306a36Sopenharmony_ci	list_del(&flood_domain_port->flood_domain_port_node);
131262306a36Sopenharmony_ci	kfree(flood_domain_port);
131362306a36Sopenharmony_cierr_port_alloc:
131462306a36Sopenharmony_ci	return err;
131562306a36Sopenharmony_ci}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_civoid
131862306a36Sopenharmony_ciprestera_flood_domain_port_destroy(struct prestera_flood_domain_port *port)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	struct prestera_flood_domain *flood_domain = port->flood_domain;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	list_del(&port->flood_domain_port_node);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	WARN_ON_ONCE(prestera_hw_flood_domain_ports_reset(flood_domain));
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	if (!list_empty(&flood_domain->flood_domain_port_list))
132762306a36Sopenharmony_ci		WARN_ON_ONCE(prestera_hw_flood_domain_ports_set(flood_domain));
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	kfree(port);
133062306a36Sopenharmony_ci}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_cistruct prestera_flood_domain_port *
133362306a36Sopenharmony_ciprestera_flood_domain_port_find(struct prestera_flood_domain *flood_domain,
133462306a36Sopenharmony_ci				struct net_device *dev, u16 vid)
133562306a36Sopenharmony_ci{
133662306a36Sopenharmony_ci	struct prestera_flood_domain_port *flood_domain_port;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	list_for_each_entry(flood_domain_port,
133962306a36Sopenharmony_ci			    &flood_domain->flood_domain_port_list,
134062306a36Sopenharmony_ci			    flood_domain_port_node)
134162306a36Sopenharmony_ci		if (flood_domain_port->dev == dev &&
134262306a36Sopenharmony_ci		    vid == flood_domain_port->vid)
134362306a36Sopenharmony_ci			return flood_domain_port;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	return NULL;
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_cistatic int prestera_netdev_event_handler_register(struct prestera_switch *sw)
134962306a36Sopenharmony_ci{
135062306a36Sopenharmony_ci	sw->netdev_nb.notifier_call = prestera_netdev_event_handler;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	return register_netdevice_notifier(&sw->netdev_nb);
135362306a36Sopenharmony_ci}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_cistatic void prestera_netdev_event_handler_unregister(struct prestera_switch *sw)
135662306a36Sopenharmony_ci{
135762306a36Sopenharmony_ci	unregister_netdevice_notifier(&sw->netdev_nb);
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic int prestera_switch_init(struct prestera_switch *sw)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	int err;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	sw->np = sw->dev->dev->of_node;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	err = prestera_hw_switch_init(sw);
136762306a36Sopenharmony_ci	if (err) {
136862306a36Sopenharmony_ci		dev_err(prestera_dev(sw), "Failed to init Switch device\n");
136962306a36Sopenharmony_ci		return err;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	rwlock_init(&sw->port_list_lock);
137362306a36Sopenharmony_ci	INIT_LIST_HEAD(&sw->port_list);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	err = prestera_switch_set_base_mac_addr(sw);
137662306a36Sopenharmony_ci	if (err)
137762306a36Sopenharmony_ci		return err;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	err = prestera_netdev_event_handler_register(sw);
138062306a36Sopenharmony_ci	if (err)
138162306a36Sopenharmony_ci		return err;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	err = prestera_router_init(sw);
138462306a36Sopenharmony_ci	if (err)
138562306a36Sopenharmony_ci		goto err_router_init;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	err = prestera_switchdev_init(sw);
138862306a36Sopenharmony_ci	if (err)
138962306a36Sopenharmony_ci		goto err_swdev_register;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	err = prestera_rxtx_switch_init(sw);
139262306a36Sopenharmony_ci	if (err)
139362306a36Sopenharmony_ci		goto err_rxtx_register;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	err = prestera_event_handlers_register(sw);
139662306a36Sopenharmony_ci	if (err)
139762306a36Sopenharmony_ci		goto err_handlers_register;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	err = prestera_counter_init(sw);
140062306a36Sopenharmony_ci	if (err)
140162306a36Sopenharmony_ci		goto err_counter_init;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	err = prestera_acl_init(sw);
140462306a36Sopenharmony_ci	if (err)
140562306a36Sopenharmony_ci		goto err_acl_init;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	err = prestera_span_init(sw);
140862306a36Sopenharmony_ci	if (err)
140962306a36Sopenharmony_ci		goto err_span_init;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	err = prestera_devlink_traps_register(sw);
141262306a36Sopenharmony_ci	if (err)
141362306a36Sopenharmony_ci		goto err_dl_register;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	err = prestera_lag_init(sw);
141662306a36Sopenharmony_ci	if (err)
141762306a36Sopenharmony_ci		goto err_lag_init;
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	err = prestera_create_ports(sw);
142062306a36Sopenharmony_ci	if (err)
142162306a36Sopenharmony_ci		goto err_ports_create;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	prestera_devlink_register(sw);
142462306a36Sopenharmony_ci	return 0;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_cierr_ports_create:
142762306a36Sopenharmony_ci	prestera_lag_fini(sw);
142862306a36Sopenharmony_cierr_lag_init:
142962306a36Sopenharmony_ci	prestera_devlink_traps_unregister(sw);
143062306a36Sopenharmony_cierr_dl_register:
143162306a36Sopenharmony_ci	prestera_span_fini(sw);
143262306a36Sopenharmony_cierr_span_init:
143362306a36Sopenharmony_ci	prestera_acl_fini(sw);
143462306a36Sopenharmony_cierr_acl_init:
143562306a36Sopenharmony_ci	prestera_counter_fini(sw);
143662306a36Sopenharmony_cierr_counter_init:
143762306a36Sopenharmony_ci	prestera_event_handlers_unregister(sw);
143862306a36Sopenharmony_cierr_handlers_register:
143962306a36Sopenharmony_ci	prestera_rxtx_switch_fini(sw);
144062306a36Sopenharmony_cierr_rxtx_register:
144162306a36Sopenharmony_ci	prestera_switchdev_fini(sw);
144262306a36Sopenharmony_cierr_swdev_register:
144362306a36Sopenharmony_ci	prestera_router_fini(sw);
144462306a36Sopenharmony_cierr_router_init:
144562306a36Sopenharmony_ci	prestera_netdev_event_handler_unregister(sw);
144662306a36Sopenharmony_ci	prestera_hw_switch_fini(sw);
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	return err;
144962306a36Sopenharmony_ci}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_cistatic void prestera_switch_fini(struct prestera_switch *sw)
145262306a36Sopenharmony_ci{
145362306a36Sopenharmony_ci	prestera_devlink_unregister(sw);
145462306a36Sopenharmony_ci	prestera_destroy_ports(sw);
145562306a36Sopenharmony_ci	prestera_lag_fini(sw);
145662306a36Sopenharmony_ci	prestera_devlink_traps_unregister(sw);
145762306a36Sopenharmony_ci	prestera_span_fini(sw);
145862306a36Sopenharmony_ci	prestera_acl_fini(sw);
145962306a36Sopenharmony_ci	prestera_counter_fini(sw);
146062306a36Sopenharmony_ci	prestera_event_handlers_unregister(sw);
146162306a36Sopenharmony_ci	prestera_rxtx_switch_fini(sw);
146262306a36Sopenharmony_ci	prestera_switchdev_fini(sw);
146362306a36Sopenharmony_ci	prestera_router_fini(sw);
146462306a36Sopenharmony_ci	prestera_netdev_event_handler_unregister(sw);
146562306a36Sopenharmony_ci	prestera_hw_switch_fini(sw);
146662306a36Sopenharmony_ci	of_node_put(sw->np);
146762306a36Sopenharmony_ci}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ciint prestera_device_register(struct prestera_device *dev)
147062306a36Sopenharmony_ci{
147162306a36Sopenharmony_ci	struct prestera_switch *sw;
147262306a36Sopenharmony_ci	int err;
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	sw = prestera_devlink_alloc(dev);
147562306a36Sopenharmony_ci	if (!sw)
147662306a36Sopenharmony_ci		return -ENOMEM;
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	dev->priv = sw;
147962306a36Sopenharmony_ci	sw->dev = dev;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	err = prestera_switch_init(sw);
148262306a36Sopenharmony_ci	if (err) {
148362306a36Sopenharmony_ci		prestera_devlink_free(sw);
148462306a36Sopenharmony_ci		return err;
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	return 0;
148862306a36Sopenharmony_ci}
148962306a36Sopenharmony_ciEXPORT_SYMBOL(prestera_device_register);
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_civoid prestera_device_unregister(struct prestera_device *dev)
149262306a36Sopenharmony_ci{
149362306a36Sopenharmony_ci	struct prestera_switch *sw = dev->priv;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	prestera_switch_fini(sw);
149662306a36Sopenharmony_ci	prestera_devlink_free(sw);
149762306a36Sopenharmony_ci}
149862306a36Sopenharmony_ciEXPORT_SYMBOL(prestera_device_unregister);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_cistatic int __init prestera_module_init(void)
150162306a36Sopenharmony_ci{
150262306a36Sopenharmony_ci	prestera_wq = alloc_workqueue("prestera", 0, 0);
150362306a36Sopenharmony_ci	if (!prestera_wq)
150462306a36Sopenharmony_ci		return -ENOMEM;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	prestera_owq = alloc_ordered_workqueue("prestera_ordered", 0);
150762306a36Sopenharmony_ci	if (!prestera_owq) {
150862306a36Sopenharmony_ci		destroy_workqueue(prestera_wq);
150962306a36Sopenharmony_ci		return -ENOMEM;
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	return 0;
151362306a36Sopenharmony_ci}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_cistatic void __exit prestera_module_exit(void)
151662306a36Sopenharmony_ci{
151762306a36Sopenharmony_ci	destroy_workqueue(prestera_wq);
151862306a36Sopenharmony_ci	destroy_workqueue(prestera_owq);
151962306a36Sopenharmony_ci}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_cimodule_init(prestera_module_init);
152262306a36Sopenharmony_cimodule_exit(prestera_module_exit);
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
152562306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell Prestera switch driver");
1526