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: Serhiy Boiko <serhiy.boiko@plvision.eu> Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu> Co-developed-by: Serhiy Pshyk <serhiy.pshyk@plvision.eu> Signed-off-by: Serhiy Pshyk <serhiy.pshyk@plvision.eu> Co-developed-by: Taras Chornyi <taras.chornyi@plvision.eu> Signed-off-by: Taras Chornyi <taras.chornyi@plvision.eu> Signed-off-by: Vadym Kochan <vadym.kochan@plvision.eu> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
a97d3c6939
commit
e1189d9a5f
@ -1,6 +1,7 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
obj-$(CONFIG_PRESTERA) += prestera.o
|
obj-$(CONFIG_PRESTERA) += prestera.o
|
||||||
prestera-objs := prestera_main.o prestera_hw.o prestera_dsa.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
|
obj-$(CONFIG_PRESTERA_PCI) += prestera_pci.o
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
#define PRESTERA_DRV_NAME "prestera"
|
#define PRESTERA_DRV_NAME "prestera"
|
||||||
|
|
||||||
|
#define PRESTERA_DEFAULT_VID 1
|
||||||
|
|
||||||
struct prestera_fw_rev {
|
struct prestera_fw_rev {
|
||||||
u16 maj;
|
u16 maj;
|
||||||
u16 min;
|
u16 min;
|
||||||
@ -66,11 +68,13 @@ struct prestera_port {
|
|||||||
u32 hw_id;
|
u32 hw_id;
|
||||||
u32 dev_id;
|
u32 dev_id;
|
||||||
u16 fp_id;
|
u16 fp_id;
|
||||||
|
u16 pvid;
|
||||||
bool autoneg;
|
bool autoneg;
|
||||||
u64 adver_link_modes;
|
u64 adver_link_modes;
|
||||||
u8 adver_fec;
|
u8 adver_fec;
|
||||||
struct prestera_port_caps caps;
|
struct prestera_port_caps caps;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
struct list_head vlans_list;
|
||||||
struct {
|
struct {
|
||||||
struct prestera_port_stats stats;
|
struct prestera_port_stats stats;
|
||||||
struct delayed_work caching_dw;
|
struct delayed_work caching_dw;
|
||||||
@ -100,6 +104,7 @@ enum prestera_event_type {
|
|||||||
PRESTERA_EVENT_TYPE_UNSPEC,
|
PRESTERA_EVENT_TYPE_UNSPEC,
|
||||||
|
|
||||||
PRESTERA_EVENT_TYPE_PORT,
|
PRESTERA_EVENT_TYPE_PORT,
|
||||||
|
PRESTERA_EVENT_TYPE_FDB,
|
||||||
PRESTERA_EVENT_TYPE_RXTX,
|
PRESTERA_EVENT_TYPE_RXTX,
|
||||||
|
|
||||||
PRESTERA_EVENT_TYPE_MAX
|
PRESTERA_EVENT_TYPE_MAX
|
||||||
@ -122,19 +127,37 @@ struct prestera_port_event {
|
|||||||
} data;
|
} 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 {
|
struct prestera_event {
|
||||||
u16 id;
|
u16 id;
|
||||||
union {
|
union {
|
||||||
struct prestera_port_event port_evt;
|
struct prestera_port_event port_evt;
|
||||||
|
struct prestera_fdb_event fdb_evt;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct prestera_switchdev;
|
||||||
struct prestera_rxtx;
|
struct prestera_rxtx;
|
||||||
|
|
||||||
struct prestera_switch {
|
struct prestera_switch {
|
||||||
struct prestera_device *dev;
|
struct prestera_device *dev;
|
||||||
|
struct prestera_switchdev *swdev;
|
||||||
struct prestera_rxtx *rxtx;
|
struct prestera_rxtx *rxtx;
|
||||||
struct list_head event_handlers;
|
struct list_head event_handlers;
|
||||||
|
struct notifier_block netdev_nb;
|
||||||
char base_mac[ETH_ALEN];
|
char base_mac[ETH_ALEN];
|
||||||
struct list_head port_list;
|
struct list_head port_list;
|
||||||
rwlock_t port_list_lock;
|
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,
|
int prestera_port_autoneg_set(struct prestera_port *port, bool enable,
|
||||||
u64 adver_link_modes, u8 adver_fec);
|
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_ */
|
#endif /* _PRESTERA_H_ */
|
||||||
|
@ -20,9 +20,27 @@ enum prestera_cmd_type_t {
|
|||||||
PRESTERA_CMD_TYPE_PORT_ATTR_GET = 0x101,
|
PRESTERA_CMD_TYPE_PORT_ATTR_GET = 0x101,
|
||||||
PRESTERA_CMD_TYPE_PORT_INFO_GET = 0x110,
|
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_INIT = 0x800,
|
||||||
PRESTERA_CMD_TYPE_RXTX_PORT_INIT = 0x801,
|
PRESTERA_CMD_TYPE_RXTX_PORT_INIT = 0x801,
|
||||||
|
|
||||||
|
PRESTERA_CMD_TYPE_STP_PORT_SET = 0x1000,
|
||||||
|
|
||||||
PRESTERA_CMD_TYPE_ACK = 0x10000,
|
PRESTERA_CMD_TYPE_ACK = 0x10000,
|
||||||
PRESTERA_CMD_TYPE_MAX
|
PRESTERA_CMD_TYPE_MAX
|
||||||
};
|
};
|
||||||
@ -32,6 +50,9 @@ enum {
|
|||||||
PRESTERA_CMD_PORT_ATTR_MTU = 3,
|
PRESTERA_CMD_PORT_ATTR_MTU = 3,
|
||||||
PRESTERA_CMD_PORT_ATTR_MAC = 4,
|
PRESTERA_CMD_PORT_ATTR_MAC = 4,
|
||||||
PRESTERA_CMD_PORT_ATTR_SPEED = 5,
|
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_CAPABILITY = 9,
|
||||||
PRESTERA_CMD_PORT_ATTR_REMOTE_CAPABILITY = 10,
|
PRESTERA_CMD_PORT_ATTR_REMOTE_CAPABILITY = 10,
|
||||||
PRESTERA_CMD_PORT_ATTR_REMOTE_FC = 11,
|
PRESTERA_CMD_PORT_ATTR_REMOTE_FC = 11,
|
||||||
@ -47,6 +68,7 @@ enum {
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
PRESTERA_CMD_SWITCH_ATTR_MAC = 1,
|
PRESTERA_CMD_SWITCH_ATTR_MAC = 1,
|
||||||
|
PRESTERA_CMD_SWITCH_ATTR_AGEING = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -132,6 +154,7 @@ struct prestera_msg_common_resp {
|
|||||||
|
|
||||||
union prestera_msg_switch_param {
|
union prestera_msg_switch_param {
|
||||||
u8 mac[ETH_ALEN];
|
u8 mac[ETH_ALEN];
|
||||||
|
u32 ageing_timeout_ms;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct prestera_msg_switch_attr_req {
|
struct prestera_msg_switch_attr_req {
|
||||||
@ -170,7 +193,10 @@ union prestera_msg_port_param {
|
|||||||
u8 oper_state;
|
u8 oper_state;
|
||||||
u32 mtu;
|
u32 mtu;
|
||||||
u8 mac[ETH_ALEN];
|
u8 mac[ETH_ALEN];
|
||||||
|
u8 accept_frm_type;
|
||||||
u32 speed;
|
u32 speed;
|
||||||
|
u8 learning;
|
||||||
|
u8 flood;
|
||||||
u32 link_mode;
|
u32 link_mode;
|
||||||
u8 type;
|
u8 type;
|
||||||
u8 duplex;
|
u8 duplex;
|
||||||
@ -211,6 +237,46 @@ struct prestera_msg_port_info_resp {
|
|||||||
u16 fp_id;
|
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_rxtx_req {
|
||||||
struct prestera_msg_cmd cmd;
|
struct prestera_msg_cmd cmd;
|
||||||
u8 use_sdma;
|
u8 use_sdma;
|
||||||
@ -242,6 +308,18 @@ struct prestera_msg_event_port {
|
|||||||
union prestera_msg_event_port_param param;
|
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,
|
static int __prestera_cmd_ret(struct prestera_switch *sw,
|
||||||
enum prestera_cmd_type_t type,
|
enum prestera_cmd_type_t type,
|
||||||
struct prestera_msg_cmd *cmd, size_t clen,
|
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;
|
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 {
|
static struct prestera_fw_evt_parser {
|
||||||
int (*func)(void *msg, struct prestera_event *evt);
|
int (*func)(void *msg, struct prestera_event *evt);
|
||||||
} fw_event_parsers[PRESTERA_EVENT_TYPE_MAX] = {
|
} fw_event_parsers[PRESTERA_EVENT_TYPE_MAX] = {
|
||||||
[PRESTERA_EVENT_TYPE_PORT] = { .func = prestera_fw_parse_port_evt },
|
[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 *
|
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));
|
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,
|
int prestera_hw_port_state_set(const struct prestera_port *port,
|
||||||
bool admin_state)
|
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));
|
&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,
|
int prestera_hw_port_cap_get(const struct prestera_port *port,
|
||||||
struct prestera_port_caps *caps)
|
struct prestera_port_caps *caps)
|
||||||
{
|
{
|
||||||
@ -853,6 +973,215 @@ int prestera_hw_port_stats_get(const struct prestera_port *port,
|
|||||||
return 0;
|
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,
|
int prestera_hw_rxtx_init(struct prestera_switch *sw,
|
||||||
struct prestera_rxtx_params *params)
|
struct prestera_rxtx_params *params)
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,19 @@
|
|||||||
|
|
||||||
#include <linux/types.h>
|
#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 {
|
enum {
|
||||||
PRESTERA_LINK_MODE_10baseT_Half,
|
PRESTERA_LINK_MODE_10baseT_Half,
|
||||||
PRESTERA_LINK_MODE_10baseT_Full,
|
PRESTERA_LINK_MODE_10baseT_Full,
|
||||||
@ -69,6 +82,13 @@ enum {
|
|||||||
PRESTERA_PORT_DUPLEX_FULL,
|
PRESTERA_PORT_DUPLEX_FULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PRESTERA_STP_DISABLED,
|
||||||
|
PRESTERA_STP_BLOCK_LISTEN,
|
||||||
|
PRESTERA_STP_LEARN,
|
||||||
|
PRESTERA_STP_FORWARD,
|
||||||
|
};
|
||||||
|
|
||||||
struct prestera_switch;
|
struct prestera_switch;
|
||||||
struct prestera_port;
|
struct prestera_port;
|
||||||
struct prestera_port_stats;
|
struct prestera_port_stats;
|
||||||
@ -84,6 +104,7 @@ struct prestera_rxtx_params;
|
|||||||
/* Switch API */
|
/* Switch API */
|
||||||
int prestera_hw_switch_init(struct prestera_switch *sw);
|
int prestera_hw_switch_init(struct prestera_switch *sw);
|
||||||
void prestera_hw_switch_fini(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);
|
int prestera_hw_switch_mac_set(struct prestera_switch *sw, const char *mac);
|
||||||
|
|
||||||
/* Port API */
|
/* Port API */
|
||||||
@ -116,6 +137,33 @@ int prestera_hw_port_mdix_get(const struct prestera_port *port, u8 *status,
|
|||||||
u8 *admin_mode);
|
u8 *admin_mode);
|
||||||
int prestera_hw_port_mdix_set(const struct prestera_port *port, u8 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_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 */
|
/* Event handlers */
|
||||||
int prestera_hw_event_handler_register(struct prestera_switch *sw,
|
int prestera_hw_event_handler_register(struct prestera_switch *sw,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "prestera_rxtx.h"
|
#include "prestera_rxtx.h"
|
||||||
#include "prestera_devlink.h"
|
#include "prestera_devlink.h"
|
||||||
#include "prestera_ethtool.h"
|
#include "prestera_ethtool.h"
|
||||||
|
#include "prestera_switchdev.h"
|
||||||
|
|
||||||
#define PRESTERA_MTU_DEFAULT 1536
|
#define PRESTERA_MTU_DEFAULT 1536
|
||||||
|
|
||||||
@ -23,6 +24,29 @@
|
|||||||
|
|
||||||
static struct workqueue_struct *prestera_wq;
|
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,
|
struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
|
||||||
u32 dev_id, u32 hw_id)
|
u32 dev_id, u32 hw_id)
|
||||||
{
|
{
|
||||||
@ -38,8 +62,7 @@ struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
|
|||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct prestera_port *prestera_find_port(struct prestera_switch *sw,
|
struct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id)
|
||||||
u32 id)
|
|
||||||
{
|
{
|
||||||
struct prestera_port *port = NULL;
|
struct prestera_port *port = NULL;
|
||||||
|
|
||||||
@ -261,6 +284,8 @@ static int prestera_port_create(struct prestera_switch *sw, u32 id)
|
|||||||
|
|
||||||
port = netdev_priv(dev);
|
port = netdev_priv(dev);
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&port->vlans_list);
|
||||||
|
port->pvid = PRESTERA_DEFAULT_VID;
|
||||||
port->dev = dev;
|
port->dev = dev;
|
||||||
port->id = id;
|
port->id = id;
|
||||||
port->sw = sw;
|
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);
|
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)
|
static int prestera_switch_init(struct prestera_switch *sw)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@ -469,10 +559,18 @@ static int prestera_switch_init(struct prestera_switch *sw)
|
|||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
err = prestera_rxtx_switch_init(sw);
|
err = prestera_netdev_event_handler_register(sw);
|
||||||
if (err)
|
if (err)
|
||||||
return 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);
|
err = prestera_event_handlers_register(sw);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_handlers_register;
|
goto err_handlers_register;
|
||||||
@ -493,6 +591,10 @@ err_dl_register:
|
|||||||
prestera_event_handlers_unregister(sw);
|
prestera_event_handlers_unregister(sw);
|
||||||
err_handlers_register:
|
err_handlers_register:
|
||||||
prestera_rxtx_switch_fini(sw);
|
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);
|
prestera_hw_switch_fini(sw);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
@ -504,6 +606,8 @@ static void prestera_switch_fini(struct prestera_switch *sw)
|
|||||||
prestera_devlink_unregister(sw);
|
prestera_devlink_unregister(sw);
|
||||||
prestera_event_handlers_unregister(sw);
|
prestera_event_handlers_unregister(sw);
|
||||||
prestera_rxtx_switch_fini(sw);
|
prestera_rxtx_switch_fini(sw);
|
||||||
|
prestera_switchdev_fini(sw);
|
||||||
|
prestera_netdev_event_handler_unregister(sw);
|
||||||
prestera_hw_switch_fini(sw);
|
prestera_hw_switch_fini(sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1277
drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
Normal file
1277
drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
Normal file
File diff suppressed because it is too large
Load Diff
13
drivers/net/ethernet/marvell/prestera/prestera_switchdev.h
Normal file
13
drivers/net/ethernet/marvell/prestera/prestera_switchdev.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
|
||||||
|
/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved. */
|
||||||
|
|
||||||
|
#ifndef _PRESTERA_SWITCHDEV_H_
|
||||||
|
#define _PRESTERA_SWITCHDEV_H_
|
||||||
|
|
||||||
|
int prestera_switchdev_init(struct prestera_switch *sw);
|
||||||
|
void prestera_switchdev_fini(struct prestera_switch *sw);
|
||||||
|
|
||||||
|
int prestera_bridge_port_event(struct net_device *dev, unsigned long event,
|
||||||
|
void *ptr);
|
||||||
|
|
||||||
|
#endif /* _PRESTERA_SWITCHDEV_H_ */
|
Loading…
Reference in New Issue
Block a user