static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco,
                                    struct vcap_admin *admin,
-                                   struct vcap_rule *vrule)
+                                   struct vcap_rule *vrule,
+                                   u16 *l3_proto)
 {
        struct sparx5_tc_flower_parse_usage state = {
                .fco = fco,
                .vrule = vrule,
+               .l3_proto = ETH_P_ALL,
        };
        int idx, err = 0;
 
                if (err)
                        return err;
        }
+
+       if (state.frule->match.dissector->used_keys ^ state.used_keys) {
+               NL_SET_ERR_MSG_MOD(fco->common.extack,
+                                  "Unsupported match item");
+               return -ENOENT;
+       }
+
+       if (l3_proto)
+               *l3_proto = state.l3_proto;
        return err;
 }
 
        struct vcap_control *vctrl;
        struct flow_rule *frule;
        struct vcap_rule *vrule;
+       u16 l3_proto;
        int err, idx;
 
        vctrl = port->sparx5->vcap_ctrl;
                return PTR_ERR(vrule);
 
        vrule->cookie = fco->cookie;
-       sparx5_tc_use_dissectors(fco, admin, vrule);
+       sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto);
        frule = flow_cls_offload_flow_rule(fco);
        flow_action_for_each(idx, act, &frule->action) {
                switch (act->id) {
                        goto out;
                }
        }
-       /* For now the keyset is hardcoded */
-       err = vcap_set_rule_set_keyset(vrule, VCAP_KFS_MAC_ETYPE);
-       if (err) {
-               NL_SET_ERR_MSG_MOD(fco->common.extack,
-                                  "No matching port keyset for filter protocol and keys");
-               goto out;
-       }
-       err = vcap_val_rule(vrule, ETH_P_ALL);
+       /* provide the l3 protocol to guide the keyset selection */
+       err = vcap_val_rule(vrule, l3_proto);
        if (err) {
                vcap_set_tc_exterr(fco, vrule);
                goto out;
 
 {
        struct sparx5_port *port = netdev_priv(ndev);
 
-       return port->sparx5->vcap_ctrl->stats->keyfield_set_names[keyset];
+       return vcap_keyset_name(port->sparx5->vcap_ctrl, keyset);
 }
 
 /* Check if this is the first lookup of IS2 */
        vcap_rule_add_key_u72(rule, VCAP_KF_IF_IGR_PORT_MASK, &port_mask);
 }
 
+/* Convert chain id to vcap lookup id */
+static int sparx5_vcap_cid_to_lookup(int cid)
+{
+       int lookup = 0;
+
+       /* For now only handle IS2 */
+       if (cid >= SPARX5_VCAP_CID_IS2_L1 && cid < SPARX5_VCAP_CID_IS2_L2)
+               lookup = 1;
+       else if (cid >= SPARX5_VCAP_CID_IS2_L2 && cid < SPARX5_VCAP_CID_IS2_L3)
+               lookup = 2;
+       else if (cid >= SPARX5_VCAP_CID_IS2_L3 && cid < SPARX5_VCAP_CID_IS2_MAX)
+               lookup = 3;
+
+       return lookup;
+}
+
+/* Return the list of keysets for the vcap port configuration */
+static int sparx5_vcap_is2_get_port_keysets(struct net_device *ndev,
+                                           int lookup,
+                                           struct vcap_keyset_list *keysetlist,
+                                           u16 l3_proto)
+{
+       struct sparx5_port *port = netdev_priv(ndev);
+       struct sparx5 *sparx5 = port->sparx5;
+       int portno = port->portno;
+       u32 value;
+
+       /* Check if the port keyset selection is enabled */
+       value = spx5_rd(sparx5, ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup));
+       if (!ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_GET(value))
+               return -ENOENT;
+
+       /* Collect all keysets for the port in a list */
+       if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_ARP) {
+               switch (ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_GET(value)) {
+               case VCAP_IS2_PS_ARP_MAC_ETYPE:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+                       break;
+               case VCAP_IS2_PS_ARP_ARP:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_ARP);
+                       break;
+               }
+       }
+
+       if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) {
+               switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_GET(value)) {
+               case VCAP_IS2_PS_IPV4_UC_MAC_ETYPE:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+                       break;
+               case VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
+                       break;
+               case VCAP_IS2_PS_IPV4_UC_IP_7TUPLE:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE);
+                       break;
+               }
+
+               switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_GET(value)) {
+               case VCAP_IS2_PS_IPV4_MC_MAC_ETYPE:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+                       break;
+               case VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
+                       break;
+               case VCAP_IS2_PS_IPV4_MC_IP_7TUPLE:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE);
+                       break;
+               }
+       }
+
+       if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) {
+               switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_GET(value)) {
+               case VCAP_IS2_PS_IPV6_UC_MAC_ETYPE:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+                       break;
+               case VCAP_IS2_PS_IPV6_UC_IP_7TUPLE:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE);
+                       break;
+               case VCAP_IS2_PS_IPV6_UC_IP6_STD:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD);
+                       break;
+               case VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
+                       break;
+               }
+
+               switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_GET(value)) {
+               case VCAP_IS2_PS_IPV6_MC_MAC_ETYPE:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+                       break;
+               case VCAP_IS2_PS_IPV6_MC_IP_7TUPLE:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE);
+                       break;
+               case VCAP_IS2_PS_IPV6_MC_IP6_STD:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD);
+                       break;
+               case VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER:
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
+                       break;
+               case VCAP_IS2_PS_IPV6_MC_IP6_VID:
+                       /* Not used */
+                       break;
+               }
+       }
+
+       if (l3_proto != ETH_P_ARP && l3_proto != ETH_P_IP &&
+           l3_proto != ETH_P_IPV6) {
+               switch (ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_GET(value)) {
+               case VCAP_IS2_PS_NONETH_MAC_ETYPE:
+                       /* IS2 non-classified frames generate MAC_ETYPE */
+                       vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
+                       break;
+               }
+       }
+       return 0;
+}
+
 /* API callback used for validating a field keyset (check the port keysets) */
 static enum vcap_keyfield_set
 sparx5_vcap_validate_keyset(struct net_device *ndev,
                            struct vcap_keyset_list *kslist,
                            u16 l3_proto)
 {
+       struct vcap_keyset_list keysetlist = {};
+       enum vcap_keyfield_set keysets[10] = {};
+       int idx, jdx, lookup;
+
        if (!kslist || kslist->cnt == 0)
                return VCAP_KFS_NO_VALUE;
-       /* for now just return whatever the API suggests */
-       return kslist->keysets[0];
+
+       /* Get a list of currently configured keysets in the lookups */
+       lookup = sparx5_vcap_cid_to_lookup(rule->vcap_chain_id);
+       keysetlist.max = ARRAY_SIZE(keysets);
+       keysetlist.keysets = keysets;
+       sparx5_vcap_is2_get_port_keysets(ndev, lookup, &keysetlist, l3_proto);
+
+       /* Check if there is a match and return the match */
+       for (idx = 0; idx < kslist->cnt; ++idx)
+               for (jdx = 0; jdx < keysetlist.cnt; ++jdx)
+                       if (kslist->keysets[idx] == keysets[jdx])
+                               return kslist->keysets[idx];
+
+       pr_err("%s:%d: %s not supported in port key selection\n",
+              __func__, __LINE__,
+              sparx5_vcap_keyset_name(ndev, kslist->keysets[0]));
+
+       return -ENOENT;
 }
 
 /* API callback used for adding default fields to a rule */
 
        return 0;
 }
 
+/* Add a keyset to a keyset list */
+bool vcap_keyset_list_add(struct vcap_keyset_list *keysetlist,
+                         enum vcap_keyfield_set keyset)
+{
+       int idx;
+
+       if (keysetlist->cnt < keysetlist->max) {
+               /* Avoid duplicates */
+               for (idx = 0; idx < keysetlist->cnt; ++idx)
+                       if (keysetlist->keysets[idx] == keyset)
+                               return keysetlist->cnt < keysetlist->max;
+               keysetlist->keysets[keysetlist->cnt++] = keyset;
+       }
+       return keysetlist->cnt < keysetlist->max;
+}
+EXPORT_SYMBOL_GPL(vcap_keyset_list_add);
+
+/* map keyset id to a string with the keyset name */
+const char *vcap_keyset_name(struct vcap_control *vctrl,
+                            enum vcap_keyfield_set keyset)
+{
+       return vctrl->stats->keyfield_set_names[keyset];
+}
+EXPORT_SYMBOL_GPL(vcap_keyset_name);
+
+/* map key field id to a string with the key name */
+const char *vcap_keyfield_name(struct vcap_control *vctrl,
+                              enum vcap_key_field key)
+{
+       return vctrl->stats->keyfield_names[key];
+}
+EXPORT_SYMBOL_GPL(vcap_keyfield_name);
+
+/* Return the keyfield that matches a key in a keyset */
+static const struct vcap_field *
+vcap_find_keyset_keyfield(struct vcap_control *vctrl,
+                         enum vcap_type vtype,
+                         enum vcap_keyfield_set keyset,
+                         enum vcap_key_field key)
+{
+       const struct vcap_field *fields;
+       int idx, count;
+
+       fields = vcap_keyfields(vctrl, vtype, keyset);
+       if (!fields)
+               return NULL;
+
+       /* Iterate the keyfields of the keyset */
+       count = vcap_keyfield_count(vctrl, vtype, keyset);
+       for (idx = 0; idx < count; ++idx) {
+               if (fields[idx].width == 0)
+                       continue;
+
+               if (key == idx)
+                       return &fields[idx];
+       }
+
+       return NULL;
+}
+
+/* Match a list of keys against the keysets available in a vcap type */
+static bool vcap_rule_find_keysets(struct vcap_rule_internal *ri,
+                                  struct vcap_keyset_list *matches)
+{
+       const struct vcap_client_keyfield *ckf;
+       int keyset, found, keycount, map_size;
+       const struct vcap_field **map;
+       enum vcap_type vtype;
+
+       vtype = ri->admin->vtype;
+       map = ri->vctrl->vcaps[vtype].keyfield_set_map;
+       map_size = ri->vctrl->vcaps[vtype].keyfield_set_size;
+
+       /* Get a count of the keyfields we want to match */
+       keycount = 0;
+       list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list)
+               ++keycount;
+
+       matches->cnt = 0;
+       /* Iterate the keysets of the VCAP */
+       for (keyset = 0; keyset < map_size; ++keyset) {
+               if (!map[keyset])
+                       continue;
+
+               /* Iterate the keys in the rule */
+               found = 0;
+               list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list)
+                       if (vcap_find_keyset_keyfield(ri->vctrl, vtype,
+                                                     keyset, ckf->ctrl.key))
+                               ++found;
+
+               /* Save the keyset if all keyfields were found */
+               if (found == keycount)
+                       if (!vcap_keyset_list_add(matches, keyset))
+                               /* bail out when the quota is filled */
+                               break;
+       }
+
+       return matches->cnt > 0;
+}
+
 /* Validate a rule with respect to available port keys */
 int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto)
 {
        struct vcap_rule_internal *ri = to_intrule(rule);
+       struct vcap_keyset_list matches = {};
        enum vcap_keyfield_set keysets[10];
-       struct vcap_keyset_list kslist;
        int ret;
 
-       /* This validation will be much expanded later */
        ret = vcap_api_check(ri->vctrl);
        if (ret)
                return ret;
                ri->data.exterr = VCAP_ERR_NO_NETDEV;
                return -EINVAL;
        }
+
+       matches.keysets = keysets;
+       matches.max = ARRAY_SIZE(keysets);
        if (ri->data.keyset == VCAP_KFS_NO_VALUE) {
-               ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH;
-               return -EINVAL;
+               /* Iterate over rule keyfields and select keysets that fits */
+               if (!vcap_rule_find_keysets(ri, &matches)) {
+                       ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH;
+                       return -EINVAL;
+               }
+       } else {
+               /* prepare for keyset validation */
+               keysets[0] = ri->data.keyset;
+               matches.cnt = 1;
        }
-       /* prepare for keyset validation */
-       keysets[0] = ri->data.keyset;
-       kslist.keysets = keysets;
-       kslist.cnt = 1;
+
        /* Pick a keyset that is supported in the port lookups */
-       ret = ri->vctrl->ops->validate_keyset(ri->ndev, ri->admin, rule, &kslist,
-                                             l3_proto);
+       ret = ri->vctrl->ops->validate_keyset(ri->ndev, ri->admin, rule,
+                                             &matches, l3_proto);
        if (ret < 0) {
                pr_err("%s:%d: keyset validation failed: %d\n",
                       __func__, __LINE__, ret);
                ri->data.exterr = VCAP_ERR_NO_PORT_KEYSET_MATCH;
                return ret;
        }
+       /* use the keyset that is supported in the port lookups */
+       ret = vcap_set_rule_set_keyset(rule, ret);
+       if (ret < 0) {
+               pr_err("%s:%d: keyset was not updated: %d\n",
+                      __func__, __LINE__, ret);
+               return ret;
+       }
        if (ri->data.actionset == VCAP_AFS_NO_VALUE) {
+               /* Later also actionsets will be matched against actions in
+                * the rule, and the type will be set accordingly
+                */
                ri->data.exterr = VCAP_ERR_NO_ACTIONSET_MATCH;
                return -EINVAL;
        }
 
 /* Cleanup a VCAP instance */
 int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin);
 
+/* Add a keyset to a keyset list */
+bool vcap_keyset_list_add(struct vcap_keyset_list *keysetlist,
+                         enum vcap_keyfield_set keyset);
+
+/* map keyset id to a string with the keyset name */
+const char *vcap_keyset_name(struct vcap_control *vctrl,
+                            enum vcap_keyfield_set keyset);
+/* map key field id to a string with the key name */
+const char *vcap_keyfield_name(struct vcap_control *vctrl,
+                              enum vcap_key_field key);
+
 #endif /* __VCAP_API_CLIENT__ */