// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 /* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */ #include #include #include #include #include "prestera.h" #include "prestera_hw.h" #include "prestera_acl.h" #include "prestera_counter.h" #include "prestera_router_hw.h" #define PRESTERA_SWITCH_INIT_TIMEOUT_MS (30 * 1000) #define PRESTERA_MIN_MTU 64 #define PRESTERA_MSG_CHUNK_SIZE 1024 enum prestera_cmd_type_t { PRESTERA_CMD_TYPE_SWITCH_INIT = 0x1, PRESTERA_CMD_TYPE_SWITCH_ATTR_SET = 0x2, PRESTERA_CMD_TYPE_PORT_ATTR_SET = 0x100, 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_COUNTER_GET = 0x510, PRESTERA_CMD_TYPE_COUNTER_ABORT = 0x511, PRESTERA_CMD_TYPE_COUNTER_TRIGGER = 0x512, PRESTERA_CMD_TYPE_COUNTER_BLOCK_GET = 0x513, PRESTERA_CMD_TYPE_COUNTER_BLOCK_RELEASE = 0x514, PRESTERA_CMD_TYPE_COUNTER_CLEAR = 0x515, PRESTERA_CMD_TYPE_VTCAM_CREATE = 0x540, PRESTERA_CMD_TYPE_VTCAM_DESTROY = 0x541, PRESTERA_CMD_TYPE_VTCAM_RULE_ADD = 0x550, PRESTERA_CMD_TYPE_VTCAM_RULE_DELETE = 0x551, PRESTERA_CMD_TYPE_VTCAM_IFACE_BIND = 0x560, PRESTERA_CMD_TYPE_VTCAM_IFACE_UNBIND = 0x561, PRESTERA_CMD_TYPE_ROUTER_RIF_CREATE = 0x600, PRESTERA_CMD_TYPE_ROUTER_RIF_DELETE = 0x601, PRESTERA_CMD_TYPE_ROUTER_LPM_ADD = 0x610, PRESTERA_CMD_TYPE_ROUTER_LPM_DELETE = 0x611, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_SET = 0x622, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_BLK_GET = 0x645, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_ADD = 0x623, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_DELETE = 0x624, PRESTERA_CMD_TYPE_ROUTER_VR_CREATE = 0x630, PRESTERA_CMD_TYPE_ROUTER_VR_DELETE = 0x631, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_CREATE = 0x700, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_DESTROY = 0x701, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_SET = 0x702, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_RESET = 0x703, PRESTERA_CMD_TYPE_MDB_CREATE = 0x704, PRESTERA_CMD_TYPE_MDB_DESTROY = 0x705, PRESTERA_CMD_TYPE_RXTX_INIT = 0x800, PRESTERA_CMD_TYPE_LAG_MEMBER_ADD = 0x900, PRESTERA_CMD_TYPE_LAG_MEMBER_DELETE = 0x901, PRESTERA_CMD_TYPE_LAG_MEMBER_ENABLE = 0x902, PRESTERA_CMD_TYPE_LAG_MEMBER_DISABLE = 0x903, PRESTERA_CMD_TYPE_STP_PORT_SET = 0x1000, PRESTERA_CMD_TYPE_SPAN_GET = 0x1100, PRESTERA_CMD_TYPE_SPAN_INGRESS_BIND = 0x1101, PRESTERA_CMD_TYPE_SPAN_INGRESS_UNBIND = 0x1102, PRESTERA_CMD_TYPE_SPAN_RELEASE = 0x1103, PRESTERA_CMD_TYPE_SPAN_EGRESS_BIND = 0x1104, PRESTERA_CMD_TYPE_SPAN_EGRESS_UNBIND = 0x1105, PRESTERA_CMD_TYPE_POLICER_CREATE = 0x1500, PRESTERA_CMD_TYPE_POLICER_RELEASE = 0x1501, PRESTERA_CMD_TYPE_POLICER_SET = 0x1502, PRESTERA_CMD_TYPE_CPU_CODE_COUNTERS_GET = 0x2000, PRESTERA_CMD_TYPE_ACK = 0x10000, PRESTERA_CMD_TYPE_MAX }; enum { PRESTERA_CMD_PORT_ATTR_ADMIN_STATE = 1, 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_LOCKED = 10, PRESTERA_CMD_PORT_ATTR_PHY_MODE = 12, PRESTERA_CMD_PORT_ATTR_TYPE = 13, PRESTERA_CMD_PORT_ATTR_STATS = 17, PRESTERA_CMD_PORT_ATTR_MAC_AUTONEG_RESTART = 18, PRESTERA_CMD_PORT_ATTR_PHY_AUTONEG_RESTART = 19, PRESTERA_CMD_PORT_ATTR_MAC_MODE = 22, }; enum { PRESTERA_CMD_SWITCH_ATTR_MAC = 1, PRESTERA_CMD_SWITCH_ATTR_AGEING = 2, }; enum { PRESTERA_CMD_ACK_OK, PRESTERA_CMD_ACK_FAILED, PRESTERA_CMD_ACK_MAX }; enum { PRESTERA_PORT_TP_NA, PRESTERA_PORT_TP_MDI, PRESTERA_PORT_TP_MDIX, PRESTERA_PORT_TP_AUTO, }; enum { PRESTERA_PORT_FLOOD_TYPE_UC = 0, PRESTERA_PORT_FLOOD_TYPE_MC = 1, }; enum { PRESTERA_PORT_GOOD_OCTETS_RCV_CNT, PRESTERA_PORT_BAD_OCTETS_RCV_CNT, PRESTERA_PORT_MAC_TRANSMIT_ERR_CNT, PRESTERA_PORT_BRDC_PKTS_RCV_CNT, PRESTERA_PORT_MC_PKTS_RCV_CNT, PRESTERA_PORT_PKTS_64L_CNT, PRESTERA_PORT_PKTS_65TO127L_CNT, PRESTERA_PORT_PKTS_128TO255L_CNT, PRESTERA_PORT_PKTS_256TO511L_CNT, PRESTERA_PORT_PKTS_512TO1023L_CNT, PRESTERA_PORT_PKTS_1024TOMAXL_CNT, PRESTERA_PORT_EXCESSIVE_COLLISIONS_CNT, PRESTERA_PORT_MC_PKTS_SENT_CNT, PRESTERA_PORT_BRDC_PKTS_SENT_CNT, PRESTERA_PORT_FC_SENT_CNT, PRESTERA_PORT_GOOD_FC_RCV_CNT, PRESTERA_PORT_DROP_EVENTS_CNT, PRESTERA_PORT_UNDERSIZE_PKTS_CNT, PRESTERA_PORT_FRAGMENTS_PKTS_CNT, PRESTERA_PORT_OVERSIZE_PKTS_CNT, PRESTERA_PORT_JABBER_PKTS_CNT, PRESTERA_PORT_MAC_RCV_ERROR_CNT, PRESTERA_PORT_BAD_CRC_CNT, PRESTERA_PORT_COLLISIONS_CNT, PRESTERA_PORT_LATE_COLLISIONS_CNT, PRESTERA_PORT_GOOD_UC_PKTS_RCV_CNT, PRESTERA_PORT_GOOD_UC_PKTS_SENT_CNT, PRESTERA_PORT_MULTIPLE_PKTS_SENT_CNT, PRESTERA_PORT_DEFERRED_PKTS_SENT_CNT, PRESTERA_PORT_GOOD_OCTETS_SENT_CNT, PRESTERA_PORT_CNT_MAX }; enum { PRESTERA_FC_NONE, PRESTERA_FC_SYMMETRIC, PRESTERA_FC_ASYMMETRIC, PRESTERA_FC_SYMM_ASYMM, }; enum { PRESTERA_POLICER_MODE_SR_TCM }; enum { PRESTERA_HW_FDB_ENTRY_TYPE_REG_PORT = 0, PRESTERA_HW_FDB_ENTRY_TYPE_LAG = 1, PRESTERA_HW_FDB_ENTRY_TYPE_MAX = 2, }; struct prestera_fw_event_handler { struct list_head list; struct rcu_head rcu; enum prestera_event_type type; prestera_event_cb_t func; void *arg; }; enum { PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_REG_PORT = 0, PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_LAG = 1, PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_MAX = 2, }; struct prestera_msg_cmd { __le32 type; }; struct prestera_msg_ret { struct prestera_msg_cmd cmd; __le32 status; }; struct prestera_msg_common_req { struct prestera_msg_cmd cmd; }; struct prestera_msg_common_resp { struct prestera_msg_ret ret; }; struct prestera_msg_switch_attr_req { struct prestera_msg_cmd cmd; __le32 attr; union { __le32 ageing_timeout_ms; struct { u8 mac[ETH_ALEN]; u8 __pad[2]; }; } param; }; struct prestera_msg_switch_init_resp { struct prestera_msg_ret ret; __le32 port_count; __le32 mtu_max; __le32 size_tbl_router_nexthop; u8 switch_id; u8 lag_max; u8 lag_member_max; }; struct prestera_msg_event_port_param { union { struct { __le32 mode; __le32 speed; u8 oper; u8 duplex; u8 fc; u8 fec; } mac; struct { __le64 lmode_bmap; u8 mdix; u8 fc; u8 __pad[2]; } __packed phy; /* make sure always 12 bytes size */ }; }; struct prestera_msg_port_cap_param { __le64 link_mode; u8 type; u8 fec; u8 fc; u8 transceiver; }; struct prestera_msg_port_flood_param { u8 type; u8 enable; u8 __pad[2]; }; union prestera_msg_port_param { __le32 mtu; __le32 speed; __le32 link_mode; u8 admin_state; u8 oper_state; u8 mac[ETH_ALEN]; u8 accept_frm_type; u8 learning; u8 flood; u8 type; u8 duplex; u8 fec; u8 fc; u8 br_locked; union { struct { u8 admin; u8 fc; u8 ap_enable; u8 __reserved[5]; union { struct { __le32 mode; __le32 speed; u8 inband; u8 duplex; u8 fec; u8 fec_supp; } reg_mode; struct { __le32 mode; __le32 speed; u8 fec; u8 fec_supp; u8 __pad[2]; } ap_modes[PRESTERA_AP_PORT_MAX]; }; } mac; struct { __le64 modes; __le32 mode; u8 admin; u8 adv_enable; u8 mdix; u8 __pad; } phy; } link; struct prestera_msg_port_cap_param cap; struct prestera_msg_port_flood_param flood_ext; struct prestera_msg_event_port_param link_evt; }; struct prestera_msg_port_attr_req { struct prestera_msg_cmd cmd; __le32 attr; __le32 port; __le32 dev; union prestera_msg_port_param param; }; struct prestera_msg_port_attr_resp { struct prestera_msg_ret ret; union prestera_msg_port_param param; }; struct prestera_msg_port_stats_resp { struct prestera_msg_ret ret; __le64 stats[PRESTERA_PORT_CNT_MAX]; }; struct prestera_msg_port_info_req { struct prestera_msg_cmd cmd; __le32 port; }; struct prestera_msg_port_info_resp { struct prestera_msg_ret ret; __le32 hw_id; __le32 dev_id; __le16 fp_id; u8 pad[2]; }; struct prestera_msg_vlan_req { struct prestera_msg_cmd cmd; __le32 port; __le32 dev; __le16 vid; u8 is_member; u8 is_tagged; }; struct prestera_msg_fdb_req { struct prestera_msg_cmd cmd; __le32 flush_mode; union { struct { __le32 port; __le32 dev; }; __le16 lag_id; } dest; __le16 vid; u8 dest_type; u8 dynamic; u8 mac[ETH_ALEN]; u8 __pad[2]; }; struct prestera_msg_bridge_req { struct prestera_msg_cmd cmd; __le32 port; __le32 dev; __le16 bridge; u8 pad[2]; }; struct prestera_msg_bridge_resp { struct prestera_msg_ret ret; __le16 bridge; u8 pad[2]; }; struct prestera_msg_vtcam_create_req { struct prestera_msg_cmd cmd; __le32 keymask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX]; u8 direction; u8 lookup; u8 pad[2]; }; struct prestera_msg_vtcam_destroy_req { struct prestera_msg_cmd cmd; __le32 vtcam_id; }; struct prestera_msg_vtcam_rule_del_req { struct prestera_msg_cmd cmd; __le32 vtcam_id; __le32 id; }; struct prestera_msg_vtcam_bind_req { struct prestera_msg_cmd cmd; union { struct { __le32 hw_id; __le32 dev_id; } port; __le32 index; }; __le32 vtcam_id; __le16 pcl_id; __le16 type; }; struct prestera_msg_vtcam_resp { struct prestera_msg_ret ret; __le32 vtcam_id; __le32 rule_id; }; struct prestera_msg_acl_action { __le32 id; __le32 __reserved; union { struct { __le32 index; } jump; struct { __le32 id; } police; struct { __le32 id; } count; __le32 reserved[6]; }; }; struct prestera_msg_vtcam_rule_add_req { struct prestera_msg_cmd cmd; __le32 key[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX]; __le32 keymask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX]; __le32 vtcam_id; __le32 prio; __le32 n_act; struct prestera_msg_acl_action actions_msg[] __counted_by_le(n_act); }; struct prestera_msg_counter_req { struct prestera_msg_cmd cmd; __le32 client; __le32 block_id; __le32 num_counters; }; struct prestera_msg_counter_stats { __le64 packets; __le64 bytes; }; struct prestera_msg_counter_resp { struct prestera_msg_ret ret; __le32 block_id; __le32 offset; __le32 num_counters; __le32 done; struct prestera_msg_counter_stats stats[]; }; struct prestera_msg_span_req { struct prestera_msg_cmd cmd; __le32 port; __le32 dev; u8 id; u8 pad[3]; }; struct prestera_msg_span_resp { struct prestera_msg_ret ret; u8 id; u8 pad[3]; }; struct prestera_msg_stp_req { struct prestera_msg_cmd cmd; __le32 port; __le32 dev; __le16 vid; u8 state; u8 __pad; }; struct prestera_msg_rxtx_req { struct prestera_msg_cmd cmd; u8 use_sdma; u8 pad[3]; }; struct prestera_msg_rxtx_resp { struct prestera_msg_ret ret; __le32 map_addr; }; struct prestera_msg_iface { union { struct { __le32 dev; __le32 port; }; __le16 lag_id; }; __le16 vr_id; __le16 vid; u8 type; u8 __pad[3]; }; struct prestera_msg_ip_addr { union { __be32 ipv4; __be32 ipv6[4]; } u; u8 v; /* e.g. PRESTERA_IPV4 */ u8 __pad[3]; }; struct prestera_msg_nh { struct prestera_msg_iface oif; __le32 hw_id; u8 mac[ETH_ALEN]; u8 is_active; u8 pad; }; struct prestera_msg_rif_req { struct prestera_msg_cmd cmd; struct prestera_msg_iface iif; __le32 mtu; __le16 rif_id; __le16 __reserved; u8 mac[ETH_ALEN]; u8 __pad[2]; }; struct prestera_msg_rif_resp { struct prestera_msg_ret ret; __le16 rif_id; u8 __pad[2]; }; struct prestera_msg_lpm_req { struct prestera_msg_cmd cmd; struct prestera_msg_ip_addr dst; __le32 grp_id; __le32 dst_len; __le16 vr_id; u8 __pad[2]; }; struct prestera_msg_nh_req { struct prestera_msg_cmd cmd; struct prestera_msg_nh nh[PRESTERA_NHGR_SIZE_MAX]; __le32 size; __le32 grp_id; }; struct prestera_msg_nh_chunk_req { struct prestera_msg_cmd cmd; __le32 offset; }; struct prestera_msg_nh_chunk_resp { struct prestera_msg_ret ret; u8 hw_state[PRESTERA_MSG_CHUNK_SIZE]; }; struct prestera_msg_nh_grp_req { struct prestera_msg_cmd cmd; __le32 grp_id; __le32 size; }; struct prestera_msg_nh_grp_resp { struct prestera_msg_ret ret; __le32 grp_id; }; struct prestera_msg_vr_req { struct prestera_msg_cmd cmd; __le16 vr_id; u8 __pad[2]; }; struct prestera_msg_vr_resp { struct prestera_msg_ret ret; __le16 vr_id; u8 __pad[2]; }; struct prestera_msg_lag_req { struct prestera_msg_cmd cmd; __le32 port; __le32 dev; __le16 lag_id; u8 pad[2]; }; struct prestera_msg_cpu_code_counter_req { struct prestera_msg_cmd cmd; u8 counter_type; u8 code; u8 pad[2]; }; struct mvsw_msg_cpu_code_counter_ret { struct prestera_msg_ret ret; __le64 packet_count; }; struct prestera_msg_policer_req { struct prestera_msg_cmd cmd; __le32 id; union { struct { __le64 cir; __le32 cbs; } __packed sr_tcm; /* make sure always 12 bytes size */ __le32 reserved[6]; }; u8 mode; u8 type; u8 pad[2]; }; struct prestera_msg_policer_resp { struct prestera_msg_ret ret; __le32 id; }; struct prestera_msg_event { __le16 type; __le16 id; }; struct prestera_msg_event_port { struct prestera_msg_event id; __le32 port_id; struct 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; __le32 vid; union { __le32 port_id; __le16 lag_id; } dest; union prestera_msg_event_fdb_param param; u8 dest_type; }; struct prestera_msg_flood_domain_create_req { struct prestera_msg_cmd cmd; }; struct prestera_msg_flood_domain_create_resp { struct prestera_msg_ret ret; __le32 flood_domain_idx; }; struct prestera_msg_flood_domain_destroy_req { struct prestera_msg_cmd cmd; __le32 flood_domain_idx; }; struct prestera_msg_flood_domain_ports_reset_req { struct prestera_msg_cmd cmd; __le32 flood_domain_idx; }; struct prestera_msg_flood_domain_port { union { struct { __le32 port_num; __le32 dev_num; }; __le16 lag_id; }; __le16 vid; __le16 port_type; }; struct prestera_msg_flood_domain_ports_set_req { struct prestera_msg_cmd cmd; __le32 flood_domain_idx; __le32 ports_num; struct prestera_msg_flood_domain_port ports[] __counted_by_le(ports_num); }; struct prestera_msg_mdb_create_req { struct prestera_msg_cmd cmd; __le32 flood_domain_idx; __le16 vid; u8 mac[ETH_ALEN]; }; struct prestera_msg_mdb_destroy_req { struct prestera_msg_cmd cmd; __le32 flood_domain_idx; __le16 vid; u8 mac[ETH_ALEN]; }; static void prestera_hw_build_tests(void) { /* check requests */ BUILD_BUG_ON(sizeof(struct prestera_msg_common_req) != 4); BUILD_BUG_ON(sizeof(struct prestera_msg_switch_attr_req) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_port_attr_req) != 144); BUILD_BUG_ON(sizeof(struct prestera_msg_port_info_req) != 8); BUILD_BUG_ON(sizeof(struct prestera_msg_vlan_req) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_fdb_req) != 28); BUILD_BUG_ON(sizeof(struct prestera_msg_bridge_req) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_span_req) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_stp_req) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_rxtx_req) != 8); BUILD_BUG_ON(sizeof(struct prestera_msg_lag_req) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_cpu_code_counter_req) != 8); BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_create_req) != 84); BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_destroy_req) != 8); BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_rule_add_req) != 168); BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_rule_del_req) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_bind_req) != 20); BUILD_BUG_ON(sizeof(struct prestera_msg_acl_action) != 32); BUILD_BUG_ON(sizeof(struct prestera_msg_counter_req) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_counter_stats) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_rif_req) != 36); BUILD_BUG_ON(sizeof(struct prestera_msg_vr_req) != 8); BUILD_BUG_ON(sizeof(struct prestera_msg_lpm_req) != 36); BUILD_BUG_ON(sizeof(struct prestera_msg_policer_req) != 36); BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_create_req) != 4); BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_destroy_req) != 8); BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_ports_set_req) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_ports_reset_req) != 8); BUILD_BUG_ON(sizeof(struct prestera_msg_mdb_create_req) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_mdb_destroy_req) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_nh_req) != 124); BUILD_BUG_ON(sizeof(struct prestera_msg_nh_chunk_req) != 8); BUILD_BUG_ON(sizeof(struct prestera_msg_nh_grp_req) != 12); /* structure that are part of req/resp fw messages */ BUILD_BUG_ON(sizeof(struct prestera_msg_iface) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_ip_addr) != 20); BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_port) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_nh) != 28); /* check responses */ BUILD_BUG_ON(sizeof(struct prestera_msg_common_resp) != 8); BUILD_BUG_ON(sizeof(struct prestera_msg_switch_init_resp) != 24); BUILD_BUG_ON(sizeof(struct prestera_msg_port_attr_resp) != 136); BUILD_BUG_ON(sizeof(struct prestera_msg_port_stats_resp) != 248); BUILD_BUG_ON(sizeof(struct prestera_msg_port_info_resp) != 20); BUILD_BUG_ON(sizeof(struct prestera_msg_bridge_resp) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_span_resp) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_rxtx_resp) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_vtcam_resp) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_counter_resp) != 24); BUILD_BUG_ON(sizeof(struct prestera_msg_rif_resp) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_vr_resp) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_policer_resp) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_create_resp) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_nh_chunk_resp) != 1032); BUILD_BUG_ON(sizeof(struct prestera_msg_nh_grp_resp) != 12); /* check events */ BUILD_BUG_ON(sizeof(struct prestera_msg_event_port) != 20); BUILD_BUG_ON(sizeof(struct prestera_msg_event_fdb) != 20); } static u8 prestera_hw_mdix_to_eth(u8 mode); static void prestera_hw_remote_fc_to_eth(u8 fc, bool *pause, bool *asym_pause); static int __prestera_cmd_ret(struct prestera_switch *sw, enum prestera_cmd_type_t type, struct prestera_msg_cmd *cmd, size_t clen, struct prestera_msg_ret *ret, size_t rlen, int waitms) { struct prestera_device *dev = sw->dev; int err; cmd->type = __cpu_to_le32(type); err = dev->send_req(dev, 0, cmd, clen, ret, rlen, waitms); if (err) return err; if (ret->cmd.type != __cpu_to_le32(PRESTERA_CMD_TYPE_ACK)) return -EBADE; if (ret->status != __cpu_to_le32(PRESTERA_CMD_ACK_OK)) return -EINVAL; return 0; } static int prestera_cmd_ret(struct prestera_switch *sw, enum prestera_cmd_type_t type, struct prestera_msg_cmd *cmd, size_t clen, struct prestera_msg_ret *ret, size_t rlen) { return __prestera_cmd_ret(sw, type, cmd, clen, ret, rlen, 0); } static int prestera_cmd_ret_wait(struct prestera_switch *sw, enum prestera_cmd_type_t type, struct prestera_msg_cmd *cmd, size_t clen, struct prestera_msg_ret *ret, size_t rlen, int waitms) { return __prestera_cmd_ret(sw, type, cmd, clen, ret, rlen, waitms); } static int prestera_cmd(struct prestera_switch *sw, enum prestera_cmd_type_t type, struct prestera_msg_cmd *cmd, size_t clen) { struct prestera_msg_common_resp resp; return prestera_cmd_ret(sw, type, cmd, clen, &resp.ret, sizeof(resp)); } static int prestera_fw_parse_port_evt(void *msg, struct prestera_event *evt) { struct prestera_msg_event_port *hw_evt; hw_evt = (struct prestera_msg_event_port *)msg; evt->port_evt.port_id = __le32_to_cpu(hw_evt->port_id); if (evt->id == PRESTERA_PORT_EVENT_MAC_STATE_CHANGED) { evt->port_evt.data.mac.oper = hw_evt->param.mac.oper; evt->port_evt.data.mac.mode = __le32_to_cpu(hw_evt->param.mac.mode); evt->port_evt.data.mac.speed = __le32_to_cpu(hw_evt->param.mac.speed); evt->port_evt.data.mac.duplex = hw_evt->param.mac.duplex; evt->port_evt.data.mac.fc = hw_evt->param.mac.fc; evt->port_evt.data.mac.fec = hw_evt->param.mac.fec; } else { return -EINVAL; } return 0; } static int prestera_fw_parse_fdb_evt(void *msg, struct prestera_event *evt) { struct prestera_msg_event_fdb *hw_evt = msg; switch (hw_evt->dest_type) { case PRESTERA_HW_FDB_ENTRY_TYPE_REG_PORT: evt->fdb_evt.type = PRESTERA_FDB_ENTRY_TYPE_REG_PORT; evt->fdb_evt.dest.port_id = __le32_to_cpu(hw_evt->dest.port_id); break; case PRESTERA_HW_FDB_ENTRY_TYPE_LAG: evt->fdb_evt.type = PRESTERA_FDB_ENTRY_TYPE_LAG; evt->fdb_evt.dest.lag_id = __le16_to_cpu(hw_evt->dest.lag_id); break; default: return -EINVAL; } evt->fdb_evt.vid = __le32_to_cpu(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 * __find_event_handler(const struct prestera_switch *sw, enum prestera_event_type type) { struct prestera_fw_event_handler *eh; list_for_each_entry_rcu(eh, &sw->event_handlers, list) { if (eh->type == type) return eh; } return NULL; } static int prestera_find_event_handler(const struct prestera_switch *sw, enum prestera_event_type type, struct prestera_fw_event_handler *eh) { struct prestera_fw_event_handler *tmp; int err = 0; rcu_read_lock(); tmp = __find_event_handler(sw, type); if (tmp) *eh = *tmp; else err = -ENOENT; rcu_read_unlock(); return err; } static int prestera_evt_recv(struct prestera_device *dev, void *buf, size_t size) { struct prestera_switch *sw = dev->priv; struct prestera_msg_event *msg = buf; struct prestera_fw_event_handler eh; struct prestera_event evt; u16 msg_type; int err; msg_type = __le16_to_cpu(msg->type); if (msg_type >= PRESTERA_EVENT_TYPE_MAX) return -EINVAL; if (!fw_event_parsers[msg_type].func) return -ENOENT; err = prestera_find_event_handler(sw, msg_type, &eh); if (err) return err; evt.id = __le16_to_cpu(msg->id); err = fw_event_parsers[msg_type].func(buf, &evt); if (err) return err; eh.func(sw, &evt, eh.arg); return 0; } static void prestera_pkt_recv(struct prestera_device *dev) { struct prestera_switch *sw = dev->priv; struct prestera_fw_event_handler eh; struct prestera_event ev; int err; ev.id = PRESTERA_RXTX_EVENT_RCV_PKT; err = prestera_find_event_handler(sw, PRESTERA_EVENT_TYPE_RXTX, &eh); if (err) return; eh.func(sw, &ev, eh.arg); } static u8 prestera_hw_mdix_to_eth(u8 mode) { switch (mode) { case PRESTERA_PORT_TP_MDI: return ETH_TP_MDI; case PRESTERA_PORT_TP_MDIX: return ETH_TP_MDI_X; case PRESTERA_PORT_TP_AUTO: return ETH_TP_MDI_AUTO; default: return ETH_TP_MDI_INVALID; } } static u8 prestera_hw_mdix_from_eth(u8 mode) { switch (mode) { case ETH_TP_MDI: return PRESTERA_PORT_TP_MDI; case ETH_TP_MDI_X: return PRESTERA_PORT_TP_MDIX; case ETH_TP_MDI_AUTO: return PRESTERA_PORT_TP_AUTO; default: return PRESTERA_PORT_TP_NA; } } int prestera_hw_port_info_get(const struct prestera_port *port, u32 *dev_id, u32 *hw_id, u16 *fp_id) { struct prestera_msg_port_info_req req = { .port = __cpu_to_le32(port->id), }; struct prestera_msg_port_info_resp resp; int err; err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_INFO_GET, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; *dev_id = __le32_to_cpu(resp.dev_id); *hw_id = __le32_to_cpu(resp.hw_id); *fp_id = __le16_to_cpu(resp.fp_id); return 0; } int prestera_hw_switch_mac_set(struct prestera_switch *sw, const char *mac) { struct prestera_msg_switch_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_SWITCH_ATTR_MAC), }; ether_addr_copy(req.param.mac, mac); return prestera_cmd(sw, PRESTERA_CMD_TYPE_SWITCH_ATTR_SET, &req.cmd, sizeof(req)); } int prestera_hw_switch_init(struct prestera_switch *sw) { struct prestera_msg_switch_init_resp resp; struct prestera_msg_common_req req; int err; INIT_LIST_HEAD(&sw->event_handlers); prestera_hw_build_tests(); err = prestera_cmd_ret_wait(sw, PRESTERA_CMD_TYPE_SWITCH_INIT, &req.cmd, sizeof(req), &resp.ret, sizeof(resp), PRESTERA_SWITCH_INIT_TIMEOUT_MS); if (err) return err; sw->dev->recv_msg = prestera_evt_recv; sw->dev->recv_pkt = prestera_pkt_recv; sw->port_count = __le32_to_cpu(resp.port_count); sw->mtu_min = PRESTERA_MIN_MTU; sw->mtu_max = __le32_to_cpu(resp.mtu_max); sw->id = resp.switch_id; sw->lag_member_max = resp.lag_member_max; sw->lag_max = resp.lag_max; sw->size_tbl_router_nexthop = __le32_to_cpu(resp.size_tbl_router_nexthop); return 0; } 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 = __cpu_to_le32(PRESTERA_CMD_SWITCH_ATTR_AGEING), .param = { .ageing_timeout_ms = __cpu_to_le32(ageing_ms), }, }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_SWITCH_ATTR_SET, &req.cmd, sizeof(req)); } int prestera_hw_port_mac_mode_get(const struct prestera_port *port, u32 *mode, u32 *speed, u8 *duplex, u8 *fec) { struct prestera_msg_port_attr_resp resp; struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_MAC_MODE), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id) }; int err; err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_GET, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; if (mode) *mode = __le32_to_cpu(resp.param.link_evt.mac.mode); if (speed) *speed = __le32_to_cpu(resp.param.link_evt.mac.speed); if (duplex) *duplex = resp.param.link_evt.mac.duplex; if (fec) *fec = resp.param.link_evt.mac.fec; return err; } int prestera_hw_port_mac_mode_set(const struct prestera_port *port, bool admin, u32 mode, u8 inband, u32 speed, u8 duplex, u8 fec) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_MAC_MODE), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .param = { .link = { .mac = { .admin = admin, .reg_mode.mode = __cpu_to_le32(mode), .reg_mode.inband = inband, .reg_mode.speed = __cpu_to_le32(speed), .reg_mode.duplex = duplex, .reg_mode.fec = fec } } } }; return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET, &req.cmd, sizeof(req)); } int prestera_hw_port_phy_mode_get(const struct prestera_port *port, u8 *mdix, u64 *lmode_bmap, bool *fc_pause, bool *fc_asym) { struct prestera_msg_port_attr_resp resp; struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_PHY_MODE), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id) }; int err; err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_GET, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; if (mdix) *mdix = prestera_hw_mdix_to_eth(resp.param.link_evt.phy.mdix); if (lmode_bmap) *lmode_bmap = __le64_to_cpu(resp.param.link_evt.phy.lmode_bmap); if (fc_pause && fc_asym) prestera_hw_remote_fc_to_eth(resp.param.link_evt.phy.fc, fc_pause, fc_asym); return err; } int prestera_hw_port_phy_mode_set(const struct prestera_port *port, bool admin, bool adv, u32 mode, u64 modes, u8 mdix) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_PHY_MODE), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .param = { .link = { .phy = { .admin = admin, .adv_enable = adv ? 1 : 0, .mode = __cpu_to_le32(mode), .modes = __cpu_to_le64(modes), } } } }; req.param.link.phy.mdix = prestera_hw_mdix_from_eth(mdix); return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET, &req.cmd, sizeof(req)); } int prestera_hw_port_mtu_set(const struct prestera_port *port, u32 mtu) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_MTU), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .param = { .mtu = __cpu_to_le32(mtu), } }; return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET, &req.cmd, sizeof(req)); } int prestera_hw_port_mac_set(const struct prestera_port *port, const char *mac) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_MAC), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), }; ether_addr_copy(req.param.mac, mac); return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET, &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 = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_ACCEPT_FRAME_TYPE), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(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) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_CAPABILITY), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), }; struct prestera_msg_port_attr_resp resp; int err; err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_GET, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; caps->supp_link_modes = __le64_to_cpu(resp.param.cap.link_mode); caps->transceiver = resp.param.cap.transceiver; caps->supp_fec = resp.param.cap.fec; caps->type = resp.param.cap.type; return err; } static void prestera_hw_remote_fc_to_eth(u8 fc, bool *pause, bool *asym_pause) { switch (fc) { case PRESTERA_FC_SYMMETRIC: *pause = true; *asym_pause = false; break; case PRESTERA_FC_ASYMMETRIC: *pause = false; *asym_pause = true; break; case PRESTERA_FC_SYMM_ASYMM: *pause = true; *asym_pause = true; break; default: *pause = false; *asym_pause = false; } } int prestera_hw_vtcam_create(struct prestera_switch *sw, u8 lookup, const u32 *keymask, u32 *vtcam_id, enum prestera_hw_vtcam_direction_t dir) { int err; struct prestera_msg_vtcam_resp resp; struct prestera_msg_vtcam_create_req req = { .lookup = lookup, .direction = dir, }; if (keymask) memcpy(req.keymask, keymask, sizeof(req.keymask)); else memset(req.keymask, 0, sizeof(req.keymask)); err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_VTCAM_CREATE, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; *vtcam_id = __le32_to_cpu(resp.vtcam_id); return 0; } int prestera_hw_vtcam_destroy(struct prestera_switch *sw, u32 vtcam_id) { struct prestera_msg_vtcam_destroy_req req = { .vtcam_id = __cpu_to_le32(vtcam_id), }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_VTCAM_DESTROY, &req.cmd, sizeof(req)); } static int prestera_acl_rule_add_put_action(struct prestera_msg_acl_action *action, struct prestera_acl_hw_action_info *info) { action->id = __cpu_to_le32(info->id); switch (info->id) { case PRESTERA_ACL_RULE_ACTION_ACCEPT: case PRESTERA_ACL_RULE_ACTION_DROP: case PRESTERA_ACL_RULE_ACTION_TRAP: /* just rule action id, no specific data */ break; case PRESTERA_ACL_RULE_ACTION_JUMP: action->jump.index = __cpu_to_le32(info->jump.index); break; case PRESTERA_ACL_RULE_ACTION_POLICE: action->police.id = __cpu_to_le32(info->police.id); break; case PRESTERA_ACL_RULE_ACTION_COUNT: action->count.id = __cpu_to_le32(info->count.id); break; default: return -EINVAL; } return 0; } int prestera_hw_vtcam_rule_add(struct prestera_switch *sw, u32 vtcam_id, u32 prio, void *key, void *keymask, struct prestera_acl_hw_action_info *act, u8 n_act, u32 *rule_id) { struct prestera_msg_vtcam_rule_add_req *req; struct prestera_msg_vtcam_resp resp; size_t size; int err; u8 i; size = struct_size(req, actions_msg, n_act); req = kzalloc(size, GFP_KERNEL); if (!req) return -ENOMEM; req->n_act = __cpu_to_le32(n_act); /* put acl matches into the message */ memcpy(req->key, key, sizeof(req->key)); memcpy(req->keymask, keymask, sizeof(req->keymask)); /* put acl actions into the message */ for (i = 0; i < n_act; i++) { err = prestera_acl_rule_add_put_action(&req->actions_msg[i], &act[i]); if (err) goto free_buff; } req->vtcam_id = __cpu_to_le32(vtcam_id); req->prio = __cpu_to_le32(prio); err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_VTCAM_RULE_ADD, &req->cmd, size, &resp.ret, sizeof(resp)); if (err) goto free_buff; *rule_id = __le32_to_cpu(resp.rule_id); free_buff: kfree(req); return err; } int prestera_hw_vtcam_rule_del(struct prestera_switch *sw, u32 vtcam_id, u32 rule_id) { struct prestera_msg_vtcam_rule_del_req req = { .vtcam_id = __cpu_to_le32(vtcam_id), .id = __cpu_to_le32(rule_id) }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_VTCAM_RULE_DELETE, &req.cmd, sizeof(req)); } int prestera_hw_vtcam_iface_bind(struct prestera_switch *sw, struct prestera_acl_iface *iface, u32 vtcam_id, u16 pcl_id) { struct prestera_msg_vtcam_bind_req req = { .vtcam_id = __cpu_to_le32(vtcam_id), .type = __cpu_to_le16(iface->type), .pcl_id = __cpu_to_le16(pcl_id) }; if (iface->type == PRESTERA_ACL_IFACE_TYPE_PORT) { req.port.dev_id = __cpu_to_le32(iface->port->dev_id); req.port.hw_id = __cpu_to_le32(iface->port->hw_id); } else { req.index = __cpu_to_le32(iface->index); } return prestera_cmd(sw, PRESTERA_CMD_TYPE_VTCAM_IFACE_BIND, &req.cmd, sizeof(req)); } int prestera_hw_vtcam_iface_unbind(struct prestera_switch *sw, struct prestera_acl_iface *iface, u32 vtcam_id) { struct prestera_msg_vtcam_bind_req req = { .vtcam_id = __cpu_to_le32(vtcam_id), .type = __cpu_to_le16(iface->type) }; if (iface->type == PRESTERA_ACL_IFACE_TYPE_PORT) { req.port.dev_id = __cpu_to_le32(iface->port->dev_id); req.port.hw_id = __cpu_to_le32(iface->port->hw_id); } else { req.index = __cpu_to_le32(iface->index); } return prestera_cmd(sw, PRESTERA_CMD_TYPE_VTCAM_IFACE_UNBIND, &req.cmd, sizeof(req)); } int prestera_hw_span_get(const struct prestera_port *port, u8 *span_id) { struct prestera_msg_span_resp resp; struct prestera_msg_span_req req = { .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), }; int err; err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_SPAN_GET, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; *span_id = resp.id; return 0; } int prestera_hw_span_bind(const struct prestera_port *port, u8 span_id, bool ingress) { struct prestera_msg_span_req req = { .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .id = span_id, }; enum prestera_cmd_type_t cmd_type; if (ingress) cmd_type = PRESTERA_CMD_TYPE_SPAN_INGRESS_BIND; else cmd_type = PRESTERA_CMD_TYPE_SPAN_EGRESS_BIND; return prestera_cmd(port->sw, cmd_type, &req.cmd, sizeof(req)); } int prestera_hw_span_unbind(const struct prestera_port *port, bool ingress) { struct prestera_msg_span_req req = { .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), }; enum prestera_cmd_type_t cmd_type; if (ingress) cmd_type = PRESTERA_CMD_TYPE_SPAN_INGRESS_UNBIND; else cmd_type = PRESTERA_CMD_TYPE_SPAN_EGRESS_UNBIND; return prestera_cmd(port->sw, cmd_type, &req.cmd, sizeof(req)); } int prestera_hw_span_release(struct prestera_switch *sw, u8 span_id) { struct prestera_msg_span_req req = { .id = span_id }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_SPAN_RELEASE, &req.cmd, sizeof(req)); } int prestera_hw_port_type_get(const struct prestera_port *port, u8 *type) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_TYPE), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), }; struct prestera_msg_port_attr_resp resp; int err; err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_GET, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; *type = resp.param.type; return 0; } int prestera_hw_port_speed_get(const struct prestera_port *port, u32 *speed) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_SPEED), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), }; struct prestera_msg_port_attr_resp resp; int err; err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_GET, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; *speed = __le32_to_cpu(resp.param.speed); return 0; } int prestera_hw_port_autoneg_restart(struct prestera_port *port) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_PHY_AUTONEG_RESTART), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), }; return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET, &req.cmd, sizeof(req)); } int prestera_hw_port_stats_get(const struct prestera_port *port, struct prestera_port_stats *st) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_STATS), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), }; struct prestera_msg_port_stats_resp resp; __le64 *hw = resp.stats; int err; err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_GET, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; st->good_octets_received = __le64_to_cpu(hw[PRESTERA_PORT_GOOD_OCTETS_RCV_CNT]); st->bad_octets_received = __le64_to_cpu(hw[PRESTERA_PORT_BAD_OCTETS_RCV_CNT]); st->mac_trans_error = __le64_to_cpu(hw[PRESTERA_PORT_MAC_TRANSMIT_ERR_CNT]); st->broadcast_frames_received = __le64_to_cpu(hw[PRESTERA_PORT_BRDC_PKTS_RCV_CNT]); st->multicast_frames_received = __le64_to_cpu(hw[PRESTERA_PORT_MC_PKTS_RCV_CNT]); st->frames_64_octets = __le64_to_cpu(hw[PRESTERA_PORT_PKTS_64L_CNT]); st->frames_65_to_127_octets = __le64_to_cpu(hw[PRESTERA_PORT_PKTS_65TO127L_CNT]); st->frames_128_to_255_octets = __le64_to_cpu(hw[PRESTERA_PORT_PKTS_128TO255L_CNT]); st->frames_256_to_511_octets = __le64_to_cpu(hw[PRESTERA_PORT_PKTS_256TO511L_CNT]); st->frames_512_to_1023_octets = __le64_to_cpu(hw[PRESTERA_PORT_PKTS_512TO1023L_CNT]); st->frames_1024_to_max_octets = __le64_to_cpu(hw[PRESTERA_PORT_PKTS_1024TOMAXL_CNT]); st->excessive_collision = __le64_to_cpu(hw[PRESTERA_PORT_EXCESSIVE_COLLISIONS_CNT]); st->multicast_frames_sent = __le64_to_cpu(hw[PRESTERA_PORT_MC_PKTS_SENT_CNT]); st->broadcast_frames_sent = __le64_to_cpu(hw[PRESTERA_PORT_BRDC_PKTS_SENT_CNT]); st->fc_sent = __le64_to_cpu(hw[PRESTERA_PORT_FC_SENT_CNT]); st->fc_received = __le64_to_cpu(hw[PRESTERA_PORT_GOOD_FC_RCV_CNT]); st->buffer_overrun = __le64_to_cpu(hw[PRESTERA_PORT_DROP_EVENTS_CNT]); st->undersize = __le64_to_cpu(hw[PRESTERA_PORT_UNDERSIZE_PKTS_CNT]); st->fragments = __le64_to_cpu(hw[PRESTERA_PORT_FRAGMENTS_PKTS_CNT]); st->oversize = __le64_to_cpu(hw[PRESTERA_PORT_OVERSIZE_PKTS_CNT]); st->jabber = __le64_to_cpu(hw[PRESTERA_PORT_JABBER_PKTS_CNT]); st->rx_error_frame_received = __le64_to_cpu(hw[PRESTERA_PORT_MAC_RCV_ERROR_CNT]); st->bad_crc = __le64_to_cpu(hw[PRESTERA_PORT_BAD_CRC_CNT]); st->collisions = __le64_to_cpu(hw[PRESTERA_PORT_COLLISIONS_CNT]); st->late_collision = __le64_to_cpu(hw[PRESTERA_PORT_LATE_COLLISIONS_CNT]); st->unicast_frames_received = __le64_to_cpu(hw[PRESTERA_PORT_GOOD_UC_PKTS_RCV_CNT]); st->unicast_frames_sent = __le64_to_cpu(hw[PRESTERA_PORT_GOOD_UC_PKTS_SENT_CNT]); st->sent_multiple = __le64_to_cpu(hw[PRESTERA_PORT_MULTIPLE_PKTS_SENT_CNT]); st->sent_deferred = __le64_to_cpu(hw[PRESTERA_PORT_DEFERRED_PKTS_SENT_CNT]); st->good_octets_sent = __le64_to_cpu(hw[PRESTERA_PORT_GOOD_OCTETS_SENT_CNT]); return 0; } int prestera_hw_port_learning_set(struct prestera_port *port, bool enable) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_LEARNING), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(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_uc_flood_set(const struct prestera_port *port, bool flood) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_FLOOD), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .param = { .flood_ext = { .type = PRESTERA_PORT_FLOOD_TYPE_UC, .enable = flood, } } }; return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET, &req.cmd, sizeof(req)); } int prestera_hw_port_mc_flood_set(const struct prestera_port *port, bool flood) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_FLOOD), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .param = { .flood_ext = { .type = PRESTERA_PORT_FLOOD_TYPE_MC, .enable = flood, } } }; return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET, &req.cmd, sizeof(req)); } int prestera_hw_port_br_locked_set(const struct prestera_port *port, bool br_locked) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_LOCKED), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .param = { .br_locked = br_locked, } }; 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 = __cpu_to_le16(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 = __cpu_to_le16(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 = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .vid = __cpu_to_le16(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 = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .vid = __cpu_to_le16(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 = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .vid = __cpu_to_le16(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 = { .dest = { .dev = __cpu_to_le32(port->dev_id), .port = __cpu_to_le32(port->hw_id), }, .vid = __cpu_to_le16(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 = { .dest = { .dev = __cpu_to_le32(port->dev_id), .port = __cpu_to_le32(port->hw_id), }, .vid = __cpu_to_le16(vid), }; ether_addr_copy(req.mac, mac); return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_FDB_DELETE, &req.cmd, sizeof(req)); } int prestera_hw_lag_fdb_add(struct prestera_switch *sw, u16 lag_id, const unsigned char *mac, u16 vid, bool dynamic) { struct prestera_msg_fdb_req req = { .dest_type = PRESTERA_HW_FDB_ENTRY_TYPE_LAG, .dest = { .lag_id = __cpu_to_le16(lag_id), }, .vid = __cpu_to_le16(vid), .dynamic = dynamic, }; ether_addr_copy(req.mac, mac); return prestera_cmd(sw, PRESTERA_CMD_TYPE_FDB_ADD, &req.cmd, sizeof(req)); } int prestera_hw_lag_fdb_del(struct prestera_switch *sw, u16 lag_id, const unsigned char *mac, u16 vid) { struct prestera_msg_fdb_req req = { .dest_type = PRESTERA_HW_FDB_ENTRY_TYPE_LAG, .dest = { .lag_id = __cpu_to_le16(lag_id), }, .vid = __cpu_to_le16(vid), }; ether_addr_copy(req.mac, mac); return prestera_cmd(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 = { .dest = { .dev = __cpu_to_le32(port->dev_id), .port = __cpu_to_le32(port->hw_id), }, .flush_mode = __cpu_to_le32(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 = __cpu_to_le16(vid), .flush_mode = __cpu_to_le32(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 = { .dest = { .dev = __cpu_to_le32(port->dev_id), .port = __cpu_to_le32(port->hw_id), }, .vid = __cpu_to_le16(vid), .flush_mode = __cpu_to_le32(mode), }; return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_FDB_FLUSH_PORT_VLAN, &req.cmd, sizeof(req)); } int prestera_hw_fdb_flush_lag(struct prestera_switch *sw, u16 lag_id, u32 mode) { struct prestera_msg_fdb_req req = { .dest_type = PRESTERA_HW_FDB_ENTRY_TYPE_LAG, .dest = { .lag_id = __cpu_to_le16(lag_id), }, .flush_mode = __cpu_to_le32(mode), }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_FDB_FLUSH_PORT, &req.cmd, sizeof(req)); } int prestera_hw_fdb_flush_lag_vlan(struct prestera_switch *sw, u16 lag_id, u16 vid, u32 mode) { struct prestera_msg_fdb_req req = { .dest_type = PRESTERA_HW_FDB_ENTRY_TYPE_LAG, .dest = { .lag_id = __cpu_to_le16(lag_id), }, .vid = __cpu_to_le16(vid), .flush_mode = __cpu_to_le32(mode), }; return prestera_cmd(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 = __le16_to_cpu(resp.bridge); return 0; } int prestera_hw_bridge_delete(struct prestera_switch *sw, u16 bridge_id) { struct prestera_msg_bridge_req req = { .bridge = __cpu_to_le16(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 = __cpu_to_le16(bridge_id), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(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 = __cpu_to_le16(bridge_id), .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), }; return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_BRIDGE_PORT_DELETE, &req.cmd, sizeof(req)); } static int prestera_iface_to_msg(struct prestera_iface *iface, struct prestera_msg_iface *msg_if) { switch (iface->type) { case PRESTERA_IF_PORT_E: case PRESTERA_IF_VID_E: msg_if->port = __cpu_to_le32(iface->dev_port.port_num); msg_if->dev = __cpu_to_le32(iface->dev_port.hw_dev_num); break; case PRESTERA_IF_LAG_E: msg_if->lag_id = __cpu_to_le16(iface->lag_id); break; default: return -EOPNOTSUPP; } msg_if->vr_id = __cpu_to_le16(iface->vr_id); msg_if->vid = __cpu_to_le16(iface->vlan_id); msg_if->type = iface->type; return 0; } int prestera_hw_rif_create(struct prestera_switch *sw, struct prestera_iface *iif, u8 *mac, u16 *rif_id) { struct prestera_msg_rif_resp resp; struct prestera_msg_rif_req req; int err; memcpy(req.mac, mac, ETH_ALEN); err = prestera_iface_to_msg(iif, &req.iif); if (err) return err; err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_ROUTER_RIF_CREATE, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; *rif_id = __le16_to_cpu(resp.rif_id); return err; } int prestera_hw_rif_delete(struct prestera_switch *sw, u16 rif_id, struct prestera_iface *iif) { struct prestera_msg_rif_req req = { .rif_id = __cpu_to_le16(rif_id), }; int err; err = prestera_iface_to_msg(iif, &req.iif); if (err) return err; return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_RIF_DELETE, &req.cmd, sizeof(req)); } int prestera_hw_vr_create(struct prestera_switch *sw, u16 *vr_id) { struct prestera_msg_vr_resp resp; struct prestera_msg_vr_req req; int err; err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_ROUTER_VR_CREATE, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; *vr_id = __le16_to_cpu(resp.vr_id); return err; } int prestera_hw_vr_delete(struct prestera_switch *sw, u16 vr_id) { struct prestera_msg_vr_req req = { .vr_id = __cpu_to_le16(vr_id), }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_VR_DELETE, &req.cmd, sizeof(req)); } int prestera_hw_lpm_add(struct prestera_switch *sw, u16 vr_id, __be32 dst, u32 dst_len, u32 grp_id) { struct prestera_msg_lpm_req req = { .dst_len = __cpu_to_le32(dst_len), .vr_id = __cpu_to_le16(vr_id), .grp_id = __cpu_to_le32(grp_id), .dst.u.ipv4 = dst }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_LPM_ADD, &req.cmd, sizeof(req)); } int prestera_hw_lpm_del(struct prestera_switch *sw, u16 vr_id, __be32 dst, u32 dst_len) { struct prestera_msg_lpm_req req = { .dst_len = __cpu_to_le32(dst_len), .vr_id = __cpu_to_le16(vr_id), .dst.u.ipv4 = dst }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_LPM_DELETE, &req.cmd, sizeof(req)); } int prestera_hw_nh_entries_set(struct prestera_switch *sw, int count, struct prestera_neigh_info *nhs, u32 grp_id) { struct prestera_msg_nh_req req = { .size = __cpu_to_le32((u32)count), .grp_id = __cpu_to_le32(grp_id) }; int i, err; for (i = 0; i < count; i++) { req.nh[i].is_active = nhs[i].connected; memcpy(&req.nh[i].mac, nhs[i].ha, ETH_ALEN); err = prestera_iface_to_msg(&nhs[i].iface, &req.nh[i].oif); if (err) return err; } return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_SET, &req.cmd, sizeof(req)); } int prestera_hw_nhgrp_blk_get(struct prestera_switch *sw, u8 *hw_state, u32 buf_size /* Buffer in bytes */) { static struct prestera_msg_nh_chunk_resp resp; struct prestera_msg_nh_chunk_req req; u32 buf_offset; int err; memset(&hw_state[0], 0, buf_size); buf_offset = 0; while (1) { if (buf_offset >= buf_size) break; memset(&req, 0, sizeof(req)); req.offset = __cpu_to_le32(buf_offset * 8); /* 8 bits in u8 */ err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_BLK_GET, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; memcpy(&hw_state[buf_offset], &resp.hw_state[0], buf_offset + PRESTERA_MSG_CHUNK_SIZE > buf_size ? buf_size - buf_offset : PRESTERA_MSG_CHUNK_SIZE); buf_offset += PRESTERA_MSG_CHUNK_SIZE; } return 0; } int prestera_hw_nh_group_create(struct prestera_switch *sw, u16 nh_count, u32 *grp_id) { struct prestera_msg_nh_grp_req req = { .size = __cpu_to_le32((u32)nh_count) }; struct prestera_msg_nh_grp_resp resp; int err; err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_ADD, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; *grp_id = __le32_to_cpu(resp.grp_id); return err; } int prestera_hw_nh_group_delete(struct prestera_switch *sw, u16 nh_count, u32 grp_id) { struct prestera_msg_nh_grp_req req = { .grp_id = __cpu_to_le32(grp_id), .size = __cpu_to_le32(nh_count) }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_DELETE, &req.cmd, sizeof(req)); } int prestera_hw_rxtx_init(struct prestera_switch *sw, struct prestera_rxtx_params *params) { struct prestera_msg_rxtx_resp resp; struct prestera_msg_rxtx_req req; int err; req.use_sdma = params->use_sdma; err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_RXTX_INIT, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; params->map_addr = __le32_to_cpu(resp.map_addr); return 0; } int prestera_hw_lag_member_add(struct prestera_port *port, u16 lag_id) { struct prestera_msg_lag_req req = { .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .lag_id = __cpu_to_le16(lag_id), }; return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_LAG_MEMBER_ADD, &req.cmd, sizeof(req)); } int prestera_hw_lag_member_del(struct prestera_port *port, u16 lag_id) { struct prestera_msg_lag_req req = { .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .lag_id = __cpu_to_le16(lag_id), }; return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_LAG_MEMBER_DELETE, &req.cmd, sizeof(req)); } int prestera_hw_lag_member_enable(struct prestera_port *port, u16 lag_id, bool enable) { struct prestera_msg_lag_req req = { .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .lag_id = __cpu_to_le16(lag_id), }; u32 cmd; cmd = enable ? PRESTERA_CMD_TYPE_LAG_MEMBER_ENABLE : PRESTERA_CMD_TYPE_LAG_MEMBER_DISABLE; return prestera_cmd(port->sw, cmd, &req.cmd, sizeof(req)); } int prestera_hw_cpu_code_counters_get(struct prestera_switch *sw, u8 code, enum prestera_hw_cpu_code_cnt_t counter_type, u64 *packet_count) { struct prestera_msg_cpu_code_counter_req req = { .counter_type = counter_type, .code = code, }; struct mvsw_msg_cpu_code_counter_ret resp; int err; err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_CPU_CODE_COUNTERS_GET, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; *packet_count = __le64_to_cpu(resp.packet_count); return 0; } int prestera_hw_event_handler_register(struct prestera_switch *sw, enum prestera_event_type type, prestera_event_cb_t fn, void *arg) { struct prestera_fw_event_handler *eh; eh = __find_event_handler(sw, type); if (eh) return -EEXIST; eh = kmalloc(sizeof(*eh), GFP_KERNEL); if (!eh) return -ENOMEM; eh->type = type; eh->func = fn; eh->arg = arg; INIT_LIST_HEAD(&eh->list); list_add_rcu(&eh->list, &sw->event_handlers); return 0; } void prestera_hw_event_handler_unregister(struct prestera_switch *sw, enum prestera_event_type type, prestera_event_cb_t fn) { struct prestera_fw_event_handler *eh; eh = __find_event_handler(sw, type); if (!eh) return; list_del_rcu(&eh->list); kfree_rcu(eh, rcu); } int prestera_hw_counter_trigger(struct prestera_switch *sw, u32 block_id) { struct prestera_msg_counter_req req = { .block_id = __cpu_to_le32(block_id) }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_COUNTER_TRIGGER, &req.cmd, sizeof(req)); } int prestera_hw_counter_abort(struct prestera_switch *sw) { struct prestera_msg_counter_req req; return prestera_cmd(sw, PRESTERA_CMD_TYPE_COUNTER_ABORT, &req.cmd, sizeof(req)); } int prestera_hw_counters_get(struct prestera_switch *sw, u32 idx, u32 *len, bool *done, struct prestera_counter_stats *stats) { struct prestera_msg_counter_resp *resp; struct prestera_msg_counter_req req = { .block_id = __cpu_to_le32(idx), .num_counters = __cpu_to_le32(*len), }; size_t size = struct_size(resp, stats, *len); int err, i; resp = kmalloc(size, GFP_KERNEL); if (!resp) return -ENOMEM; err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_COUNTER_GET, &req.cmd, sizeof(req), &resp->ret, size); if (err) goto free_buff; for (i = 0; i < __le32_to_cpu(resp->num_counters); i++) { stats[i].packets += __le64_to_cpu(resp->stats[i].packets); stats[i].bytes += __le64_to_cpu(resp->stats[i].bytes); } *len = __le32_to_cpu(resp->num_counters); *done = __le32_to_cpu(resp->done); free_buff: kfree(resp); return err; } int prestera_hw_counter_block_get(struct prestera_switch *sw, u32 client, u32 *block_id, u32 *offset, u32 *num_counters) { struct prestera_msg_counter_resp resp; struct prestera_msg_counter_req req = { .client = __cpu_to_le32(client) }; int err; err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_COUNTER_BLOCK_GET, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; *block_id = __le32_to_cpu(resp.block_id); *offset = __le32_to_cpu(resp.offset); *num_counters = __le32_to_cpu(resp.num_counters); return 0; } int prestera_hw_counter_block_release(struct prestera_switch *sw, u32 block_id) { struct prestera_msg_counter_req req = { .block_id = __cpu_to_le32(block_id) }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_COUNTER_BLOCK_RELEASE, &req.cmd, sizeof(req)); } int prestera_hw_counter_clear(struct prestera_switch *sw, u32 block_id, u32 counter_id) { struct prestera_msg_counter_req req = { .block_id = __cpu_to_le32(block_id), .num_counters = __cpu_to_le32(counter_id) }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_COUNTER_CLEAR, &req.cmd, sizeof(req)); } int prestera_hw_policer_create(struct prestera_switch *sw, u8 type, u32 *policer_id) { struct prestera_msg_policer_resp resp; struct prestera_msg_policer_req req = { .type = type }; int err; err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_POLICER_CREATE, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; *policer_id = __le32_to_cpu(resp.id); return 0; } int prestera_hw_policer_release(struct prestera_switch *sw, u32 policer_id) { struct prestera_msg_policer_req req = { .id = __cpu_to_le32(policer_id) }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_POLICER_RELEASE, &req.cmd, sizeof(req)); } int prestera_hw_policer_sr_tcm_set(struct prestera_switch *sw, u32 policer_id, u64 cir, u32 cbs) { struct prestera_msg_policer_req req = { .mode = PRESTERA_POLICER_MODE_SR_TCM, .id = __cpu_to_le32(policer_id), .sr_tcm = { .cir = __cpu_to_le64(cir), .cbs = __cpu_to_le32(cbs) } }; return prestera_cmd(sw, PRESTERA_CMD_TYPE_POLICER_SET, &req.cmd, sizeof(req)); } int prestera_hw_flood_domain_create(struct prestera_flood_domain *domain) { struct prestera_msg_flood_domain_create_resp resp; struct prestera_msg_flood_domain_create_req req; int err; err = prestera_cmd_ret(domain->sw, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_CREATE, &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); if (err) return err; domain->idx = __le32_to_cpu(resp.flood_domain_idx); return 0; } int prestera_hw_flood_domain_destroy(struct prestera_flood_domain *domain) { struct prestera_msg_flood_domain_destroy_req req = { .flood_domain_idx = __cpu_to_le32(domain->idx), }; return prestera_cmd(domain->sw, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_DESTROY, &req.cmd, sizeof(req)); } int prestera_hw_flood_domain_ports_set(struct prestera_flood_domain *domain) { struct prestera_flood_domain_port *flood_domain_port; struct prestera_msg_flood_domain_ports_set_req *req; struct prestera_switch *sw = domain->sw; struct prestera_port *port; u32 ports_num = 0; size_t buf_size; u16 lag_id; int err; int i = 0; list_for_each_entry(flood_domain_port, &domain->flood_domain_port_list, flood_domain_port_node) ports_num++; if (!ports_num) return -EINVAL; buf_size = struct_size(req, ports, ports_num); req = kmalloc(buf_size, GFP_KERNEL); if (!req) return -ENOMEM; req->flood_domain_idx = __cpu_to_le32(domain->idx); req->ports_num = __cpu_to_le32(ports_num); list_for_each_entry(flood_domain_port, &domain->flood_domain_port_list, flood_domain_port_node) { if (netif_is_lag_master(flood_domain_port->dev)) { if (prestera_lag_id(sw, flood_domain_port->dev, &lag_id)) { kfree(req); return -EINVAL; } req->ports[i].port_type = __cpu_to_le16(PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_LAG); req->ports[i].lag_id = __cpu_to_le16(lag_id); } else { port = prestera_port_dev_lower_find(flood_domain_port->dev); req->ports[i].port_type = __cpu_to_le16(PRESTERA_HW_FDB_ENTRY_TYPE_REG_PORT); req->ports[i].dev_num = __cpu_to_le32(port->dev_id); req->ports[i].port_num = __cpu_to_le32(port->hw_id); } req->ports[i].vid = __cpu_to_le16(flood_domain_port->vid); i++; } err = prestera_cmd(sw, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_SET, &req->cmd, buf_size); kfree(req); return err; } int prestera_hw_flood_domain_ports_reset(struct prestera_flood_domain *domain) { struct prestera_msg_flood_domain_ports_reset_req req = { .flood_domain_idx = __cpu_to_le32(domain->idx), }; return prestera_cmd(domain->sw, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_RESET, &req.cmd, sizeof(req)); } int prestera_hw_mdb_create(struct prestera_mdb_entry *mdb) { struct prestera_msg_mdb_create_req req = { .flood_domain_idx = __cpu_to_le32(mdb->flood_domain->idx), .vid = __cpu_to_le16(mdb->vid), }; memcpy(req.mac, mdb->addr, ETH_ALEN); return prestera_cmd(mdb->sw, PRESTERA_CMD_TYPE_MDB_CREATE, &req.cmd, sizeof(req)); } int prestera_hw_mdb_destroy(struct prestera_mdb_entry *mdb) { struct prestera_msg_mdb_destroy_req req = { .flood_domain_idx = __cpu_to_le32(mdb->flood_domain->idx), .vid = __cpu_to_le16(mdb->vid), }; memcpy(req.mac, mdb->addr, ETH_ALEN); return prestera_cmd(mdb->sw, PRESTERA_CMD_TYPE_MDB_DESTROY, &req.cmd, sizeof(req)); }