1f515f192SVivien Didelot /* 2f515f192SVivien Didelot * Handling of a single switch chip, part of a switch fabric 3f515f192SVivien Didelot * 4f515f192SVivien Didelot * Copyright (c) 2017 Vivien Didelot <vivien.didelot@savoirfairelinux.com> 5f515f192SVivien Didelot * 6f515f192SVivien Didelot * This program is free software; you can redistribute it and/or modify 7f515f192SVivien Didelot * it under the terms of the GNU General Public License as published by 8f515f192SVivien Didelot * the Free Software Foundation; either version 2 of the License, or 9f515f192SVivien Didelot * (at your option) any later version. 10f515f192SVivien Didelot */ 11f515f192SVivien Didelot 12f515f192SVivien Didelot #include <linux/netdevice.h> 13f515f192SVivien Didelot #include <linux/notifier.h> 14f515f192SVivien Didelot #include <net/dsa.h> 15f515f192SVivien Didelot 16*04d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds, 17*04d3a4c6SVivien Didelot struct dsa_notifier_bridge_info *info) 18*04d3a4c6SVivien Didelot { 19*04d3a4c6SVivien Didelot if (ds->index == info->sw_index && ds->ops->port_bridge_join) 20*04d3a4c6SVivien Didelot return ds->ops->port_bridge_join(ds, info->port, info->br); 21*04d3a4c6SVivien Didelot 22*04d3a4c6SVivien Didelot if (ds->index != info->sw_index) 23*04d3a4c6SVivien Didelot dev_dbg(ds->dev, "crosschip DSA port %d.%d bridged to %s\n", 24*04d3a4c6SVivien Didelot info->sw_index, info->port, netdev_name(info->br)); 25*04d3a4c6SVivien Didelot 26*04d3a4c6SVivien Didelot return 0; 27*04d3a4c6SVivien Didelot } 28*04d3a4c6SVivien Didelot 29*04d3a4c6SVivien Didelot static int dsa_switch_bridge_leave(struct dsa_switch *ds, 30*04d3a4c6SVivien Didelot struct dsa_notifier_bridge_info *info) 31*04d3a4c6SVivien Didelot { 32*04d3a4c6SVivien Didelot if (ds->index == info->sw_index && ds->ops->port_bridge_leave) 33*04d3a4c6SVivien Didelot ds->ops->port_bridge_leave(ds, info->port, info->br); 34*04d3a4c6SVivien Didelot 35*04d3a4c6SVivien Didelot if (ds->index != info->sw_index) 36*04d3a4c6SVivien Didelot dev_dbg(ds->dev, "crosschip DSA port %d.%d unbridged from %s\n", 37*04d3a4c6SVivien Didelot info->sw_index, info->port, netdev_name(info->br)); 38*04d3a4c6SVivien Didelot 39*04d3a4c6SVivien Didelot return 0; 40*04d3a4c6SVivien Didelot } 41*04d3a4c6SVivien Didelot 42f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb, 43f515f192SVivien Didelot unsigned long event, void *info) 44f515f192SVivien Didelot { 45f515f192SVivien Didelot struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb); 46f515f192SVivien Didelot int err; 47f515f192SVivien Didelot 48f515f192SVivien Didelot switch (event) { 49*04d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_JOIN: 50*04d3a4c6SVivien Didelot err = dsa_switch_bridge_join(ds, info); 51*04d3a4c6SVivien Didelot break; 52*04d3a4c6SVivien Didelot case DSA_NOTIFIER_BRIDGE_LEAVE: 53*04d3a4c6SVivien Didelot err = dsa_switch_bridge_leave(ds, info); 54*04d3a4c6SVivien Didelot break; 55f515f192SVivien Didelot default: 56f515f192SVivien Didelot err = -EOPNOTSUPP; 57f515f192SVivien Didelot break; 58f515f192SVivien Didelot } 59f515f192SVivien Didelot 60f515f192SVivien Didelot /* Non-switchdev operations cannot be rolled back. If a DSA driver 61f515f192SVivien Didelot * returns an error during the chained call, switch chips may be in an 62f515f192SVivien Didelot * inconsistent state. 63f515f192SVivien Didelot */ 64f515f192SVivien Didelot if (err) 65f515f192SVivien Didelot dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n", 66f515f192SVivien Didelot event, err); 67f515f192SVivien Didelot 68f515f192SVivien Didelot return notifier_from_errno(err); 69f515f192SVivien Didelot } 70f515f192SVivien Didelot 71f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds) 72f515f192SVivien Didelot { 73f515f192SVivien Didelot ds->nb.notifier_call = dsa_switch_event; 74f515f192SVivien Didelot 75f515f192SVivien Didelot return raw_notifier_chain_register(&ds->dst->nh, &ds->nb); 76f515f192SVivien Didelot } 77f515f192SVivien Didelot 78f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds) 79f515f192SVivien Didelot { 80f515f192SVivien Didelot int err; 81f515f192SVivien Didelot 82f515f192SVivien Didelot err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb); 83f515f192SVivien Didelot if (err) 84f515f192SVivien Didelot dev_err(ds->dev, "failed to unregister notifier (%d)\n", err); 85f515f192SVivien Didelot } 86