12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2f515f192SVivien Didelot /* 3f515f192SVivien Didelot * Handling of a single switch chip, part of a switch fabric 4f515f192SVivien Didelot * 54333d619SVivien Didelot * Copyright (c) 2017 Savoir-faire Linux Inc. 64333d619SVivien Didelot * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 7f515f192SVivien Didelot */ 8f515f192SVivien Didelot 9d371b7c9SVladimir Oltean #include <linux/if_bridge.h> 10f515f192SVivien Didelot #include <linux/netdevice.h> 11f515f192SVivien Didelot #include <linux/notifier.h> 12061f6a50SFlorian Fainelli #include <linux/if_vlan.h> 131faabf74SVivien Didelot #include <net/switchdev.h> 14ea5dd34bSVivien Didelot 15ea5dd34bSVivien Didelot #include "dsa_priv.h" 16f515f192SVivien Didelot 171faabf74SVivien Didelot static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, 181faabf74SVivien Didelot unsigned int ageing_time) 191faabf74SVivien Didelot { 201faabf74SVivien Didelot int i; 211faabf74SVivien Didelot 221faabf74SVivien Didelot for (i = 0; i < ds->num_ports; ++i) { 2368bb8ea8SVivien Didelot struct dsa_port *dp = dsa_to_port(ds, i); 241faabf74SVivien Didelot 251faabf74SVivien Didelot if (dp->ageing_time && dp->ageing_time < ageing_time) 261faabf74SVivien Didelot ageing_time = dp->ageing_time; 271faabf74SVivien Didelot } 281faabf74SVivien Didelot 291faabf74SVivien Didelot return ageing_time; 301faabf74SVivien Didelot } 311faabf74SVivien Didelot 321faabf74SVivien Didelot static int dsa_switch_ageing_time(struct dsa_switch *ds, 331faabf74SVivien Didelot struct dsa_notifier_ageing_time_info *info) 341faabf74SVivien Didelot { 351faabf74SVivien Didelot unsigned int ageing_time = info->ageing_time; 361faabf74SVivien Didelot 371faabf74SVivien Didelot if (ds->ageing_time_min && ageing_time < ds->ageing_time_min) 381faabf74SVivien Didelot return -ERANGE; 3977b61365SVladimir Oltean 401faabf74SVivien Didelot if (ds->ageing_time_max && ageing_time > ds->ageing_time_max) 411faabf74SVivien Didelot return -ERANGE; 421faabf74SVivien Didelot 431faabf74SVivien Didelot /* Program the fastest ageing time in case of multiple bridges */ 441faabf74SVivien Didelot ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time); 451faabf74SVivien Didelot 461faabf74SVivien Didelot if (ds->ops->set_ageing_time) 471faabf74SVivien Didelot return ds->ops->set_ageing_time(ds, ageing_time); 481faabf74SVivien Didelot 491faabf74SVivien Didelot return 0; 501faabf74SVivien Didelot } 511faabf74SVivien Didelot 52bfcb8132SVladimir Oltean static bool dsa_switch_mtu_match(struct dsa_switch *ds, int port, 53bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info *info) 54bfcb8132SVladimir Oltean { 55bfcb8132SVladimir Oltean if (ds->index == info->sw_index) 56bfcb8132SVladimir Oltean return (port == info->port) || dsa_is_dsa_port(ds, port); 57bfcb8132SVladimir Oltean 58bfcb8132SVladimir Oltean if (!info->propagate_upstream) 59bfcb8132SVladimir Oltean return false; 60bfcb8132SVladimir Oltean 61bfcb8132SVladimir Oltean if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) 62bfcb8132SVladimir Oltean return true; 63bfcb8132SVladimir Oltean 64bfcb8132SVladimir Oltean return false; 65bfcb8132SVladimir Oltean } 66bfcb8132SVladimir Oltean 67bfcb8132SVladimir Oltean static int dsa_switch_mtu(struct dsa_switch *ds, 68bfcb8132SVladimir Oltean struct dsa_notifier_mtu_info *info) 69bfcb8132SVladimir Oltean { 70bfcb8132SVladimir Oltean int port, ret; 71bfcb8132SVladimir Oltean 72bfcb8132SVladimir Oltean if (!ds->ops->port_change_mtu) 73bfcb8132SVladimir Oltean return -EOPNOTSUPP; 74bfcb8132SVladimir Oltean 75bfcb8132SVladimir Oltean for (port = 0; port < ds->num_ports; port++) { 76bfcb8132SVladimir Oltean if (dsa_switch_mtu_match(ds, port, info)) { 77bfcb8132SVladimir Oltean ret = ds->ops->port_change_mtu(ds, port, info->mtu); 78bfcb8132SVladimir Oltean if (ret) 79bfcb8132SVladimir Oltean return ret; 80bfcb8132SVladimir Oltean } 81bfcb8132SVladimir Oltean } 82bfcb8132SVladimir Oltean 83bfcb8132SVladimir Oltean return 0; 84bfcb8132SVladimir Oltean } 85bfcb8132SVladimir Oltean 8604d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds, 8704d3a4c6SVivien Didelot struct dsa_notifier_bridge_info *info) 8804d3a4c6SVivien Didelot { 89f66a6a69SVladimir Oltean struct dsa_switch_tree *dst = ds->dst; 90f66a6a69SVladimir Oltean 91f66a6a69SVladimir Oltean if (dst->index == info->tree_index && ds->index == info->sw_index && 92f66a6a69SVladimir Oltean ds->ops->port_bridge_join) 9304d3a4c6SVivien Didelot return ds->ops->port_bridge_join(ds, info->port, info->br); 9404d3a4c6SVivien Didelot 95f66a6a69SVladimir Oltean if ((dst->index != info->tree_index || ds->index != info->sw_index) && 96f66a6a69SVladimir Oltean ds->ops->crosschip_bridge_join) 97f66a6a69SVladimir Oltean return ds->ops->crosschip_bridge_join(ds, info->tree_index, 98f66a6a69SVladimir Oltean info->sw_index, 9940ef2c93SVivien Didelot info->port, info->br); 10004d3a4c6SVivien Didelot 10104d3a4c6SVivien Didelot return 0; 10204d3a4c6SVivien Didelot } 10304d3a4c6SVivien Didelot 10404d3a4c6SVivien Didelot static int dsa_switch_bridge_leave(struct dsa_switch *ds, 10504d3a4c6SVivien Didelot struct dsa_notifier_bridge_info *info) 10604d3a4c6SVivien Didelot { 107d371b7c9SVladimir Oltean bool unset_vlan_filtering = br_vlan_enabled(info->br); 108f66a6a69SVladimir Oltean struct dsa_switch_tree *dst = ds->dst; 10989153ed6SVladimir Oltean struct netlink_ext_ack extack = {0}; 110*479dc497SVladimir Oltean int err, port; 111d371b7c9SVladimir Oltean 112f66a6a69SVladimir Oltean if (dst->index == info->tree_index && ds->index == info->sw_index && 113f66a6a69SVladimir Oltean ds->ops->port_bridge_join) 11404d3a4c6SVivien Didelot ds->ops->port_bridge_leave(ds, info->port, info->br); 11504d3a4c6SVivien Didelot 116f66a6a69SVladimir Oltean if ((dst->index != info->tree_index || ds->index != info->sw_index) && 117f66a6a69SVladimir Oltean ds->ops->crosschip_bridge_join) 118f66a6a69SVladimir Oltean ds->ops->crosschip_bridge_leave(ds, info->tree_index, 119f66a6a69SVladimir Oltean info->sw_index, info->port, 12040ef2c93SVivien Didelot info->br); 12104d3a4c6SVivien Didelot 122d371b7c9SVladimir Oltean /* If the bridge was vlan_filtering, the bridge core doesn't trigger an 123d371b7c9SVladimir Oltean * event for changing vlan_filtering setting upon slave ports leaving 124d371b7c9SVladimir Oltean * it. That is a good thing, because that lets us handle it and also 125d371b7c9SVladimir Oltean * handle the case where the switch's vlan_filtering setting is global 126d371b7c9SVladimir Oltean * (not per port). When that happens, the correct moment to trigger the 127*479dc497SVladimir Oltean * vlan_filtering callback is only when the last port leaves the last 128*479dc497SVladimir Oltean * VLAN-aware bridge. 129d371b7c9SVladimir Oltean */ 130d371b7c9SVladimir Oltean if (unset_vlan_filtering && ds->vlan_filtering_is_global) { 131*479dc497SVladimir Oltean for (port = 0; port < ds->num_ports; port++) { 132*479dc497SVladimir Oltean struct net_device *bridge_dev; 133*479dc497SVladimir Oltean 134*479dc497SVladimir Oltean bridge_dev = dsa_to_port(ds, port)->bridge_dev; 135*479dc497SVladimir Oltean 136*479dc497SVladimir Oltean if (bridge_dev && br_vlan_enabled(bridge_dev)) { 137d371b7c9SVladimir Oltean unset_vlan_filtering = false; 138d371b7c9SVladimir Oltean break; 139d371b7c9SVladimir Oltean } 140d371b7c9SVladimir Oltean } 141d371b7c9SVladimir Oltean } 142d371b7c9SVladimir Oltean if (unset_vlan_filtering) { 1432e554a7aSVladimir Oltean err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port), 14489153ed6SVladimir Oltean false, &extack); 14589153ed6SVladimir Oltean if (extack._msg) 14689153ed6SVladimir Oltean dev_err(ds->dev, "port %d: %s\n", info->port, 14789153ed6SVladimir Oltean extack._msg); 148d371b7c9SVladimir Oltean if (err && err != EOPNOTSUPP) 149d371b7c9SVladimir Oltean return err; 150d371b7c9SVladimir Oltean } 15104d3a4c6SVivien Didelot return 0; 15204d3a4c6SVivien Didelot } 15304d3a4c6SVivien Didelot 154685fb6a4SVivien Didelot static int dsa_switch_fdb_add(struct dsa_switch *ds, 155685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 156685fb6a4SVivien Didelot { 1573169241fSVivien Didelot int port = dsa_towards_port(ds, info->sw_index, info->port); 158685fb6a4SVivien Didelot 1591b6dd556SArkadi Sharshevsky if (!ds->ops->port_fdb_add) 160685fb6a4SVivien Didelot return -EOPNOTSUPP; 161685fb6a4SVivien Didelot 1623169241fSVivien Didelot return ds->ops->port_fdb_add(ds, port, info->addr, info->vid); 163685fb6a4SVivien Didelot } 164685fb6a4SVivien Didelot 165685fb6a4SVivien Didelot static int dsa_switch_fdb_del(struct dsa_switch *ds, 166685fb6a4SVivien Didelot struct dsa_notifier_fdb_info *info) 167685fb6a4SVivien Didelot { 1683169241fSVivien Didelot int port = dsa_towards_port(ds, info->sw_index, info->port); 169685fb6a4SVivien Didelot 170685fb6a4SVivien Didelot if (!ds->ops->port_fdb_del) 171685fb6a4SVivien Didelot return -EOPNOTSUPP; 172685fb6a4SVivien Didelot 1733169241fSVivien Didelot return ds->ops->port_fdb_del(ds, port, info->addr, info->vid); 174685fb6a4SVivien Didelot } 175685fb6a4SVivien Didelot 17618596f50SGeorge McCollister static int dsa_switch_hsr_join(struct dsa_switch *ds, 17718596f50SGeorge McCollister struct dsa_notifier_hsr_info *info) 17818596f50SGeorge McCollister { 17918596f50SGeorge McCollister if (ds->index == info->sw_index && ds->ops->port_hsr_join) 18018596f50SGeorge McCollister return ds->ops->port_hsr_join(ds, info->port, info->hsr); 18118596f50SGeorge McCollister 18218596f50SGeorge McCollister return -EOPNOTSUPP; 18318596f50SGeorge McCollister } 18418596f50SGeorge McCollister 18518596f50SGeorge McCollister static int dsa_switch_hsr_leave(struct dsa_switch *ds, 18618596f50SGeorge McCollister struct dsa_notifier_hsr_info *info) 18718596f50SGeorge McCollister { 18818596f50SGeorge McCollister if (ds->index == info->sw_index && ds->ops->port_hsr_leave) 18918596f50SGeorge McCollister return ds->ops->port_hsr_leave(ds, info->port, info->hsr); 19018596f50SGeorge McCollister 19118596f50SGeorge McCollister return -EOPNOTSUPP; 19218596f50SGeorge McCollister } 19318596f50SGeorge McCollister 194058102a6STobias Waldekranz static int dsa_switch_lag_change(struct dsa_switch *ds, 195058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 196058102a6STobias Waldekranz { 197058102a6STobias Waldekranz if (ds->index == info->sw_index && ds->ops->port_lag_change) 198058102a6STobias Waldekranz return ds->ops->port_lag_change(ds, info->port); 199058102a6STobias Waldekranz 200058102a6STobias Waldekranz if (ds->index != info->sw_index && ds->ops->crosschip_lag_change) 201058102a6STobias Waldekranz return ds->ops->crosschip_lag_change(ds, info->sw_index, 202058102a6STobias Waldekranz info->port); 203058102a6STobias Waldekranz 204058102a6STobias Waldekranz return 0; 205058102a6STobias Waldekranz } 206058102a6STobias Waldekranz 207058102a6STobias Waldekranz static int dsa_switch_lag_join(struct dsa_switch *ds, 208058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 209058102a6STobias Waldekranz { 210058102a6STobias Waldekranz if (ds->index == info->sw_index && ds->ops->port_lag_join) 211058102a6STobias Waldekranz return ds->ops->port_lag_join(ds, info->port, info->lag, 212058102a6STobias Waldekranz info->info); 213058102a6STobias Waldekranz 214058102a6STobias Waldekranz if (ds->index != info->sw_index && ds->ops->crosschip_lag_join) 215058102a6STobias Waldekranz return ds->ops->crosschip_lag_join(ds, info->sw_index, 216058102a6STobias Waldekranz info->port, info->lag, 217058102a6STobias Waldekranz info->info); 218058102a6STobias Waldekranz 219058102a6STobias Waldekranz return 0; 220058102a6STobias Waldekranz } 221058102a6STobias Waldekranz 222058102a6STobias Waldekranz static int dsa_switch_lag_leave(struct dsa_switch *ds, 223058102a6STobias Waldekranz struct dsa_notifier_lag_info *info) 224058102a6STobias Waldekranz { 225058102a6STobias Waldekranz if (ds->index == info->sw_index && ds->ops->port_lag_leave) 226058102a6STobias Waldekranz return ds->ops->port_lag_leave(ds, info->port, info->lag); 227058102a6STobias Waldekranz 228058102a6STobias Waldekranz if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave) 229058102a6STobias Waldekranz return ds->ops->crosschip_lag_leave(ds, info->sw_index, 230058102a6STobias Waldekranz info->port, info->lag); 231058102a6STobias Waldekranz 232058102a6STobias Waldekranz return 0; 233058102a6STobias Waldekranz } 234058102a6STobias Waldekranz 235e65d45ccSVivien Didelot static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port, 236e65d45ccSVivien Didelot struct dsa_notifier_mdb_info *info) 237e65d45ccSVivien Didelot { 238e65d45ccSVivien Didelot if (ds->index == info->sw_index && port == info->port) 239e65d45ccSVivien Didelot return true; 240e65d45ccSVivien Didelot 241e65d45ccSVivien Didelot if (dsa_is_dsa_port(ds, port)) 242e65d45ccSVivien Didelot return true; 243e65d45ccSVivien Didelot 244e65d45ccSVivien Didelot return false; 245e65d45ccSVivien Didelot } 246e65d45ccSVivien Didelot 247ffb68fc5SVladimir Oltean static int dsa_switch_mdb_add(struct dsa_switch *ds, 248e65d45ccSVivien Didelot struct dsa_notifier_mdb_info *info) 249e6db98dbSVivien Didelot { 250a52b2da7SVladimir Oltean int err = 0; 251a52b2da7SVladimir Oltean int port; 252e6db98dbSVivien Didelot 253a52b2da7SVladimir Oltean if (!ds->ops->port_mdb_add) 254e6db98dbSVivien Didelot return -EOPNOTSUPP; 255e6db98dbSVivien Didelot 256e65d45ccSVivien Didelot for (port = 0; port < ds->num_ports; port++) { 257e65d45ccSVivien Didelot if (dsa_switch_mdb_match(ds, port, info)) { 258a52b2da7SVladimir Oltean err = ds->ops->port_mdb_add(ds, port, info->mdb); 259e6db98dbSVivien Didelot if (err) 260a52b2da7SVladimir Oltean break; 261a52b2da7SVladimir Oltean } 262a52b2da7SVladimir Oltean } 263a52b2da7SVladimir Oltean 264e6db98dbSVivien Didelot return err; 265e6db98dbSVivien Didelot } 2668ae5bcdcSVivien Didelot 2678ae5bcdcSVivien Didelot static int dsa_switch_mdb_del(struct dsa_switch *ds, 2688ae5bcdcSVivien Didelot struct dsa_notifier_mdb_info *info) 2698ae5bcdcSVivien Didelot { 2708ae5bcdcSVivien Didelot if (!ds->ops->port_mdb_del) 2718ae5bcdcSVivien Didelot return -EOPNOTSUPP; 2728ae5bcdcSVivien Didelot 273a1a6b7eaSVivien Didelot if (ds->index == info->sw_index) 274e65d45ccSVivien Didelot return ds->ops->port_mdb_del(ds, info->port, info->mdb); 275a1a6b7eaSVivien Didelot 276a1a6b7eaSVivien Didelot return 0; 2778ae5bcdcSVivien Didelot } 2788ae5bcdcSVivien Didelot 279e65d45ccSVivien Didelot static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port, 280e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 281e65d45ccSVivien Didelot { 282e65d45ccSVivien Didelot if (ds->index == info->sw_index && port == info->port) 283e65d45ccSVivien Didelot return true; 284e65d45ccSVivien Didelot 2857e1741b4SVivien Didelot if (dsa_is_dsa_port(ds, port)) 286e65d45ccSVivien Didelot return true; 287e65d45ccSVivien Didelot 288e65d45ccSVivien Didelot return false; 289e65d45ccSVivien Didelot } 290e65d45ccSVivien Didelot 291ffb68fc5SVladimir Oltean static int dsa_switch_vlan_add(struct dsa_switch *ds, 292e65d45ccSVivien Didelot struct dsa_notifier_vlan_info *info) 2939c428c59SVivien Didelot { 2949c428c59SVivien Didelot int port, err; 2959c428c59SVivien Didelot 2961958d581SVladimir Oltean if (!ds->ops->port_vlan_add) 2979c428c59SVivien Didelot return -EOPNOTSUPP; 2989c428c59SVivien Didelot 299e65d45ccSVivien Didelot for (port = 0; port < ds->num_ports; port++) { 300e65d45ccSVivien Didelot if (dsa_switch_vlan_match(ds, port, info)) { 30131046a5fSVladimir Oltean err = ds->ops->port_vlan_add(ds, port, info->vlan, 30231046a5fSVladimir Oltean info->extack); 3039c428c59SVivien Didelot if (err) 3049c428c59SVivien Didelot return err; 3059c428c59SVivien Didelot } 306e65d45ccSVivien Didelot } 3079c428c59SVivien Didelot 308d0c627b8SVivien Didelot return 0; 309d0c627b8SVivien Didelot } 310d0c627b8SVivien Didelot 311d0c627b8SVivien Didelot static int dsa_switch_vlan_del(struct dsa_switch *ds, 312d0c627b8SVivien Didelot struct dsa_notifier_vlan_info *info) 313d0c627b8SVivien Didelot { 314d0c627b8SVivien Didelot if (!ds->ops->port_vlan_del) 315d0c627b8SVivien Didelot return -EOPNOTSUPP; 316d0c627b8SVivien Didelot 3171ca4aa9cSVivien Didelot if (ds->index == info->sw_index) 318e65d45ccSVivien Didelot return ds->ops->port_vlan_del(ds, info->port, info->vlan); 3191ca4aa9cSVivien Didelot 3207e1741b4SVivien Didelot /* Do not deprogram the DSA links as they may be used as conduit 3217e1741b4SVivien Didelot * for other VLAN members in the fabric. 3227e1741b4SVivien Didelot */ 3231ca4aa9cSVivien Didelot return 0; 324d0c627b8SVivien Didelot } 325d0c627b8SVivien Didelot 32653da0ebaSVladimir Oltean static bool dsa_switch_tag_proto_match(struct dsa_switch *ds, int port, 32753da0ebaSVladimir Oltean struct dsa_notifier_tag_proto_info *info) 32853da0ebaSVladimir Oltean { 32953da0ebaSVladimir Oltean if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) 33053da0ebaSVladimir Oltean return true; 33153da0ebaSVladimir Oltean 33253da0ebaSVladimir Oltean return false; 33353da0ebaSVladimir Oltean } 33453da0ebaSVladimir Oltean 33553da0ebaSVladimir Oltean static int dsa_switch_change_tag_proto(struct dsa_switch *ds, 33653da0ebaSVladimir Oltean struct dsa_notifier_tag_proto_info *info) 33753da0ebaSVladimir Oltean { 33853da0ebaSVladimir Oltean const struct dsa_device_ops *tag_ops = info->tag_ops; 33953da0ebaSVladimir Oltean int port, err; 34053da0ebaSVladimir Oltean 34153da0ebaSVladimir Oltean if (!ds->ops->change_tag_protocol) 34253da0ebaSVladimir Oltean return -EOPNOTSUPP; 34353da0ebaSVladimir Oltean 34453da0ebaSVladimir Oltean ASSERT_RTNL(); 34553da0ebaSVladimir Oltean 34653da0ebaSVladimir Oltean for (port = 0; port < ds->num_ports; port++) { 34753da0ebaSVladimir Oltean if (dsa_switch_tag_proto_match(ds, port, info)) { 34853da0ebaSVladimir Oltean err = ds->ops->change_tag_protocol(ds, port, 34953da0ebaSVladimir Oltean tag_ops->proto); 35053da0ebaSVladimir Oltean if (err) 35153da0ebaSVladimir Oltean return err; 35253da0ebaSVladimir Oltean 35353da0ebaSVladimir Oltean if (dsa_is_cpu_port(ds, port)) 35453da0ebaSVladimir Oltean dsa_port_set_tag_protocol(dsa_to_port(ds, port), 35553da0ebaSVladimir Oltean tag_ops); 35653da0ebaSVladimir Oltean } 35753da0ebaSVladimir Oltean } 35853da0ebaSVladimir Oltean 35953da0ebaSVladimir Oltean /* Now that changing the tag protocol can no longer fail, let's update 36053da0ebaSVladimir Oltean * the remaining bits which are "duplicated for faster access", and the 36153da0ebaSVladimir Oltean * bits that depend on the tagger, such as the MTU. 36253da0ebaSVladimir Oltean */ 36353da0ebaSVladimir Oltean for (port = 0; port < ds->num_ports; port++) { 36453da0ebaSVladimir Oltean if (dsa_is_user_port(ds, port)) { 36553da0ebaSVladimir Oltean struct net_device *slave; 36653da0ebaSVladimir Oltean 36753da0ebaSVladimir Oltean slave = dsa_to_port(ds, port)->slave; 36853da0ebaSVladimir Oltean dsa_slave_setup_tagger(slave); 36953da0ebaSVladimir Oltean 37053da0ebaSVladimir Oltean /* rtnl_mutex is held in dsa_tree_change_tag_proto */ 37153da0ebaSVladimir Oltean dsa_slave_change_mtu(slave, slave->mtu); 37253da0ebaSVladimir Oltean } 37353da0ebaSVladimir Oltean } 37453da0ebaSVladimir Oltean 37553da0ebaSVladimir Oltean return 0; 37653da0ebaSVladimir Oltean } 37753da0ebaSVladimir Oltean 378c595c433SHoratiu Vultur static bool dsa_switch_mrp_match(struct dsa_switch *ds, int port, 379c595c433SHoratiu Vultur struct dsa_notifier_mrp_info *info) 380c595c433SHoratiu Vultur { 381c595c433SHoratiu Vultur if (ds->index == info->sw_index && port == info->port) 382c595c433SHoratiu Vultur return true; 383c595c433SHoratiu Vultur 384c595c433SHoratiu Vultur if (dsa_is_dsa_port(ds, port)) 385c595c433SHoratiu Vultur return true; 386c595c433SHoratiu Vultur 387c595c433SHoratiu Vultur return false; 388c595c433SHoratiu Vultur } 389c595c433SHoratiu Vultur 390c595c433SHoratiu Vultur static int dsa_switch_mrp_add(struct dsa_switch *ds, 391c595c433SHoratiu Vultur struct dsa_notifier_mrp_info *info) 392c595c433SHoratiu Vultur { 393c595c433SHoratiu Vultur int err = 0; 394c595c433SHoratiu Vultur int port; 395c595c433SHoratiu Vultur 396c595c433SHoratiu Vultur if (!ds->ops->port_mrp_add) 397c595c433SHoratiu Vultur return -EOPNOTSUPP; 398c595c433SHoratiu Vultur 399c595c433SHoratiu Vultur for (port = 0; port < ds->num_ports; port++) { 400c595c433SHoratiu Vultur if (dsa_switch_mrp_match(ds, port, info)) { 401c595c433SHoratiu Vultur err = ds->ops->port_mrp_add(ds, port, info->mrp); 402c595c433SHoratiu Vultur if (err) 403c595c433SHoratiu Vultur break; 404c595c433SHoratiu Vultur } 405c595c433SHoratiu Vultur } 406c595c433SHoratiu Vultur 407c595c433SHoratiu Vultur return err; 408c595c433SHoratiu Vultur } 409c595c433SHoratiu Vultur 410c595c433SHoratiu Vultur static int dsa_switch_mrp_del(struct dsa_switch *ds, 411c595c433SHoratiu Vultur struct dsa_notifier_mrp_info *info) 412c595c433SHoratiu Vultur { 413c595c433SHoratiu Vultur if (!ds->ops->port_mrp_del) 414c595c433SHoratiu Vultur return -EOPNOTSUPP; 415c595c433SHoratiu Vultur 416c595c433SHoratiu Vultur if (ds->index == info->sw_index) 417c595c433SHoratiu Vultur return ds->ops->port_mrp_del(ds, info->port, info->mrp); 418c595c433SHoratiu Vultur 419c595c433SHoratiu Vultur return 0; 420c595c433SHoratiu Vultur } 421c595c433SHoratiu Vultur 422c595c433SHoratiu Vultur static bool 423c595c433SHoratiu Vultur dsa_switch_mrp_ring_role_match(struct dsa_switch *ds, int port, 424c595c433SHoratiu Vultur struct dsa_notifier_mrp_ring_role_info *info) 425c595c433SHoratiu Vultur { 426c595c433SHoratiu Vultur if (ds->index == info->sw_index && port == info->port) 427c595c433SHoratiu Vultur return true; 428c595c433SHoratiu Vultur 429c595c433SHoratiu Vultur if (dsa_is_dsa_port(ds, port)) 430c595c433SHoratiu Vultur return true; 431c595c433SHoratiu Vultur 432c595c433SHoratiu Vultur return false; 433c595c433SHoratiu Vultur } 434c595c433SHoratiu Vultur 435c595c433SHoratiu Vultur static int 436c595c433SHoratiu Vultur dsa_switch_mrp_add_ring_role(struct dsa_switch *ds, 437c595c433SHoratiu Vultur struct dsa_notifier_mrp_ring_role_info *info) 438c595c433SHoratiu Vultur { 439c595c433SHoratiu Vultur int err = 0; 440c595c433SHoratiu Vultur int port; 441c595c433SHoratiu Vultur 442c595c433SHoratiu Vultur if (!ds->ops->port_mrp_add) 443c595c433SHoratiu Vultur return -EOPNOTSUPP; 444c595c433SHoratiu Vultur 445c595c433SHoratiu Vultur for (port = 0; port < ds->num_ports; port++) { 446c595c433SHoratiu Vultur if (dsa_switch_mrp_ring_role_match(ds, port, info)) { 447c595c433SHoratiu Vultur err = ds->ops->port_mrp_add_ring_role(ds, port, 448c595c433SHoratiu Vultur info->mrp); 449c595c433SHoratiu Vultur if (err) 450c595c433SHoratiu Vultur break; 451c595c433SHoratiu Vultur } 452c595c433SHoratiu Vultur } 453c595c433SHoratiu Vultur 454c595c433SHoratiu Vultur return err; 455c595c433SHoratiu Vultur } 456c595c433SHoratiu Vultur 457c595c433SHoratiu Vultur static int 458c595c433SHoratiu Vultur dsa_switch_mrp_del_ring_role(struct dsa_switch *ds, 459c595c433SHoratiu Vultur struct dsa_notifier_mrp_ring_role_info *info) 460c595c433SHoratiu Vultur { 461c595c433SHoratiu Vultur if (!ds->ops->port_mrp_del) 462c595c433SHoratiu Vultur return -EOPNOTSUPP; 463c595c433SHoratiu Vultur 464c595c433SHoratiu Vultur if (ds->index == info->sw_index) 465c595c433SHoratiu Vultur return ds->ops->port_mrp_del_ring_role(ds, info->port, 466c595c433SHoratiu Vultur info->mrp); 467c595c433SHoratiu Vultur 468c595c433SHoratiu Vultur return 0; 469c595c433SHoratiu Vultur } 470c595c433SHoratiu Vultur 471f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb, 472f515f192SVivien Didelot unsigned long event, void *info) 473f515f192SVivien Didelot { 474f515f192SVivien Didelot struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb); 475f515f192SVivien Didelot int err; 476f515f192SVivien Didelot 477f515f192SVivien Didelot switch (event) { 4781faabf74SVivien Didelot case DSA_NOTIFIER_AGEING_TIME: 4791faabf74SVivien Didelot err = dsa_switch_ageing_time(ds, info); 4801faabf74SVivien Didelot break; 48104d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_JOIN: 48204d3a4c6SVivien Didelot err = dsa_switch_bridge_join(ds, info); 48304d3a4c6SVivien Didelot break; 48404d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_LEAVE: 48504d3a4c6SVivien Didelot err = dsa_switch_bridge_leave(ds, info); 48604d3a4c6SVivien Didelot break; 487685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_ADD: 488685fb6a4SVivien Didelot err = dsa_switch_fdb_add(ds, info); 489685fb6a4SVivien Didelot break; 490685fb6a4SVivien Didelot case DSA_NOTIFIER_FDB_DEL: 491685fb6a4SVivien Didelot err = dsa_switch_fdb_del(ds, info); 492685fb6a4SVivien Didelot break; 49318596f50SGeorge McCollister case DSA_NOTIFIER_HSR_JOIN: 49418596f50SGeorge McCollister err = dsa_switch_hsr_join(ds, info); 49518596f50SGeorge McCollister break; 49618596f50SGeorge McCollister case DSA_NOTIFIER_HSR_LEAVE: 49718596f50SGeorge McCollister err = dsa_switch_hsr_leave(ds, info); 49818596f50SGeorge McCollister break; 499058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_CHANGE: 500058102a6STobias Waldekranz err = dsa_switch_lag_change(ds, info); 501058102a6STobias Waldekranz break; 502058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_JOIN: 503058102a6STobias Waldekranz err = dsa_switch_lag_join(ds, info); 504058102a6STobias Waldekranz break; 505058102a6STobias Waldekranz case DSA_NOTIFIER_LAG_LEAVE: 506058102a6STobias Waldekranz err = dsa_switch_lag_leave(ds, info); 507058102a6STobias Waldekranz break; 5088ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_ADD: 5098ae5bcdcSVivien Didelot err = dsa_switch_mdb_add(ds, info); 5108ae5bcdcSVivien Didelot break; 5118ae5bcdcSVivien Didelot case DSA_NOTIFIER_MDB_DEL: 5128ae5bcdcSVivien Didelot err = dsa_switch_mdb_del(ds, info); 5138ae5bcdcSVivien Didelot break; 514d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_ADD: 515d0c627b8SVivien Didelot err = dsa_switch_vlan_add(ds, info); 516d0c627b8SVivien Didelot break; 517d0c627b8SVivien Didelot case DSA_NOTIFIER_VLAN_DEL: 518d0c627b8SVivien Didelot err = dsa_switch_vlan_del(ds, info); 519d0c627b8SVivien Didelot break; 520bfcb8132SVladimir Oltean case DSA_NOTIFIER_MTU: 521bfcb8132SVladimir Oltean err = dsa_switch_mtu(ds, info); 522bfcb8132SVladimir Oltean break; 52353da0ebaSVladimir Oltean case DSA_NOTIFIER_TAG_PROTO: 52453da0ebaSVladimir Oltean err = dsa_switch_change_tag_proto(ds, info); 52553da0ebaSVladimir Oltean break; 526c595c433SHoratiu Vultur case DSA_NOTIFIER_MRP_ADD: 527c595c433SHoratiu Vultur err = dsa_switch_mrp_add(ds, info); 528c595c433SHoratiu Vultur break; 529c595c433SHoratiu Vultur case DSA_NOTIFIER_MRP_DEL: 530c595c433SHoratiu Vultur err = dsa_switch_mrp_del(ds, info); 531c595c433SHoratiu Vultur break; 532c595c433SHoratiu Vultur case DSA_NOTIFIER_MRP_ADD_RING_ROLE: 533c595c433SHoratiu Vultur err = dsa_switch_mrp_add_ring_role(ds, info); 534c595c433SHoratiu Vultur break; 535c595c433SHoratiu Vultur case DSA_NOTIFIER_MRP_DEL_RING_ROLE: 536c595c433SHoratiu Vultur err = dsa_switch_mrp_del_ring_role(ds, info); 537c595c433SHoratiu Vultur break; 538f515f192SVivien Didelot default: 539f515f192SVivien Didelot err = -EOPNOTSUPP; 540f515f192SVivien Didelot break; 541f515f192SVivien Didelot } 542f515f192SVivien Didelot 543f515f192SVivien Didelot if (err) 544f515f192SVivien Didelot dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n", 545f515f192SVivien Didelot event, err); 546f515f192SVivien Didelot 547f515f192SVivien Didelot return notifier_from_errno(err); 548f515f192SVivien Didelot } 549f515f192SVivien Didelot 550f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds) 551f515f192SVivien Didelot { 552f515f192SVivien Didelot ds->nb.notifier_call = dsa_switch_event; 553f515f192SVivien Didelot 554f515f192SVivien Didelot return raw_notifier_chain_register(&ds->dst->nh, &ds->nb); 555f515f192SVivien Didelot } 556f515f192SVivien Didelot 557f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds) 558f515f192SVivien Didelot { 559f515f192SVivien Didelot int err; 560f515f192SVivien Didelot 561f515f192SVivien Didelot err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb); 562f515f192SVivien Didelot if (err) 563f515f192SVivien Didelot dev_err(ds->dev, "failed to unregister notifier (%d)\n", err); 564f515f192SVivien Didelot } 565