linux/drivers/net/ethernet/mscc/ocelot_mrp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0 OR MIT)
   2/* Microsemi Ocelot Switch driver
   3 *
   4 * Copyright (c) 2017, 2019 Microsemi Corporation
   5 * Copyright 2020-2021 NXP
   6 */
   7
   8#include <linux/if_bridge.h>
   9#include <linux/mrp_bridge.h>
  10#include <soc/mscc/ocelot_vcap.h>
  11#include <uapi/linux/mrp_bridge.h>
  12#include "ocelot.h"
  13#include "ocelot_vcap.h"
  14
  15static const u8 mrp_test_dmac[] = { 0x01, 0x15, 0x4e, 0x00, 0x00, 0x01 };
  16static const u8 mrp_control_dmac[] = { 0x01, 0x15, 0x4e, 0x00, 0x00, 0x02 };
  17
  18static int ocelot_mrp_find_partner_port(struct ocelot *ocelot,
  19                                        struct ocelot_port *p)
  20{
  21        int i;
  22
  23        for (i = 0; i < ocelot->num_phys_ports; ++i) {
  24                struct ocelot_port *ocelot_port = ocelot->ports[i];
  25
  26                if (!ocelot_port || p == ocelot_port)
  27                        continue;
  28
  29                if (ocelot_port->mrp_ring_id == p->mrp_ring_id)
  30                        return i;
  31        }
  32
  33        return -1;
  34}
  35
  36static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int id)
  37{
  38        struct ocelot_vcap_block *block_vcap_is2;
  39        struct ocelot_vcap_filter *filter;
  40
  41        block_vcap_is2 = &ocelot->block[VCAP_IS2];
  42        filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, id,
  43                                                     false);
  44        if (!filter)
  45                return 0;
  46
  47        return ocelot_vcap_filter_del(ocelot, filter);
  48}
  49
  50static int ocelot_mrp_redirect_add_vcap(struct ocelot *ocelot, int src_port,
  51                                        int dst_port)
  52{
  53        const u8 mrp_test_mask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  54        struct ocelot_vcap_filter *filter;
  55        int err;
  56
  57        filter = kzalloc(sizeof(*filter), GFP_KERNEL);
  58        if (!filter)
  59                return -ENOMEM;
  60
  61        filter->key_type = OCELOT_VCAP_KEY_ETYPE;
  62        filter->prio = 1;
  63        filter->id.cookie = src_port;
  64        filter->id.tc_offload = false;
  65        filter->block_id = VCAP_IS2;
  66        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
  67        filter->ingress_port_mask = BIT(src_port);
  68        ether_addr_copy(filter->key.etype.dmac.value, mrp_test_dmac);
  69        ether_addr_copy(filter->key.etype.dmac.mask, mrp_test_mask);
  70        filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
  71        filter->action.port_mask = BIT(dst_port);
  72
  73        err = ocelot_vcap_filter_add(ocelot, filter, NULL);
  74        if (err)
  75                kfree(filter);
  76
  77        return err;
  78}
  79
  80static int ocelot_mrp_copy_add_vcap(struct ocelot *ocelot, int port,
  81                                    int prio, unsigned long cookie)
  82{
  83        const u8 mrp_mask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
  84        struct ocelot_vcap_filter *filter;
  85        int err;
  86
  87        filter = kzalloc(sizeof(*filter), GFP_KERNEL);
  88        if (!filter)
  89                return -ENOMEM;
  90
  91        filter->key_type = OCELOT_VCAP_KEY_ETYPE;
  92        filter->prio = prio;
  93        filter->id.cookie = cookie;
  94        filter->id.tc_offload = false;
  95        filter->block_id = VCAP_IS2;
  96        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
  97        filter->ingress_port_mask = BIT(port);
  98        /* Here is possible to use control or test dmac because the mask
  99         * doesn't cover the LSB
 100         */
 101        ether_addr_copy(filter->key.etype.dmac.value, mrp_test_dmac);
 102        ether_addr_copy(filter->key.etype.dmac.mask, mrp_mask);
 103        filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
 104        filter->action.port_mask = 0x0;
 105        filter->action.cpu_copy_ena = true;
 106        filter->action.cpu_qu_num = OCELOT_MRP_CPUQ;
 107
 108        err = ocelot_vcap_filter_add(ocelot, filter, NULL);
 109        if (err)
 110                kfree(filter);
 111
 112        return err;
 113}
 114
 115static void ocelot_mrp_save_mac(struct ocelot *ocelot,
 116                                struct ocelot_port *port)
 117{
 118        ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_test_dmac,
 119                          port->pvid_vlan.vid, ENTRYTYPE_LOCKED);
 120        ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_control_dmac,
 121                          port->pvid_vlan.vid, ENTRYTYPE_LOCKED);
 122}
 123
 124static void ocelot_mrp_del_mac(struct ocelot *ocelot,
 125                               struct ocelot_port *port)
 126{
 127        ocelot_mact_forget(ocelot, mrp_test_dmac, port->pvid_vlan.vid);
 128        ocelot_mact_forget(ocelot, mrp_control_dmac, port->pvid_vlan.vid);
 129}
 130
 131int ocelot_mrp_add(struct ocelot *ocelot, int port,
 132                   const struct switchdev_obj_mrp *mrp)
 133{
 134        struct ocelot_port *ocelot_port = ocelot->ports[port];
 135        struct ocelot_port_private *priv;
 136        struct net_device *dev;
 137
 138        if (!ocelot_port)
 139                return -EOPNOTSUPP;
 140
 141        priv = container_of(ocelot_port, struct ocelot_port_private, port);
 142        dev = priv->dev;
 143
 144        if (mrp->p_port != dev && mrp->s_port != dev)
 145                return 0;
 146
 147        ocelot_port->mrp_ring_id = mrp->ring_id;
 148
 149        return 0;
 150}
 151EXPORT_SYMBOL(ocelot_mrp_add);
 152
 153int ocelot_mrp_del(struct ocelot *ocelot, int port,
 154                   const struct switchdev_obj_mrp *mrp)
 155{
 156        struct ocelot_port *ocelot_port = ocelot->ports[port];
 157
 158        if (!ocelot_port)
 159                return -EOPNOTSUPP;
 160
 161        if (ocelot_port->mrp_ring_id != mrp->ring_id)
 162                return 0;
 163
 164        ocelot_port->mrp_ring_id = 0;
 165
 166        return 0;
 167}
 168EXPORT_SYMBOL(ocelot_mrp_del);
 169
 170int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port,
 171                             const struct switchdev_obj_ring_role_mrp *mrp)
 172{
 173        struct ocelot_port *ocelot_port = ocelot->ports[port];
 174        int dst_port;
 175        int err;
 176
 177        if (!ocelot_port)
 178                return -EOPNOTSUPP;
 179
 180        if (mrp->ring_role != BR_MRP_RING_ROLE_MRC && !mrp->sw_backup)
 181                return -EOPNOTSUPP;
 182
 183        if (ocelot_port->mrp_ring_id != mrp->ring_id)
 184                return 0;
 185
 186        ocelot_mrp_save_mac(ocelot, ocelot_port);
 187
 188        if (mrp->ring_role != BR_MRP_RING_ROLE_MRC)
 189                return ocelot_mrp_copy_add_vcap(ocelot, port, 1, port);
 190
 191        dst_port = ocelot_mrp_find_partner_port(ocelot, ocelot_port);
 192        if (dst_port == -1)
 193                return -EINVAL;
 194
 195        err = ocelot_mrp_redirect_add_vcap(ocelot, port, dst_port);
 196        if (err)
 197                return err;
 198
 199        err = ocelot_mrp_copy_add_vcap(ocelot, port, 2,
 200                                       port + ocelot->num_phys_ports);
 201        if (err) {
 202                ocelot_mrp_del_vcap(ocelot, port);
 203                return err;
 204        }
 205
 206        return 0;
 207}
 208EXPORT_SYMBOL(ocelot_mrp_add_ring_role);
 209
 210int ocelot_mrp_del_ring_role(struct ocelot *ocelot, int port,
 211                             const struct switchdev_obj_ring_role_mrp *mrp)
 212{
 213        struct ocelot_port *ocelot_port = ocelot->ports[port];
 214        int i;
 215
 216        if (!ocelot_port)
 217                return -EOPNOTSUPP;
 218
 219        if (mrp->ring_role != BR_MRP_RING_ROLE_MRC && !mrp->sw_backup)
 220                return -EOPNOTSUPP;
 221
 222        if (ocelot_port->mrp_ring_id != mrp->ring_id)
 223                return 0;
 224
 225        ocelot_mrp_del_vcap(ocelot, port);
 226        ocelot_mrp_del_vcap(ocelot, port + ocelot->num_phys_ports);
 227
 228        for (i = 0; i < ocelot->num_phys_ports; ++i) {
 229                ocelot_port = ocelot->ports[i];
 230
 231                if (!ocelot_port)
 232                        continue;
 233
 234                if (ocelot_port->mrp_ring_id != 0)
 235                        goto out;
 236        }
 237
 238        ocelot_mrp_del_mac(ocelot, ocelot->ports[port]);
 239out:
 240        return 0;
 241}
 242EXPORT_SYMBOL(ocelot_mrp_del_ring_role);
 243