| // 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); |
| } |