Commit e1189d9a authored by Vadym Kochan's avatar Vadym Kochan Committed by David S. Miller
Browse files

net: marvell: prestera: Add Switchdev driver implementation



The following features are supported:

    - VLAN-aware bridge offloading
    - VLAN-unaware bridge offloading
    - FDB offloading (learning, ageing)
    - Switchport configuration

Currently there are some limitations like:

    - Only 1 VLAN-aware bridge instance supported
    - FDB ageing timeout parameter is set globally per device

Co-developed-by: default avatarSerhiy Boiko <serhiy.boiko@plvision.eu>
Signed-off-by: default avatarSerhiy Boiko <serhiy.boiko@plvision.eu>
Co-developed-by: default avatarSerhiy Pshyk <serhiy.pshyk@plvision.eu>
Signed-off-by: default avatarSerhiy Pshyk <serhiy.pshyk@plvision.eu>
Co-developed-by: default avatarTaras Chornyi <taras.chornyi@plvision.eu>
Signed-off-by: default avatarTaras Chornyi <taras.chornyi@plvision.eu>
Signed-off-by: default avatarVadym Kochan <vadym.kochan@plvision.eu>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a97d3c69
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PRESTERA)	+= prestera.o
prestera-objs		:= prestera_main.o prestera_hw.o prestera_dsa.o \
			   prestera_rxtx.o prestera_devlink.o prestera_ethtool.o
			   prestera_rxtx.o prestera_devlink.o prestera_ethtool.o \
			   prestera_switchdev.o

obj-$(CONFIG_PRESTERA_PCI)	+= prestera_pci.o
+31 −0
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@

#define PRESTERA_DRV_NAME	"prestera"

#define PRESTERA_DEFAULT_VID    1

struct prestera_fw_rev {
	u16 maj;
	u16 min;
@@ -66,11 +68,13 @@ struct prestera_port {
	u32 hw_id;
	u32 dev_id;
	u16 fp_id;
	u16 pvid;
	bool autoneg;
	u64 adver_link_modes;
	u8 adver_fec;
	struct prestera_port_caps caps;
	struct list_head list;
	struct list_head vlans_list;
	struct {
		struct prestera_port_stats stats;
		struct delayed_work caching_dw;
@@ -100,6 +104,7 @@ enum prestera_event_type {
	PRESTERA_EVENT_TYPE_UNSPEC,

	PRESTERA_EVENT_TYPE_PORT,
	PRESTERA_EVENT_TYPE_FDB,
	PRESTERA_EVENT_TYPE_RXTX,

	PRESTERA_EVENT_TYPE_MAX
@@ -122,19 +127,37 @@ struct prestera_port_event {
	} data;
};

enum prestera_fdb_event_id {
	PRESTERA_FDB_EVENT_UNSPEC,
	PRESTERA_FDB_EVENT_LEARNED,
	PRESTERA_FDB_EVENT_AGED,
};

struct prestera_fdb_event {
	u32 port_id;
	u32 vid;
	union {
		u8 mac[ETH_ALEN];
	} data;
};

struct prestera_event {
	u16 id;
	union {
		struct prestera_port_event port_evt;
		struct prestera_fdb_event fdb_evt;
	};
};

struct prestera_switchdev;
struct prestera_rxtx;

struct prestera_switch {
	struct prestera_device *dev;
	struct prestera_switchdev *swdev;
	struct prestera_rxtx *rxtx;
	struct list_head event_handlers;
	struct notifier_block netdev_nb;
	char base_mac[ETH_ALEN];
	struct list_head port_list;
	rwlock_t port_list_lock;
@@ -172,4 +195,12 @@ struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
int prestera_port_autoneg_set(struct prestera_port *port, bool enable,
			      u64 adver_link_modes, u8 adver_fec);

struct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id);

struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev);

int prestera_port_pvid_set(struct prestera_port *port, u16 vid);

bool prestera_netdev_check(const struct net_device *dev);

#endif /* _PRESTERA_H_ */
+329 −0
Original line number Diff line number Diff line
@@ -20,9 +20,27 @@ enum prestera_cmd_type_t {
	PRESTERA_CMD_TYPE_PORT_ATTR_GET = 0x101,
	PRESTERA_CMD_TYPE_PORT_INFO_GET = 0x110,

	PRESTERA_CMD_TYPE_VLAN_CREATE = 0x200,
	PRESTERA_CMD_TYPE_VLAN_DELETE = 0x201,
	PRESTERA_CMD_TYPE_VLAN_PORT_SET = 0x202,
	PRESTERA_CMD_TYPE_VLAN_PVID_SET = 0x203,

	PRESTERA_CMD_TYPE_FDB_ADD = 0x300,
	PRESTERA_CMD_TYPE_FDB_DELETE = 0x301,
	PRESTERA_CMD_TYPE_FDB_FLUSH_PORT = 0x310,
	PRESTERA_CMD_TYPE_FDB_FLUSH_VLAN = 0x311,
	PRESTERA_CMD_TYPE_FDB_FLUSH_PORT_VLAN = 0x312,

	PRESTERA_CMD_TYPE_BRIDGE_CREATE = 0x400,
	PRESTERA_CMD_TYPE_BRIDGE_DELETE = 0x401,
	PRESTERA_CMD_TYPE_BRIDGE_PORT_ADD = 0x402,
	PRESTERA_CMD_TYPE_BRIDGE_PORT_DELETE = 0x403,

	PRESTERA_CMD_TYPE_RXTX_INIT = 0x800,
	PRESTERA_CMD_TYPE_RXTX_PORT_INIT = 0x801,

	PRESTERA_CMD_TYPE_STP_PORT_SET = 0x1000,

	PRESTERA_CMD_TYPE_ACK = 0x10000,
	PRESTERA_CMD_TYPE_MAX
};
@@ -32,6 +50,9 @@ enum {
	PRESTERA_CMD_PORT_ATTR_MTU = 3,
	PRESTERA_CMD_PORT_ATTR_MAC = 4,
	PRESTERA_CMD_PORT_ATTR_SPEED = 5,
	PRESTERA_CMD_PORT_ATTR_ACCEPT_FRAME_TYPE = 6,
	PRESTERA_CMD_PORT_ATTR_LEARNING = 7,
	PRESTERA_CMD_PORT_ATTR_FLOOD = 8,
	PRESTERA_CMD_PORT_ATTR_CAPABILITY = 9,
	PRESTERA_CMD_PORT_ATTR_REMOTE_CAPABILITY = 10,
	PRESTERA_CMD_PORT_ATTR_REMOTE_FC = 11,
@@ -47,6 +68,7 @@ enum {

enum {
	PRESTERA_CMD_SWITCH_ATTR_MAC = 1,
	PRESTERA_CMD_SWITCH_ATTR_AGEING = 2,
};

enum {
@@ -132,6 +154,7 @@ struct prestera_msg_common_resp {

union prestera_msg_switch_param {
	u8 mac[ETH_ALEN];
	u32 ageing_timeout_ms;
};

struct prestera_msg_switch_attr_req {
@@ -170,7 +193,10 @@ union prestera_msg_port_param {
	u8  oper_state;
	u32 mtu;
	u8  mac[ETH_ALEN];
	u8  accept_frm_type;
	u32 speed;
	u8 learning;
	u8 flood;
	u32 link_mode;
	u8  type;
	u8  duplex;
@@ -211,6 +237,46 @@ struct prestera_msg_port_info_resp {
	u16 fp_id;
};

struct prestera_msg_vlan_req {
	struct prestera_msg_cmd cmd;
	u32 port;
	u32 dev;
	u16 vid;
	u8  is_member;
	u8  is_tagged;
};

struct prestera_msg_fdb_req {
	struct prestera_msg_cmd cmd;
	u8 dest_type;
	u32 port;
	u32 dev;
	u8  mac[ETH_ALEN];
	u16 vid;
	u8  dynamic;
	u32 flush_mode;
};

struct prestera_msg_bridge_req {
	struct prestera_msg_cmd cmd;
	u32 port;
	u32 dev;
	u16 bridge;
};

struct prestera_msg_bridge_resp {
	struct prestera_msg_ret ret;
	u16 bridge;
};

struct prestera_msg_stp_req {
	struct prestera_msg_cmd cmd;
	u32 port;
	u32 dev;
	u16 vid;
	u8  state;
};

struct prestera_msg_rxtx_req {
	struct prestera_msg_cmd cmd;
	u8 use_sdma;
@@ -242,6 +308,18 @@ struct prestera_msg_event_port {
	union prestera_msg_event_port_param param;
};

union prestera_msg_event_fdb_param {
	u8 mac[ETH_ALEN];
};

struct prestera_msg_event_fdb {
	struct prestera_msg_event id;
	u8 dest_type;
	u32 port_id;
	u32 vid;
	union prestera_msg_event_fdb_param param;
};

static int __prestera_cmd_ret(struct prestera_switch *sw,
			      enum prestera_cmd_type_t type,
			      struct prestera_msg_cmd *cmd, size_t clen,
@@ -304,10 +382,23 @@ static int prestera_fw_parse_port_evt(void *msg, struct prestera_event *evt)
	return 0;
}

static int prestera_fw_parse_fdb_evt(void *msg, struct prestera_event *evt)
{
	struct prestera_msg_event_fdb *hw_evt = msg;

	evt->fdb_evt.port_id = hw_evt->port_id;
	evt->fdb_evt.vid = hw_evt->vid;

	ether_addr_copy(evt->fdb_evt.data.mac, hw_evt->param.mac);

	return 0;
}

static struct prestera_fw_evt_parser {
	int (*func)(void *msg, struct prestera_event *evt);
} fw_event_parsers[PRESTERA_EVENT_TYPE_MAX] = {
	[PRESTERA_EVENT_TYPE_PORT] = { .func = prestera_fw_parse_port_evt },
	[PRESTERA_EVENT_TYPE_FDB] = { .func = prestera_fw_parse_fdb_evt },
};

static struct prestera_fw_event_handler *
@@ -449,6 +540,19 @@ void prestera_hw_switch_fini(struct prestera_switch *sw)
	WARN_ON(!list_empty(&sw->event_handlers));
}

int prestera_hw_switch_ageing_set(struct prestera_switch *sw, u32 ageing_ms)
{
	struct prestera_msg_switch_attr_req req = {
		.attr = PRESTERA_CMD_SWITCH_ATTR_AGEING,
		.param = {
			.ageing_timeout_ms = ageing_ms,
		},
	};

	return prestera_cmd(sw, PRESTERA_CMD_TYPE_SWITCH_ATTR_SET,
			    &req.cmd, sizeof(req));
}

int prestera_hw_port_state_set(const struct prestera_port *port,
			       bool admin_state)
{
@@ -494,6 +598,22 @@ int prestera_hw_port_mac_set(const struct prestera_port *port, const char *mac)
			    &req.cmd, sizeof(req));
}

int prestera_hw_port_accept_frm_type(struct prestera_port *port,
				     enum prestera_accept_frm_type type)
{
	struct prestera_msg_port_attr_req req = {
		.attr = PRESTERA_CMD_PORT_ATTR_ACCEPT_FRAME_TYPE,
		.port = port->hw_id,
		.dev = port->dev_id,
		.param = {
			.accept_frm_type = type,
		}
	};

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
			    &req.cmd, sizeof(req));
}

int prestera_hw_port_cap_get(const struct prestera_port *port,
			     struct prestera_port_caps *caps)
{
@@ -853,6 +973,215 @@ int prestera_hw_port_stats_get(const struct prestera_port *port,
	return 0;
}

int prestera_hw_port_learning_set(struct prestera_port *port, bool enable)
{
	struct prestera_msg_port_attr_req req = {
		.attr = PRESTERA_CMD_PORT_ATTR_LEARNING,
		.port = port->hw_id,
		.dev = port->dev_id,
		.param = {
			.learning = enable,
		}
	};

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
			    &req.cmd, sizeof(req));
}

int prestera_hw_port_flood_set(struct prestera_port *port, bool flood)
{
	struct prestera_msg_port_attr_req req = {
		.attr = PRESTERA_CMD_PORT_ATTR_FLOOD,
		.port = port->hw_id,
		.dev = port->dev_id,
		.param = {
			.flood = flood,
		}
	};

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
			    &req.cmd, sizeof(req));
}

int prestera_hw_vlan_create(struct prestera_switch *sw, u16 vid)
{
	struct prestera_msg_vlan_req req = {
		.vid = vid,
	};

	return prestera_cmd(sw, PRESTERA_CMD_TYPE_VLAN_CREATE,
			    &req.cmd, sizeof(req));
}

int prestera_hw_vlan_delete(struct prestera_switch *sw, u16 vid)
{
	struct prestera_msg_vlan_req req = {
		.vid = vid,
	};

	return prestera_cmd(sw, PRESTERA_CMD_TYPE_VLAN_DELETE,
			    &req.cmd, sizeof(req));
}

int prestera_hw_vlan_port_set(struct prestera_port *port, u16 vid,
			      bool is_member, bool untagged)
{
	struct prestera_msg_vlan_req req = {
		.port = port->hw_id,
		.dev = port->dev_id,
		.vid = vid,
		.is_member = is_member,
		.is_tagged = !untagged,
	};

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_VLAN_PORT_SET,
			    &req.cmd, sizeof(req));
}

int prestera_hw_vlan_port_vid_set(struct prestera_port *port, u16 vid)
{
	struct prestera_msg_vlan_req req = {
		.port = port->hw_id,
		.dev = port->dev_id,
		.vid = vid,
	};

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_VLAN_PVID_SET,
			    &req.cmd, sizeof(req));
}

int prestera_hw_vlan_port_stp_set(struct prestera_port *port, u16 vid, u8 state)
{
	struct prestera_msg_stp_req req = {
		.port = port->hw_id,
		.dev = port->dev_id,
		.vid = vid,
		.state = state,
	};

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_STP_PORT_SET,
			    &req.cmd, sizeof(req));
}

int prestera_hw_fdb_add(struct prestera_port *port, const unsigned char *mac,
			u16 vid, bool dynamic)
{
	struct prestera_msg_fdb_req req = {
		.port = port->hw_id,
		.dev = port->dev_id,
		.vid = vid,
		.dynamic = dynamic,
	};

	ether_addr_copy(req.mac, mac);

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_FDB_ADD,
			    &req.cmd, sizeof(req));
}

int prestera_hw_fdb_del(struct prestera_port *port, const unsigned char *mac,
			u16 vid)
{
	struct prestera_msg_fdb_req req = {
		.port = port->hw_id,
		.dev = port->dev_id,
		.vid = vid,
	};

	ether_addr_copy(req.mac, mac);

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_FDB_DELETE,
			    &req.cmd, sizeof(req));
}

int prestera_hw_fdb_flush_port(struct prestera_port *port, u32 mode)
{
	struct prestera_msg_fdb_req req = {
		.port = port->hw_id,
		.dev = port->dev_id,
		.flush_mode = mode,
	};

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_FDB_FLUSH_PORT,
			    &req.cmd, sizeof(req));
}

int prestera_hw_fdb_flush_vlan(struct prestera_switch *sw, u16 vid, u32 mode)
{
	struct prestera_msg_fdb_req req = {
		.vid = vid,
		.flush_mode = mode,
	};

	return prestera_cmd(sw, PRESTERA_CMD_TYPE_FDB_FLUSH_VLAN,
			    &req.cmd, sizeof(req));
}

int prestera_hw_fdb_flush_port_vlan(struct prestera_port *port, u16 vid,
				    u32 mode)
{
	struct prestera_msg_fdb_req req = {
		.port = port->hw_id,
		.dev = port->dev_id,
		.vid = vid,
		.flush_mode = mode,
	};

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_FDB_FLUSH_PORT_VLAN,
			    &req.cmd, sizeof(req));
}

int prestera_hw_bridge_create(struct prestera_switch *sw, u16 *bridge_id)
{
	struct prestera_msg_bridge_resp resp;
	struct prestera_msg_bridge_req req;
	int err;

	err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_BRIDGE_CREATE,
			       &req.cmd, sizeof(req),
			       &resp.ret, sizeof(resp));
	if (err)
		return err;

	*bridge_id = resp.bridge;

	return 0;
}

int prestera_hw_bridge_delete(struct prestera_switch *sw, u16 bridge_id)
{
	struct prestera_msg_bridge_req req = {
		.bridge = bridge_id,
	};

	return prestera_cmd(sw, PRESTERA_CMD_TYPE_BRIDGE_DELETE,
			    &req.cmd, sizeof(req));
}

int prestera_hw_bridge_port_add(struct prestera_port *port, u16 bridge_id)
{
	struct prestera_msg_bridge_req req = {
		.bridge = bridge_id,
		.port = port->hw_id,
		.dev = port->dev_id,
	};

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_BRIDGE_PORT_ADD,
			    &req.cmd, sizeof(req));
}

int prestera_hw_bridge_port_delete(struct prestera_port *port, u16 bridge_id)
{
	struct prestera_msg_bridge_req req = {
		.bridge = bridge_id,
		.port = port->hw_id,
		.dev = port->dev_id,
	};

	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_BRIDGE_PORT_DELETE,
			    &req.cmd, sizeof(req));
}

int prestera_hw_rxtx_init(struct prestera_switch *sw,
			  struct prestera_rxtx_params *params)
{
+48 −0
Original line number Diff line number Diff line
@@ -6,6 +6,19 @@

#include <linux/types.h>

enum prestera_accept_frm_type {
	PRESTERA_ACCEPT_FRAME_TYPE_TAGGED,
	PRESTERA_ACCEPT_FRAME_TYPE_UNTAGGED,
	PRESTERA_ACCEPT_FRAME_TYPE_ALL,
};

enum prestera_fdb_flush_mode {
	PRESTERA_FDB_FLUSH_MODE_DYNAMIC = BIT(0),
	PRESTERA_FDB_FLUSH_MODE_STATIC = BIT(1),
	PRESTERA_FDB_FLUSH_MODE_ALL = PRESTERA_FDB_FLUSH_MODE_DYNAMIC
					| PRESTERA_FDB_FLUSH_MODE_STATIC,
};

enum {
	PRESTERA_LINK_MODE_10baseT_Half,
	PRESTERA_LINK_MODE_10baseT_Full,
@@ -69,6 +82,13 @@ enum {
	PRESTERA_PORT_DUPLEX_FULL,
};

enum {
	PRESTERA_STP_DISABLED,
	PRESTERA_STP_BLOCK_LISTEN,
	PRESTERA_STP_LEARN,
	PRESTERA_STP_FORWARD,
};

struct prestera_switch;
struct prestera_port;
struct prestera_port_stats;
@@ -84,6 +104,7 @@ struct prestera_rxtx_params;
/* Switch API */
int prestera_hw_switch_init(struct prestera_switch *sw);
void prestera_hw_switch_fini(struct prestera_switch *sw);
int prestera_hw_switch_ageing_set(struct prestera_switch *sw, u32 ageing_ms);
int prestera_hw_switch_mac_set(struct prestera_switch *sw, const char *mac);

/* Port API */
@@ -116,6 +137,33 @@ int prestera_hw_port_mdix_get(const struct prestera_port *port, u8 *status,
			      u8 *admin_mode);
int prestera_hw_port_mdix_set(const struct prestera_port *port, u8 mode);
int prestera_hw_port_speed_get(const struct prestera_port *port, u32 *speed);
int prestera_hw_port_learning_set(struct prestera_port *port, bool enable);
int prestera_hw_port_flood_set(struct prestera_port *port, bool flood);
int prestera_hw_port_accept_frm_type(struct prestera_port *port,
				     enum prestera_accept_frm_type type);
/* Vlan API */
int prestera_hw_vlan_create(struct prestera_switch *sw, u16 vid);
int prestera_hw_vlan_delete(struct prestera_switch *sw, u16 vid);
int prestera_hw_vlan_port_set(struct prestera_port *port, u16 vid,
			      bool is_member, bool untagged);
int prestera_hw_vlan_port_vid_set(struct prestera_port *port, u16 vid);
int prestera_hw_vlan_port_stp_set(struct prestera_port *port, u16 vid, u8 state);

/* FDB API */
int prestera_hw_fdb_add(struct prestera_port *port, const unsigned char *mac,
			u16 vid, bool dynamic);
int prestera_hw_fdb_del(struct prestera_port *port, const unsigned char *mac,
			u16 vid);
int prestera_hw_fdb_flush_port(struct prestera_port *port, u32 mode);
int prestera_hw_fdb_flush_vlan(struct prestera_switch *sw, u16 vid, u32 mode);
int prestera_hw_fdb_flush_port_vlan(struct prestera_port *port, u16 vid,
				    u32 mode);

/* Bridge API */
int prestera_hw_bridge_create(struct prestera_switch *sw, u16 *bridge_id);
int prestera_hw_bridge_delete(struct prestera_switch *sw, u16 bridge_id);
int prestera_hw_bridge_port_add(struct prestera_port *port, u16 bridge_id);
int prestera_hw_bridge_port_delete(struct prestera_port *port, u16 bridge_id);

/* Event handlers */
int prestera_hw_event_handler_register(struct prestera_switch *sw,
+107 −3
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include "prestera_rxtx.h"
#include "prestera_devlink.h"
#include "prestera_ethtool.h"
#include "prestera_switchdev.h"

#define PRESTERA_MTU_DEFAULT	1536

@@ -23,6 +24,29 @@

static struct workqueue_struct *prestera_wq;

int prestera_port_pvid_set(struct prestera_port *port, u16 vid)
{
	enum prestera_accept_frm_type frm_type;
	int err;

	frm_type = PRESTERA_ACCEPT_FRAME_TYPE_TAGGED;

	if (vid) {
		err = prestera_hw_vlan_port_vid_set(port, vid);
		if (err)
			return err;

		frm_type = PRESTERA_ACCEPT_FRAME_TYPE_ALL;
	}

	err = prestera_hw_port_accept_frm_type(port, frm_type);
	if (err && frm_type == PRESTERA_ACCEPT_FRAME_TYPE_ALL)
		prestera_hw_vlan_port_vid_set(port, port->pvid);

	port->pvid = vid;
	return 0;
}

struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
						 u32 dev_id, u32 hw_id)
{
@@ -38,8 +62,7 @@ struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
	return port;
}

static struct prestera_port *prestera_find_port(struct prestera_switch *sw,
						u32 id)
struct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id)
{
	struct prestera_port *port = NULL;

@@ -261,6 +284,8 @@ static int prestera_port_create(struct prestera_switch *sw, u32 id)

	port = netdev_priv(dev);

	INIT_LIST_HEAD(&port->vlans_list);
	port->pvid = PRESTERA_DEFAULT_VID;
	port->dev = dev;
	port->id = id;
	port->sw = sw;
@@ -452,6 +477,71 @@ static int prestera_switch_set_base_mac_addr(struct prestera_switch *sw)
	return prestera_hw_switch_mac_set(sw, sw->base_mac);
}

bool prestera_netdev_check(const struct net_device *dev)
{
	return dev->netdev_ops == &prestera_netdev_ops;
}

static int prestera_lower_dev_walk(struct net_device *dev, void *data)
{
	struct prestera_port **pport = data;

	if (prestera_netdev_check(dev)) {
		*pport = netdev_priv(dev);
		return 1;
	}

	return 0;
}

struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev)
{
	struct prestera_port *port = NULL;

	if (prestera_netdev_check(dev))
		return netdev_priv(dev);

	netdev_walk_all_lower_dev(dev, prestera_lower_dev_walk, &port);

	return port;
}

static int prestera_netdev_port_event(struct net_device *dev,
				      unsigned long event, void *ptr)
{
	switch (event) {
	case NETDEV_PRECHANGEUPPER:
	case NETDEV_CHANGEUPPER:
		return prestera_bridge_port_event(dev, event, ptr);
	default:
		return 0;
	}
}

static int prestera_netdev_event_handler(struct notifier_block *nb,
					 unsigned long event, void *ptr)
{
	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
	int err = 0;

	if (prestera_netdev_check(dev))
		err = prestera_netdev_port_event(dev, event, ptr);

	return notifier_from_errno(err);
}

static int prestera_netdev_event_handler_register(struct prestera_switch *sw)
{
	sw->netdev_nb.notifier_call = prestera_netdev_event_handler;

	return register_netdevice_notifier(&sw->netdev_nb);
}

static void prestera_netdev_event_handler_unregister(struct prestera_switch *sw)
{
	unregister_netdevice_notifier(&sw->netdev_nb);
}

static int prestera_switch_init(struct prestera_switch *sw)
{
	int err;
@@ -469,10 +559,18 @@ static int prestera_switch_init(struct prestera_switch *sw)
	if (err)
		return err;

	err = prestera_rxtx_switch_init(sw);
	err = prestera_netdev_event_handler_register(sw);
	if (err)
		return err;

	err = prestera_switchdev_init(sw);
	if (err)
		goto err_swdev_register;

	err = prestera_rxtx_switch_init(sw);
	if (err)
		goto err_rxtx_register;

	err = prestera_event_handlers_register(sw);
	if (err)
		goto err_handlers_register;
@@ -493,6 +591,10 @@ static int prestera_switch_init(struct prestera_switch *sw)
	prestera_event_handlers_unregister(sw);
err_handlers_register:
	prestera_rxtx_switch_fini(sw);
err_rxtx_register:
	prestera_switchdev_fini(sw);
err_swdev_register:
	prestera_netdev_event_handler_unregister(sw);
	prestera_hw_switch_fini(sw);

	return err;
@@ -504,6 +606,8 @@ static void prestera_switch_fini(struct prestera_switch *sw)
	prestera_devlink_unregister(sw);
	prestera_event_handlers_unregister(sw);
	prestera_rxtx_switch_fini(sw);
	prestera_switchdev_fini(sw);
	prestera_netdev_event_handler_unregister(sw);
	prestera_hw_switch_fini(sw);
}

Loading