1*639f1dcfSCedric Jehasse // SPDX-License-Identifier: GPL-2.0-or-later 2*639f1dcfSCedric Jehasse /* 3*639f1dcfSCedric Jehasse * Marvell 88E6xxx Switch flower support 4*639f1dcfSCedric Jehasse * 5*639f1dcfSCedric Jehasse * Copyright (c) 2026 Luminex Network Intelligence 6*639f1dcfSCedric Jehasse */ 7*639f1dcfSCedric Jehasse 8*639f1dcfSCedric Jehasse #include "chip.h" 9*639f1dcfSCedric Jehasse #include "tcflower.h" 10*639f1dcfSCedric Jehasse #include "tcam.h" 11*639f1dcfSCedric Jehasse 12*639f1dcfSCedric Jehasse #define MV88E6XXX_ETHTYPE_OFFSET 16 13*639f1dcfSCedric Jehasse #define MV88E6XXX_IP_PROTO_OFFSET 27 14*639f1dcfSCedric Jehasse #define MV88E6XXX_IPV4_SRC_OFFSET 30 15*639f1dcfSCedric Jehasse #define MV88E6XXX_IPV4_DST_OFFSET 34 16*639f1dcfSCedric Jehasse 17*639f1dcfSCedric Jehasse static int mv88e6xxx_flower_parse_key(struct mv88e6xxx_chip *chip, 18*639f1dcfSCedric Jehasse struct netlink_ext_ack *extack, 19*639f1dcfSCedric Jehasse struct flow_cls_offload *cls, 20*639f1dcfSCedric Jehasse struct mv88e6xxx_tcam_key *key) 21*639f1dcfSCedric Jehasse { 22*639f1dcfSCedric Jehasse struct flow_rule *rule = flow_cls_offload_flow_rule(cls); 23*639f1dcfSCedric Jehasse struct flow_dissector *dissector = rule->match.dissector; 24*639f1dcfSCedric Jehasse u16 addr_type = 0; 25*639f1dcfSCedric Jehasse 26*639f1dcfSCedric Jehasse if (dissector->used_keys & 27*639f1dcfSCedric Jehasse ~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | 28*639f1dcfSCedric Jehasse BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | 29*639f1dcfSCedric Jehasse BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS))) { 30*639f1dcfSCedric Jehasse NL_SET_ERR_MSG_MOD(extack, 31*639f1dcfSCedric Jehasse "Unsupported keys used"); 32*639f1dcfSCedric Jehasse return -EOPNOTSUPP; 33*639f1dcfSCedric Jehasse } 34*639f1dcfSCedric Jehasse 35*639f1dcfSCedric Jehasse if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { 36*639f1dcfSCedric Jehasse struct flow_match_control match; 37*639f1dcfSCedric Jehasse 38*639f1dcfSCedric Jehasse flow_rule_match_control(rule, &match); 39*639f1dcfSCedric Jehasse addr_type = match.key->addr_type; 40*639f1dcfSCedric Jehasse 41*639f1dcfSCedric Jehasse if (flow_rule_has_control_flags(match.mask->flags, 42*639f1dcfSCedric Jehasse cls->common.extack)) 43*639f1dcfSCedric Jehasse return -EOPNOTSUPP; 44*639f1dcfSCedric Jehasse } 45*639f1dcfSCedric Jehasse if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { 46*639f1dcfSCedric Jehasse struct flow_match_basic match; 47*639f1dcfSCedric Jehasse 48*639f1dcfSCedric Jehasse flow_rule_match_basic(rule, &match); 49*639f1dcfSCedric Jehasse mv88e6xxx_tcam_match_set(key, MV88E6XXX_ETHTYPE_OFFSET, 50*639f1dcfSCedric Jehasse match.key->n_proto, 51*639f1dcfSCedric Jehasse match.mask->n_proto); 52*639f1dcfSCedric Jehasse mv88e6xxx_tcam_match_set(key, MV88E6XXX_IP_PROTO_OFFSET, 53*639f1dcfSCedric Jehasse match.key->ip_proto, 54*639f1dcfSCedric Jehasse match.mask->ip_proto); 55*639f1dcfSCedric Jehasse } 56*639f1dcfSCedric Jehasse 57*639f1dcfSCedric Jehasse if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 58*639f1dcfSCedric Jehasse struct flow_match_ipv4_addrs match; 59*639f1dcfSCedric Jehasse 60*639f1dcfSCedric Jehasse flow_rule_match_ipv4_addrs(cls->rule, &match); 61*639f1dcfSCedric Jehasse mv88e6xxx_tcam_match_set(key, MV88E6XXX_IPV4_SRC_OFFSET, 62*639f1dcfSCedric Jehasse match.key->src, 63*639f1dcfSCedric Jehasse match.mask->src); 64*639f1dcfSCedric Jehasse mv88e6xxx_tcam_match_set(key, MV88E6XXX_IPV4_DST_OFFSET, 65*639f1dcfSCedric Jehasse match.key->dst, 66*639f1dcfSCedric Jehasse match.mask->dst); 67*639f1dcfSCedric Jehasse } 68*639f1dcfSCedric Jehasse 69*639f1dcfSCedric Jehasse return 0; 70*639f1dcfSCedric Jehasse } 71*639f1dcfSCedric Jehasse 72*639f1dcfSCedric Jehasse int mv88e6xxx_cls_flower_add(struct dsa_switch *ds, int port, 73*639f1dcfSCedric Jehasse struct flow_cls_offload *cls, bool ingress) 74*639f1dcfSCedric Jehasse { 75*639f1dcfSCedric Jehasse struct flow_rule *rule = flow_cls_offload_flow_rule(cls); 76*639f1dcfSCedric Jehasse struct netlink_ext_ack *extack = cls->common.extack; 77*639f1dcfSCedric Jehasse struct mv88e6xxx_chip *chip = ds->priv; 78*639f1dcfSCedric Jehasse struct mv88e6xxx_tcam_key key = { 0 }; 79*639f1dcfSCedric Jehasse const struct flow_action_entry *act; 80*639f1dcfSCedric Jehasse unsigned long cookie = cls->cookie; 81*639f1dcfSCedric Jehasse struct mv88e6xxx_tcam_entry *entry; 82*639f1dcfSCedric Jehasse int err, i; 83*639f1dcfSCedric Jehasse 84*639f1dcfSCedric Jehasse if (!mv88e6xxx_has_tcam(chip)) { 85*639f1dcfSCedric Jehasse NL_SET_ERR_MSG_MOD(extack, "hardware offload not supported"); 86*639f1dcfSCedric Jehasse return -EOPNOTSUPP; 87*639f1dcfSCedric Jehasse } 88*639f1dcfSCedric Jehasse 89*639f1dcfSCedric Jehasse err = mv88e6xxx_flower_parse_key(chip, extack, cls, &key); 90*639f1dcfSCedric Jehasse if (err) 91*639f1dcfSCedric Jehasse return err; 92*639f1dcfSCedric Jehasse 93*639f1dcfSCedric Jehasse mv88e6xxx_reg_lock(chip); 94*639f1dcfSCedric Jehasse entry = mv88e6xxx_tcam_entry_find(chip, cookie); 95*639f1dcfSCedric Jehasse if (entry) { 96*639f1dcfSCedric Jehasse err = -EEXIST; 97*639f1dcfSCedric Jehasse goto err_unlock; 98*639f1dcfSCedric Jehasse } 99*639f1dcfSCedric Jehasse 100*639f1dcfSCedric Jehasse entry = kzalloc(sizeof(*entry), GFP_KERNEL); 101*639f1dcfSCedric Jehasse if (!entry) { 102*639f1dcfSCedric Jehasse err = -ENOMEM; 103*639f1dcfSCedric Jehasse goto err_unlock; 104*639f1dcfSCedric Jehasse } 105*639f1dcfSCedric Jehasse 106*639f1dcfSCedric Jehasse entry->cookie = cookie; 107*639f1dcfSCedric Jehasse entry->prio = cls->common.prio; 108*639f1dcfSCedric Jehasse entry->key = key; 109*639f1dcfSCedric Jehasse 110*639f1dcfSCedric Jehasse flow_action_for_each(i, act, &rule->action) { 111*639f1dcfSCedric Jehasse switch (act->id) { 112*639f1dcfSCedric Jehasse case FLOW_ACTION_TRAP: { 113*639f1dcfSCedric Jehasse int cpu = dsa_upstream_port(ds, port); 114*639f1dcfSCedric Jehasse 115*639f1dcfSCedric Jehasse entry->action.dpv_mode = DPV_MODE_REPLACE; 116*639f1dcfSCedric Jehasse entry->action.dpv = BIT(cpu); 117*639f1dcfSCedric Jehasse break; 118*639f1dcfSCedric Jehasse } 119*639f1dcfSCedric Jehasse default: 120*639f1dcfSCedric Jehasse NL_SET_ERR_MSG_MOD(extack, "action not supported"); 121*639f1dcfSCedric Jehasse err = -EOPNOTSUPP; 122*639f1dcfSCedric Jehasse goto err_free_entry; 123*639f1dcfSCedric Jehasse } 124*639f1dcfSCedric Jehasse } 125*639f1dcfSCedric Jehasse 126*639f1dcfSCedric Jehasse entry->key.spv = BIT(port); 127*639f1dcfSCedric Jehasse entry->key.spv_mask = mv88e6xxx_port_mask(chip); 128*639f1dcfSCedric Jehasse 129*639f1dcfSCedric Jehasse err = mv88e6xxx_tcam_entry_add(chip, entry); 130*639f1dcfSCedric Jehasse if (err) 131*639f1dcfSCedric Jehasse goto err_free_entry; 132*639f1dcfSCedric Jehasse 133*639f1dcfSCedric Jehasse mv88e6xxx_reg_unlock(chip); 134*639f1dcfSCedric Jehasse return 0; 135*639f1dcfSCedric Jehasse 136*639f1dcfSCedric Jehasse err_free_entry: 137*639f1dcfSCedric Jehasse kfree(entry); 138*639f1dcfSCedric Jehasse err_unlock: 139*639f1dcfSCedric Jehasse mv88e6xxx_reg_unlock(chip); 140*639f1dcfSCedric Jehasse return err; 141*639f1dcfSCedric Jehasse } 142*639f1dcfSCedric Jehasse 143*639f1dcfSCedric Jehasse int mv88e6xxx_cls_flower_del(struct dsa_switch *ds, int port, 144*639f1dcfSCedric Jehasse struct flow_cls_offload *cls, bool ingress) 145*639f1dcfSCedric Jehasse { 146*639f1dcfSCedric Jehasse struct mv88e6xxx_chip *chip = ds->priv; 147*639f1dcfSCedric Jehasse struct mv88e6xxx_tcam_entry *entry; 148*639f1dcfSCedric Jehasse int err = 0; 149*639f1dcfSCedric Jehasse 150*639f1dcfSCedric Jehasse mv88e6xxx_reg_lock(chip); 151*639f1dcfSCedric Jehasse entry = mv88e6xxx_tcam_entry_find(chip, cls->cookie); 152*639f1dcfSCedric Jehasse 153*639f1dcfSCedric Jehasse if (entry) 154*639f1dcfSCedric Jehasse err = mv88e6xxx_tcam_entry_del(chip, entry); 155*639f1dcfSCedric Jehasse mv88e6xxx_reg_unlock(chip); 156*639f1dcfSCedric Jehasse return err; 157*639f1dcfSCedric Jehasse } 158*639f1dcfSCedric Jehasse 159*639f1dcfSCedric Jehasse void mv88e6xxx_flower_teardown(struct mv88e6xxx_chip *chip) 160*639f1dcfSCedric Jehasse { 161*639f1dcfSCedric Jehasse struct mv88e6xxx_tcam_entry *pos, *n; 162*639f1dcfSCedric Jehasse 163*639f1dcfSCedric Jehasse list_for_each_entry_safe(pos, n, &chip->tcam.entries, list) { 164*639f1dcfSCedric Jehasse list_del(&pos->list); 165*639f1dcfSCedric Jehasse kfree(pos); 166*639f1dcfSCedric Jehasse } 167*639f1dcfSCedric Jehasse } 168