// SPDX-License-Identifier: GPL-2.0+ #include "lan966x_main.h" #include "lan966x_vcap_ag_api.h" #include "vcap_api.h" #include "vcap_api_client.h" #include "vcap_api_debugfs.h" #define STREAMSIZE (64 * 4) #define LAN966X_IS1_LOOKUPS 3 #define LAN966X_IS2_LOOKUPS 2 #define LAN966X_ES0_LOOKUPS 1 #define LAN966X_STAT_ESDX_GRN_BYTES 0x300 #define LAN966X_STAT_ESDX_GRN_PKTS 0x301 #define LAN966X_STAT_ESDX_YEL_BYTES 0x302 #define LAN966X_STAT_ESDX_YEL_PKTS 0x303 static struct lan966x_vcap_inst { enum vcap_type vtype; /* type of vcap */ int tgt_inst; /* hardware instance number */ int lookups; /* number of lookups in this vcap type */ int first_cid; /* first chain id in this vcap */ int last_cid; /* last chain id in this vcap */ int count; /* number of available addresses */ bool ingress; /* is vcap in the ingress path */ } lan966x_vcap_inst_cfg[] = { { .vtype = VCAP_TYPE_ES0, .tgt_inst = 0, .lookups = LAN966X_ES0_LOOKUPS, .first_cid = LAN966X_VCAP_CID_ES0_L0, .last_cid = LAN966X_VCAP_CID_ES0_MAX, .count = 64, }, { .vtype = VCAP_TYPE_IS1, /* IS1-0 */ .tgt_inst = 1, .lookups = LAN966X_IS1_LOOKUPS, .first_cid = LAN966X_VCAP_CID_IS1_L0, .last_cid = LAN966X_VCAP_CID_IS1_MAX, .count = 768, .ingress = true, }, { .vtype = VCAP_TYPE_IS2, /* IS2-0 */ .tgt_inst = 2, .lookups = LAN966X_IS2_LOOKUPS, .first_cid = LAN966X_VCAP_CID_IS2_L0, .last_cid = LAN966X_VCAP_CID_IS2_MAX, .count = 256, .ingress = true, }, }; struct lan966x_vcap_cmd_cb { struct lan966x *lan966x; u32 instance; }; static u32 lan966x_vcap_read_update_ctrl(const struct lan966x_vcap_cmd_cb *cb) { return lan_rd(cb->lan966x, VCAP_UPDATE_CTRL(cb->instance)); } static void lan966x_vcap_wait_update(struct lan966x *lan966x, int instance) { const struct lan966x_vcap_cmd_cb cb = { .lan966x = lan966x, .instance = instance }; u32 val; readx_poll_timeout(lan966x_vcap_read_update_ctrl, &cb, val, (val & VCAP_UPDATE_CTRL_UPDATE_SHOT) == 0, 10, 100000); } static void __lan966x_vcap_range_init(struct lan966x *lan966x, struct vcap_admin *admin, u32 addr, u32 count) { lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(0) | VCAP_MV_CFG_MV_SIZE_SET(count - 1), lan966x, VCAP_MV_CFG(admin->tgt_inst)); lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(VCAP_CMD_INITIALIZE) | VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(0) | VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(0) | VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(0) | VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(true) | VCAP_UPDATE_CTRL_UPDATE_SHOT_SET(1), lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); lan966x_vcap_wait_update(lan966x, admin->tgt_inst); } static int lan966x_vcap_is1_cid_to_lookup(int cid) { int lookup = 0; if (cid >= LAN966X_VCAP_CID_IS1_L1 && cid < LAN966X_VCAP_CID_IS1_L2) lookup = 1; else if (cid >= LAN966X_VCAP_CID_IS1_L2 && cid < LAN966X_VCAP_CID_IS1_MAX) lookup = 2; return lookup; } static int lan966x_vcap_is2_cid_to_lookup(int cid) { if (cid >= LAN966X_VCAP_CID_IS2_L1 && cid < LAN966X_VCAP_CID_IS2_MAX) return 1; return 0; } /* Return the list of keysets for the vcap port configuration */ static int lan966x_vcap_is1_get_port_keysets(struct net_device *ndev, int lookup, struct vcap_keyset_list *keysetlist, u16 l3_proto) { struct lan966x_port *port = netdev_priv(ndev); struct lan966x *lan966x = port->lan966x; u32 val; val = lan_rd(lan966x, ANA_VCAP_S1_CFG(port->chip_port, lookup)); /* Collect all keysets for the port in a list */ if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) { switch (ANA_VCAP_S1_CFG_KEY_IP4_CFG_GET(val)) { case VCAP_IS1_PS_IPV4_7TUPLE: vcap_keyset_list_add(keysetlist, VCAP_KFS_7TUPLE); break; case VCAP_IS1_PS_IPV4_5TUPLE_IP4: vcap_keyset_list_add(keysetlist, VCAP_KFS_5TUPLE_IP4); break; case VCAP_IS1_PS_IPV4_NORMAL: vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL); break; } } if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) { switch (ANA_VCAP_S1_CFG_KEY_IP6_CFG_GET(val)) { case VCAP_IS1_PS_IPV6_NORMAL: case VCAP_IS1_PS_IPV6_NORMAL_IP6: vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL); vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL_IP6); break; case VCAP_IS1_PS_IPV6_5TUPLE_IP6: vcap_keyset_list_add(keysetlist, VCAP_KFS_5TUPLE_IP6); break; case VCAP_IS1_PS_IPV6_7TUPLE: vcap_keyset_list_add(keysetlist, VCAP_KFS_7TUPLE); break; case VCAP_IS1_PS_IPV6_5TUPLE_IP4: vcap_keyset_list_add(keysetlist, VCAP_KFS_5TUPLE_IP4); break; case VCAP_IS1_PS_IPV6_DMAC_VID: vcap_keyset_list_add(keysetlist, VCAP_KFS_DMAC_VID); break; } } switch (ANA_VCAP_S1_CFG_KEY_OTHER_CFG_GET(val)) { case VCAP_IS1_PS_OTHER_7TUPLE: vcap_keyset_list_add(keysetlist, VCAP_KFS_7TUPLE); break; case VCAP_IS1_PS_OTHER_NORMAL: vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL); break; } return 0; } static int lan966x_vcap_is2_get_port_keysets(struct net_device *dev, int lookup, struct vcap_keyset_list *keysetlist, u16 l3_proto) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; bool found = false; u32 val; val = lan_rd(lan966x, ANA_VCAP_S2_CFG(port->chip_port)); /* Collect all keysets for the port in a list */ if (l3_proto == ETH_P_ALL) vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_SNAP) { if (ANA_VCAP_S2_CFG_SNAP_DIS_GET(val) & (BIT(0) << lookup)) vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_LLC); else vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_SNAP); found = true; } if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_CFM) { if (ANA_VCAP_S2_CFG_OAM_DIS_GET(val) & (BIT(0) << lookup)) vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); else vcap_keyset_list_add(keysetlist, VCAP_KFS_OAM); found = true; } if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_ARP) { if (ANA_VCAP_S2_CFG_ARP_DIS_GET(val) & (BIT(0) << lookup)) vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); else vcap_keyset_list_add(keysetlist, VCAP_KFS_ARP); found = true; } if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) { if (ANA_VCAP_S2_CFG_IP_OTHER_DIS_GET(val) & (BIT(0) << lookup)) vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); else vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); if (ANA_VCAP_S2_CFG_IP_TCPUDP_DIS_GET(val) & (BIT(0) << lookup)) vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); else vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); found = true; } if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) { switch (ANA_VCAP_S2_CFG_IP6_CFG_GET(val) & (0x3 << lookup)) { case VCAP_IS2_PS_IPV6_TCPUDP_OTHER: vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_OTHER); vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_TCP_UDP); break; case VCAP_IS2_PS_IPV6_STD: vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD); break; case VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER: vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); break; case VCAP_IS2_PS_IPV6_MAC_ETYPE: vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); break; } found = true; } if (!found) vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); return 0; } static enum vcap_keyfield_set lan966x_vcap_validate_keyset(struct net_device *dev, struct vcap_admin *admin, struct vcap_rule *rule, struct vcap_keyset_list *kslist, u16 l3_proto) { struct vcap_keyset_list keysetlist = {}; enum vcap_keyfield_set keysets[10] = {}; int lookup; int err; if (!kslist || kslist->cnt == 0) return VCAP_KFS_NO_VALUE; keysetlist.max = ARRAY_SIZE(keysets); keysetlist.keysets = keysets; switch (admin->vtype) { case VCAP_TYPE_IS1: lookup = lan966x_vcap_is1_cid_to_lookup(rule->vcap_chain_id); err = lan966x_vcap_is1_get_port_keysets(dev, lookup, &keysetlist, l3_proto); break; case VCAP_TYPE_IS2: lookup = lan966x_vcap_is2_cid_to_lookup(rule->vcap_chain_id); err = lan966x_vcap_is2_get_port_keysets(dev, lookup, &keysetlist, l3_proto); break; case VCAP_TYPE_ES0: return kslist->keysets[0]; default: pr_err("vcap type: %s not supported\n", lan966x_vcaps[admin->vtype].name); return VCAP_KFS_NO_VALUE; } if (err) return VCAP_KFS_NO_VALUE; /* Check if there is a match and return the match */ for (int i = 0; i < kslist->cnt; ++i) for (int j = 0; j < keysetlist.cnt; ++j) if (kslist->keysets[i] == keysets[j]) return kslist->keysets[i]; return VCAP_KFS_NO_VALUE; } static bool lan966x_vcap_is2_is_first_chain(struct vcap_rule *rule) { return (rule->vcap_chain_id >= LAN966X_VCAP_CID_IS2_L0 && rule->vcap_chain_id < LAN966X_VCAP_CID_IS2_L1); } static void lan966x_vcap_is1_add_default_fields(struct lan966x_port *port, struct vcap_admin *admin, struct vcap_rule *rule) { u32 value, mask; u32 lookup; if (vcap_rule_get_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, &value, &mask)) vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, ~BIT(port->chip_port)); lookup = lan966x_vcap_is1_cid_to_lookup(rule->vcap_chain_id); vcap_rule_add_key_u32(rule, VCAP_KF_LOOKUP_INDEX, lookup, 0x3); } static void lan966x_vcap_is2_add_default_fields(struct lan966x_port *port, struct vcap_admin *admin, struct vcap_rule *rule) { u32 value, mask; if (vcap_rule_get_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, &value, &mask)) vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, ~BIT(port->chip_port)); if (lan966x_vcap_is2_is_first_chain(rule)) vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1); else vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_0); } static void lan966x_vcap_es0_add_default_fields(struct lan966x_port *port, struct vcap_admin *admin, struct vcap_rule *rule) { vcap_rule_add_key_u32(rule, VCAP_KF_IF_EGR_PORT_NO, port->chip_port, GENMASK(4, 0)); } static void lan966x_vcap_add_default_fields(struct net_device *dev, struct vcap_admin *admin, struct vcap_rule *rule) { struct lan966x_port *port = netdev_priv(dev); switch (admin->vtype) { case VCAP_TYPE_IS1: lan966x_vcap_is1_add_default_fields(port, admin, rule); break; case VCAP_TYPE_IS2: lan966x_vcap_is2_add_default_fields(port, admin, rule); break; case VCAP_TYPE_ES0: lan966x_vcap_es0_add_default_fields(port, admin, rule); break; default: pr_err("vcap type: %s not supported\n", lan966x_vcaps[admin->vtype].name); break; } } static void lan966x_vcap_cache_erase(struct vcap_admin *admin) { memset(admin->cache.keystream, 0, STREAMSIZE); memset(admin->cache.maskstream, 0, STREAMSIZE); memset(admin->cache.actionstream, 0, STREAMSIZE); memset(&admin->cache.counter, 0, sizeof(admin->cache.counter)); } /* The ESDX counter is only used/incremented if the frame has been classified * with an ISDX > 0 (e.g by a rule in IS0). This is not mentioned in the * datasheet. */ static void lan966x_es0_read_esdx_counter(struct lan966x *lan966x, struct vcap_admin *admin, u32 id) { u32 counter; id = id & 0xff; /* counter limit */ mutex_lock(&lan966x->stats_lock); lan_wr(SYS_STAT_CFG_STAT_VIEW_SET(id), lan966x, SYS_STAT_CFG); counter = lan_rd(lan966x, SYS_CNT(LAN966X_STAT_ESDX_GRN_PKTS)) + lan_rd(lan966x, SYS_CNT(LAN966X_STAT_ESDX_YEL_PKTS)); mutex_unlock(&lan966x->stats_lock); if (counter) admin->cache.counter = counter; } static void lan966x_es0_write_esdx_counter(struct lan966x *lan966x, struct vcap_admin *admin, u32 id) { id = id & 0xff; /* counter limit */ mutex_lock(&lan966x->stats_lock); lan_wr(SYS_STAT_CFG_STAT_VIEW_SET(id), lan966x, SYS_STAT_CFG); lan_wr(0, lan966x, SYS_CNT(LAN966X_STAT_ESDX_GRN_BYTES)); lan_wr(admin->cache.counter, lan966x, SYS_CNT(LAN966X_STAT_ESDX_GRN_PKTS)); lan_wr(0, lan966x, SYS_CNT(LAN966X_STAT_ESDX_YEL_BYTES)); lan_wr(0, lan966x, SYS_CNT(LAN966X_STAT_ESDX_YEL_PKTS)); mutex_unlock(&lan966x->stats_lock); } static void lan966x_vcap_cache_write(struct net_device *dev, struct vcap_admin *admin, enum vcap_selection sel, u32 start, u32 count) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; u32 *keystr, *mskstr, *actstr; keystr = &admin->cache.keystream[start]; mskstr = &admin->cache.maskstream[start]; actstr = &admin->cache.actionstream[start]; switch (sel) { case VCAP_SEL_ENTRY: for (int i = 0; i < count; ++i) { lan_wr(keystr[i] & mskstr[i], lan966x, VCAP_ENTRY_DAT(admin->tgt_inst, i)); lan_wr(~mskstr[i], lan966x, VCAP_MASK_DAT(admin->tgt_inst, i)); } break; case VCAP_SEL_ACTION: for (int i = 0; i < count; ++i) lan_wr(actstr[i], lan966x, VCAP_ACTION_DAT(admin->tgt_inst, i)); break; case VCAP_SEL_COUNTER: admin->cache.sticky = admin->cache.counter > 0; lan_wr(admin->cache.counter, lan966x, VCAP_CNT_DAT(admin->tgt_inst, 0)); if (admin->vtype == VCAP_TYPE_ES0) lan966x_es0_write_esdx_counter(lan966x, admin, start); break; default: break; } } static void lan966x_vcap_cache_read(struct net_device *dev, struct vcap_admin *admin, enum vcap_selection sel, u32 start, u32 count) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; int instance = admin->tgt_inst; u32 *keystr, *mskstr, *actstr; keystr = &admin->cache.keystream[start]; mskstr = &admin->cache.maskstream[start]; actstr = &admin->cache.actionstream[start]; if (sel & VCAP_SEL_ENTRY) { for (int i = 0; i < count; ++i) { keystr[i] = lan_rd(lan966x, VCAP_ENTRY_DAT(instance, i)); mskstr[i] = ~lan_rd(lan966x, VCAP_MASK_DAT(instance, i)); } } if (sel & VCAP_SEL_ACTION) for (int i = 0; i < count; ++i) actstr[i] = lan_rd(lan966x, VCAP_ACTION_DAT(instance, i)); if (sel & VCAP_SEL_COUNTER) { admin->cache.counter = lan_rd(lan966x, VCAP_CNT_DAT(instance, 0)); admin->cache.sticky = admin->cache.counter > 0; if (admin->vtype == VCAP_TYPE_ES0) lan966x_es0_read_esdx_counter(lan966x, admin, start); } } static void lan966x_vcap_range_init(struct net_device *dev, struct vcap_admin *admin, u32 addr, u32 count) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; __lan966x_vcap_range_init(lan966x, admin, addr, count); } static void lan966x_vcap_update(struct net_device *dev, struct vcap_admin *admin, enum vcap_command cmd, enum vcap_selection sel, u32 addr) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; bool clear; clear = (cmd == VCAP_CMD_INITIALIZE); lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(0) | VCAP_MV_CFG_MV_SIZE_SET(0), lan966x, VCAP_MV_CFG(admin->tgt_inst)); lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) | VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET((VCAP_SEL_ENTRY & sel) == 0) | VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET((VCAP_SEL_ACTION & sel) == 0) | VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET((VCAP_SEL_COUNTER & sel) == 0) | VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(clear) | VCAP_UPDATE_CTRL_UPDATE_SHOT, lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); lan966x_vcap_wait_update(lan966x, admin->tgt_inst); } static void lan966x_vcap_move(struct net_device *dev, struct vcap_admin *admin, u32 addr, int offset, int count) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; enum vcap_command cmd; u16 mv_num_pos; u16 mv_size; mv_size = count - 1; if (offset > 0) { mv_num_pos = offset - 1; cmd = VCAP_CMD_MOVE_DOWN; } else { mv_num_pos = -offset - 1; cmd = VCAP_CMD_MOVE_UP; } lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(mv_num_pos) | VCAP_MV_CFG_MV_SIZE_SET(mv_size), lan966x, VCAP_MV_CFG(admin->tgt_inst)); lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) | VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(0) | VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(0) | VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(0) | VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(false) | VCAP_UPDATE_CTRL_UPDATE_SHOT, lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); lan966x_vcap_wait_update(lan966x, admin->tgt_inst); } static struct vcap_operations lan966x_vcap_ops = { .validate_keyset = lan966x_vcap_validate_keyset, .add_default_fields = lan966x_vcap_add_default_fields, .cache_erase = lan966x_vcap_cache_erase, .cache_write = lan966x_vcap_cache_write, .cache_read = lan966x_vcap_cache_read, .init = lan966x_vcap_range_init, .update = lan966x_vcap_update, .move = lan966x_vcap_move, .port_info = lan966x_vcap_port_info, }; static void lan966x_vcap_admin_free(struct vcap_admin *admin) { if (!admin) return; kfree(admin->cache.keystream); kfree(admin->cache.maskstream); kfree(admin->cache.actionstream); mutex_destroy(&admin->lock); kfree(admin); } static struct vcap_admin * lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl, const struct lan966x_vcap_inst *cfg) { struct vcap_admin *admin; admin = kzalloc(sizeof(*admin), GFP_KERNEL); if (!admin) return ERR_PTR(-ENOMEM); mutex_init(&admin->lock); INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); admin->vtype = cfg->vtype; admin->vinst = 0; admin->ingress = cfg->ingress; admin->w32be = true; admin->tgt_inst = cfg->tgt_inst; admin->lookups = cfg->lookups; admin->lookups_per_instance = cfg->lookups; admin->first_cid = cfg->first_cid; admin->last_cid = cfg->last_cid; admin->cache.keystream = kzalloc(STREAMSIZE, GFP_KERNEL); admin->cache.maskstream = kzalloc(STREAMSIZE, GFP_KERNEL); admin->cache.actionstream = kzalloc(STREAMSIZE, GFP_KERNEL); if (!admin->cache.keystream || !admin->cache.maskstream || !admin->cache.actionstream) { lan966x_vcap_admin_free(admin); return ERR_PTR(-ENOMEM); } return admin; } static void lan966x_vcap_block_init(struct lan966x *lan966x, struct vcap_admin *admin, struct lan966x_vcap_inst *cfg) { admin->first_valid_addr = 0; admin->last_used_addr = cfg->count; admin->last_valid_addr = cfg->count - 1; lan_wr(VCAP_CORE_IDX_CORE_IDX_SET(0), lan966x, VCAP_CORE_IDX(admin->tgt_inst)); lan_wr(VCAP_CORE_MAP_CORE_MAP_SET(1), lan966x, VCAP_CORE_MAP(admin->tgt_inst)); __lan966x_vcap_range_init(lan966x, admin, admin->first_valid_addr, admin->last_valid_addr - admin->first_valid_addr); } static void lan966x_vcap_port_key_deselection(struct lan966x *lan966x, struct vcap_admin *admin) { u32 val; switch (admin->vtype) { case VCAP_TYPE_IS1: val = ANA_VCAP_S1_CFG_KEY_IP6_CFG_SET(VCAP_IS1_PS_IPV6_5TUPLE_IP6) | ANA_VCAP_S1_CFG_KEY_IP4_CFG_SET(VCAP_IS1_PS_IPV4_5TUPLE_IP4) | ANA_VCAP_S1_CFG_KEY_OTHER_CFG_SET(VCAP_IS1_PS_OTHER_NORMAL); for (int p = 0; p < lan966x->num_phys_ports; ++p) { if (!lan966x->ports[p]) continue; for (int l = 0; l < LAN966X_IS1_LOOKUPS; ++l) lan_wr(val, lan966x, ANA_VCAP_S1_CFG(p, l)); lan_rmw(ANA_VCAP_CFG_S1_ENA_SET(true), ANA_VCAP_CFG_S1_ENA, lan966x, ANA_VCAP_CFG(p)); } break; case VCAP_TYPE_IS2: for (int p = 0; p < lan966x->num_phys_ports; ++p) lan_wr(0, lan966x, ANA_VCAP_S2_CFG(p)); break; case VCAP_TYPE_ES0: for (int p = 0; p < lan966x->num_phys_ports; ++p) lan_rmw(REW_PORT_CFG_ES0_EN_SET(false), REW_PORT_CFG_ES0_EN, lan966x, REW_PORT_CFG(p)); break; default: pr_err("vcap type: %s not supported\n", lan966x_vcaps[admin->vtype].name); break; } } int lan966x_vcap_init(struct lan966x *lan966x) { struct lan966x_vcap_inst *cfg; struct vcap_control *ctrl; struct vcap_admin *admin; struct dentry *dir; ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); if (!ctrl) return -ENOMEM; ctrl->vcaps = lan966x_vcaps; ctrl->stats = &lan966x_vcap_stats; ctrl->ops = &lan966x_vcap_ops; INIT_LIST_HEAD(&ctrl->list); for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) { cfg = &lan966x_vcap_inst_cfg[i]; admin = lan966x_vcap_admin_alloc(lan966x, ctrl, cfg); if (IS_ERR(admin)) return PTR_ERR(admin); lan966x_vcap_block_init(lan966x, admin, cfg); lan966x_vcap_port_key_deselection(lan966x, admin); list_add_tail(&admin->list, &ctrl->list); } dir = vcap_debugfs(lan966x->dev, lan966x->debugfs_root, ctrl); for (int p = 0; p < lan966x->num_phys_ports; ++p) { if (lan966x->ports[p]) { vcap_port_debugfs(lan966x->dev, dir, ctrl, lan966x->ports[p]->dev); lan_rmw(ANA_VCAP_S2_CFG_ENA_SET(true), ANA_VCAP_S2_CFG_ENA, lan966x, ANA_VCAP_S2_CFG(lan966x->ports[p]->chip_port)); lan_rmw(ANA_VCAP_CFG_S1_ENA_SET(true), ANA_VCAP_CFG_S1_ENA, lan966x, ANA_VCAP_CFG(lan966x->ports[p]->chip_port)); lan_rmw(REW_PORT_CFG_ES0_EN_SET(true), REW_PORT_CFG_ES0_EN, lan966x, REW_PORT_CFG(lan966x->ports[p]->chip_port)); } } /* Statistics: Use ESDX from ES0 if hit, otherwise no counting */ lan_rmw(REW_STAT_CFG_STAT_MODE_SET(1), REW_STAT_CFG_STAT_MODE, lan966x, REW_STAT_CFG); lan966x->vcap_ctrl = ctrl; return 0; } void lan966x_vcap_deinit(struct lan966x *lan966x) { struct vcap_admin *admin, *admin_next; struct vcap_control *ctrl; ctrl = lan966x->vcap_ctrl; if (!ctrl) return; list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) { lan966x_vcap_port_key_deselection(lan966x, admin); vcap_del_rules(ctrl, admin); list_del(&admin->list); lan966x_vcap_admin_free(admin); } kfree(ctrl); }