162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/if_bridge.h> 462306a36Sopenharmony_ci#include <net/switchdev.h> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "lan966x_main.h" 762306a36Sopenharmony_ci 862306a36Sopenharmony_cistatic struct notifier_block lan966x_netdevice_nb __read_mostly; 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistatic void lan966x_port_set_mcast_ip_flood(struct lan966x_port *port, 1162306a36Sopenharmony_ci u32 pgid_ip) 1262306a36Sopenharmony_ci{ 1362306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 1462306a36Sopenharmony_ci u32 flood_mask_ip; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci flood_mask_ip = lan_rd(lan966x, ANA_PGID(pgid_ip)); 1762306a36Sopenharmony_ci flood_mask_ip = ANA_PGID_PGID_GET(flood_mask_ip); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci /* If mcast snooping is not enabled then use mcast flood mask 2062306a36Sopenharmony_ci * to decide to enable multicast flooding or not. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci if (!port->mcast_ena) { 2362306a36Sopenharmony_ci u32 flood_mask; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci flood_mask = lan_rd(lan966x, ANA_PGID(PGID_MC)); 2662306a36Sopenharmony_ci flood_mask = ANA_PGID_PGID_GET(flood_mask); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (flood_mask & BIT(port->chip_port)) 2962306a36Sopenharmony_ci flood_mask_ip |= BIT(port->chip_port); 3062306a36Sopenharmony_ci else 3162306a36Sopenharmony_ci flood_mask_ip &= ~BIT(port->chip_port); 3262306a36Sopenharmony_ci } else { 3362306a36Sopenharmony_ci flood_mask_ip &= ~BIT(port->chip_port); 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci lan_rmw(ANA_PGID_PGID_SET(flood_mask_ip), 3762306a36Sopenharmony_ci ANA_PGID_PGID, 3862306a36Sopenharmony_ci lan966x, ANA_PGID(pgid_ip)); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void lan966x_port_set_mcast_flood(struct lan966x_port *port, 4262306a36Sopenharmony_ci bool enabled) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci u32 val = lan_rd(port->lan966x, ANA_PGID(PGID_MC)); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci val = ANA_PGID_PGID_GET(val); 4762306a36Sopenharmony_ci if (enabled) 4862306a36Sopenharmony_ci val |= BIT(port->chip_port); 4962306a36Sopenharmony_ci else 5062306a36Sopenharmony_ci val &= ~BIT(port->chip_port); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci lan_rmw(ANA_PGID_PGID_SET(val), 5362306a36Sopenharmony_ci ANA_PGID_PGID, 5462306a36Sopenharmony_ci port->lan966x, ANA_PGID(PGID_MC)); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (!port->mcast_ena) { 5762306a36Sopenharmony_ci lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV4); 5862306a36Sopenharmony_ci lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV6); 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic void lan966x_port_set_ucast_flood(struct lan966x_port *port, 6362306a36Sopenharmony_ci bool enabled) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci u32 val = lan_rd(port->lan966x, ANA_PGID(PGID_UC)); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci val = ANA_PGID_PGID_GET(val); 6862306a36Sopenharmony_ci if (enabled) 6962306a36Sopenharmony_ci val |= BIT(port->chip_port); 7062306a36Sopenharmony_ci else 7162306a36Sopenharmony_ci val &= ~BIT(port->chip_port); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci lan_rmw(ANA_PGID_PGID_SET(val), 7462306a36Sopenharmony_ci ANA_PGID_PGID, 7562306a36Sopenharmony_ci port->lan966x, ANA_PGID(PGID_UC)); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void lan966x_port_set_bcast_flood(struct lan966x_port *port, 7962306a36Sopenharmony_ci bool enabled) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci u32 val = lan_rd(port->lan966x, ANA_PGID(PGID_BC)); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci val = ANA_PGID_PGID_GET(val); 8462306a36Sopenharmony_ci if (enabled) 8562306a36Sopenharmony_ci val |= BIT(port->chip_port); 8662306a36Sopenharmony_ci else 8762306a36Sopenharmony_ci val &= ~BIT(port->chip_port); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci lan_rmw(ANA_PGID_PGID_SET(val), 9062306a36Sopenharmony_ci ANA_PGID_PGID, 9162306a36Sopenharmony_ci port->lan966x, ANA_PGID(PGID_BC)); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void lan966x_port_set_learning(struct lan966x_port *port, bool enabled) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci lan_rmw(ANA_PORT_CFG_LEARN_ENA_SET(enabled), 9762306a36Sopenharmony_ci ANA_PORT_CFG_LEARN_ENA, 9862306a36Sopenharmony_ci port->lan966x, ANA_PORT_CFG(port->chip_port)); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci port->learn_ena = enabled; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void lan966x_port_bridge_flags(struct lan966x_port *port, 10462306a36Sopenharmony_ci struct switchdev_brport_flags flags) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci if (flags.mask & BR_MCAST_FLOOD) 10762306a36Sopenharmony_ci lan966x_port_set_mcast_flood(port, 10862306a36Sopenharmony_ci !!(flags.val & BR_MCAST_FLOOD)); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (flags.mask & BR_FLOOD) 11162306a36Sopenharmony_ci lan966x_port_set_ucast_flood(port, 11262306a36Sopenharmony_ci !!(flags.val & BR_FLOOD)); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (flags.mask & BR_BCAST_FLOOD) 11562306a36Sopenharmony_ci lan966x_port_set_bcast_flood(port, 11662306a36Sopenharmony_ci !!(flags.val & BR_BCAST_FLOOD)); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (flags.mask & BR_LEARNING) 11962306a36Sopenharmony_ci lan966x_port_set_learning(port, 12062306a36Sopenharmony_ci !!(flags.val & BR_LEARNING)); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int lan966x_port_pre_bridge_flags(struct lan966x_port *port, 12462306a36Sopenharmony_ci struct switchdev_brport_flags flags) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci if (flags.mask & ~(BR_MCAST_FLOOD | BR_FLOOD | BR_BCAST_FLOOD | 12762306a36Sopenharmony_ci BR_LEARNING)) 12862306a36Sopenharmony_ci return -EINVAL; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_civoid lan966x_update_fwd_mask(struct lan966x *lan966x) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci int i; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for (i = 0; i < lan966x->num_phys_ports; i++) { 13862306a36Sopenharmony_ci struct lan966x_port *port = lan966x->ports[i]; 13962306a36Sopenharmony_ci unsigned long mask = 0; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (port && lan966x->bridge_fwd_mask & BIT(i)) { 14262306a36Sopenharmony_ci mask = lan966x->bridge_fwd_mask & ~BIT(i); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (port->bond) 14562306a36Sopenharmony_ci mask &= ~lan966x_lag_get_mask(lan966x, 14662306a36Sopenharmony_ci port->bond); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci mask |= BIT(CPU_PORT); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci lan_wr(ANA_PGID_PGID_SET(mask), 15262306a36Sopenharmony_ci lan966x, ANA_PGID(PGID_SRC + i)); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_civoid lan966x_port_stp_state_set(struct lan966x_port *port, u8 state) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 15962306a36Sopenharmony_ci bool learn_ena = false; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if ((state == BR_STATE_FORWARDING || state == BR_STATE_LEARNING) && 16262306a36Sopenharmony_ci port->learn_ena) 16362306a36Sopenharmony_ci learn_ena = true; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (state == BR_STATE_FORWARDING) 16662306a36Sopenharmony_ci lan966x->bridge_fwd_mask |= BIT(port->chip_port); 16762306a36Sopenharmony_ci else 16862306a36Sopenharmony_ci lan966x->bridge_fwd_mask &= ~BIT(port->chip_port); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci lan_rmw(ANA_PORT_CFG_LEARN_ENA_SET(learn_ena), 17162306a36Sopenharmony_ci ANA_PORT_CFG_LEARN_ENA, 17262306a36Sopenharmony_ci lan966x, ANA_PORT_CFG(port->chip_port)); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci lan966x_update_fwd_mask(lan966x); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_civoid lan966x_port_ageing_set(struct lan966x_port *port, 17862306a36Sopenharmony_ci unsigned long ageing_clock_t) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); 18162306a36Sopenharmony_ci u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci lan966x_mac_set_ageing(port->lan966x, ageing_time); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic void lan966x_port_mc_set(struct lan966x_port *port, bool mcast_ena) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci port->mcast_ena = mcast_ena; 19162306a36Sopenharmony_ci if (mcast_ena) 19262306a36Sopenharmony_ci lan966x_mdb_restore_entries(lan966x); 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci lan966x_mdb_clear_entries(lan966x); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci lan_rmw(ANA_CPU_FWD_CFG_IGMP_REDIR_ENA_SET(mcast_ena) | 19762306a36Sopenharmony_ci ANA_CPU_FWD_CFG_MLD_REDIR_ENA_SET(mcast_ena) | 19862306a36Sopenharmony_ci ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA_SET(mcast_ena), 19962306a36Sopenharmony_ci ANA_CPU_FWD_CFG_IGMP_REDIR_ENA | 20062306a36Sopenharmony_ci ANA_CPU_FWD_CFG_MLD_REDIR_ENA | 20162306a36Sopenharmony_ci ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA, 20262306a36Sopenharmony_ci lan966x, ANA_CPU_FWD_CFG(port->chip_port)); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV4); 20562306a36Sopenharmony_ci lan966x_port_set_mcast_ip_flood(port, PGID_MCIPV6); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int lan966x_port_attr_set(struct net_device *dev, const void *ctx, 20962306a36Sopenharmony_ci const struct switchdev_attr *attr, 21062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 21362306a36Sopenharmony_ci int err = 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (ctx && ctx != port) 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci switch (attr->id) { 21962306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: 22062306a36Sopenharmony_ci lan966x_port_bridge_flags(port, attr->u.brport_flags); 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: 22362306a36Sopenharmony_ci err = lan966x_port_pre_bridge_flags(port, attr->u.brport_flags); 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 22662306a36Sopenharmony_ci lan966x_port_stp_state_set(port, attr->u.stp_state); 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: 22962306a36Sopenharmony_ci lan966x_port_ageing_set(port, attr->u.ageing_time); 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: 23262306a36Sopenharmony_ci lan966x_vlan_port_set_vlan_aware(port, attr->u.vlan_filtering); 23362306a36Sopenharmony_ci lan966x_vlan_port_apply(port); 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: 23662306a36Sopenharmony_ci lan966x_port_mc_set(port, !attr->u.mc_disabled); 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci default: 23962306a36Sopenharmony_ci err = -EOPNOTSUPP; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return err; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int lan966x_port_bridge_join(struct lan966x_port *port, 24762306a36Sopenharmony_ci struct net_device *brport_dev, 24862306a36Sopenharmony_ci struct net_device *bridge, 24962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct switchdev_brport_flags flags = {0}; 25262306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 25362306a36Sopenharmony_ci struct net_device *dev = port->dev; 25462306a36Sopenharmony_ci int err; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (!lan966x->bridge_mask) { 25762306a36Sopenharmony_ci lan966x->bridge = bridge; 25862306a36Sopenharmony_ci } else { 25962306a36Sopenharmony_ci if (lan966x->bridge != bridge) { 26062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Not allow to add port to different bridge"); 26162306a36Sopenharmony_ci return -ENODEV; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci err = switchdev_bridge_port_offload(brport_dev, dev, port, 26662306a36Sopenharmony_ci &lan966x_switchdev_nb, 26762306a36Sopenharmony_ci &lan966x_switchdev_blocking_nb, 26862306a36Sopenharmony_ci false, extack); 26962306a36Sopenharmony_ci if (err) 27062306a36Sopenharmony_ci return err; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci lan966x->bridge_mask |= BIT(port->chip_port); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; 27562306a36Sopenharmony_ci flags.val = flags.mask; 27662306a36Sopenharmony_ci lan966x_port_bridge_flags(port, flags); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic void lan966x_port_bridge_leave(struct lan966x_port *port, 28262306a36Sopenharmony_ci struct net_device *bridge) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct switchdev_brport_flags flags = {0}; 28562306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; 28862306a36Sopenharmony_ci flags.val = flags.mask & ~BR_LEARNING; 28962306a36Sopenharmony_ci lan966x_port_bridge_flags(port, flags); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci lan966x->bridge_mask &= ~BIT(port->chip_port); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (!lan966x->bridge_mask) 29462306a36Sopenharmony_ci lan966x->bridge = NULL; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* Set the port back to host mode */ 29762306a36Sopenharmony_ci lan966x_vlan_port_set_vlan_aware(port, false); 29862306a36Sopenharmony_ci lan966x_vlan_port_set_vid(port, HOST_PVID, false, false); 29962306a36Sopenharmony_ci lan966x_vlan_port_apply(port); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ciint lan966x_port_changeupper(struct net_device *dev, 30362306a36Sopenharmony_ci struct net_device *brport_dev, 30462306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 30762306a36Sopenharmony_ci struct netlink_ext_ack *extack; 30862306a36Sopenharmony_ci int err = 0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci extack = netdev_notifier_info_to_extack(&info->info); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (netif_is_bridge_master(info->upper_dev)) { 31362306a36Sopenharmony_ci if (info->linking) 31462306a36Sopenharmony_ci err = lan966x_port_bridge_join(port, brport_dev, 31562306a36Sopenharmony_ci info->upper_dev, 31662306a36Sopenharmony_ci extack); 31762306a36Sopenharmony_ci else 31862306a36Sopenharmony_ci lan966x_port_bridge_leave(port, info->upper_dev); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (netif_is_lag_master(info->upper_dev)) { 32262306a36Sopenharmony_ci if (info->linking) 32362306a36Sopenharmony_ci err = lan966x_lag_port_join(port, info->upper_dev, 32462306a36Sopenharmony_ci info->upper_dev, 32562306a36Sopenharmony_ci extack); 32662306a36Sopenharmony_ci else 32762306a36Sopenharmony_ci lan966x_lag_port_leave(port, info->upper_dev); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return err; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ciint lan966x_port_prechangeupper(struct net_device *dev, 33462306a36Sopenharmony_ci struct net_device *brport_dev, 33562306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 33862306a36Sopenharmony_ci int err = NOTIFY_DONE; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (netif_is_bridge_master(info->upper_dev) && !info->linking) { 34162306a36Sopenharmony_ci switchdev_bridge_port_unoffload(port->dev, port, NULL, NULL); 34262306a36Sopenharmony_ci lan966x_fdb_flush_workqueue(port->lan966x); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (netif_is_lag_master(info->upper_dev)) { 34662306a36Sopenharmony_ci err = lan966x_lag_port_prechangeupper(dev, info); 34762306a36Sopenharmony_ci if (err || info->linking) 34862306a36Sopenharmony_ci return err; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci switchdev_bridge_port_unoffload(brport_dev, port, NULL, NULL); 35162306a36Sopenharmony_ci lan966x_fdb_flush_workqueue(port->lan966x); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return err; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int lan966x_foreign_bridging_check(struct net_device *upper, 35862306a36Sopenharmony_ci bool *has_foreign, 35962306a36Sopenharmony_ci bool *seen_lan966x, 36062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct lan966x *lan966x = NULL; 36362306a36Sopenharmony_ci struct net_device *dev; 36462306a36Sopenharmony_ci struct list_head *iter; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (!netif_is_bridge_master(upper) && 36762306a36Sopenharmony_ci !netif_is_lag_master(upper)) 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci netdev_for_each_lower_dev(upper, dev, iter) { 37162306a36Sopenharmony_ci if (lan966x_netdevice_check(dev)) { 37262306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (lan966x) { 37562306a36Sopenharmony_ci /* Upper already has at least one port of a 37662306a36Sopenharmony_ci * lan966x switch inside it, check that it's 37762306a36Sopenharmony_ci * the same instance of the driver. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ci if (port->lan966x != lan966x) { 38062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 38162306a36Sopenharmony_ci "Bridging between multiple lan966x switches disallowed"); 38262306a36Sopenharmony_ci return -EINVAL; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci } else { 38562306a36Sopenharmony_ci /* This is the first lan966x port inside this 38662306a36Sopenharmony_ci * upper device 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci lan966x = port->lan966x; 38962306a36Sopenharmony_ci *seen_lan966x = true; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci } else if (netif_is_lag_master(dev)) { 39262306a36Sopenharmony_ci /* Allow to have bond interfaces that have only lan966x 39362306a36Sopenharmony_ci * devices 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ci if (lan966x_foreign_bridging_check(dev, has_foreign, 39662306a36Sopenharmony_ci seen_lan966x, 39762306a36Sopenharmony_ci extack)) 39862306a36Sopenharmony_ci return -EINVAL; 39962306a36Sopenharmony_ci } else { 40062306a36Sopenharmony_ci *has_foreign = true; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (*seen_lan966x && *has_foreign) { 40462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 40562306a36Sopenharmony_ci "Bridging lan966x ports with foreign interfaces disallowed"); 40662306a36Sopenharmony_ci return -EINVAL; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic int lan966x_bridge_check(struct net_device *dev, 41462306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci bool has_foreign = false; 41762306a36Sopenharmony_ci bool seen_lan966x = false; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return lan966x_foreign_bridging_check(info->upper_dev, 42062306a36Sopenharmony_ci &has_foreign, 42162306a36Sopenharmony_ci &seen_lan966x, 42262306a36Sopenharmony_ci info->info.extack); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic int lan966x_netdevice_port_event(struct net_device *dev, 42662306a36Sopenharmony_ci struct notifier_block *nb, 42762306a36Sopenharmony_ci unsigned long event, void *ptr) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci int err = 0; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (!lan966x_netdevice_check(dev)) { 43262306a36Sopenharmony_ci switch (event) { 43362306a36Sopenharmony_ci case NETDEV_CHANGEUPPER: 43462306a36Sopenharmony_ci case NETDEV_PRECHANGEUPPER: 43562306a36Sopenharmony_ci err = lan966x_bridge_check(dev, ptr); 43662306a36Sopenharmony_ci if (err) 43762306a36Sopenharmony_ci return err; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (netif_is_lag_master(dev)) { 44062306a36Sopenharmony_ci if (event == NETDEV_CHANGEUPPER) 44162306a36Sopenharmony_ci err = lan966x_lag_netdev_changeupper(dev, 44262306a36Sopenharmony_ci ptr); 44362306a36Sopenharmony_ci else 44462306a36Sopenharmony_ci err = lan966x_lag_netdev_prechangeupper(dev, 44562306a36Sopenharmony_ci ptr); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return err; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci default: 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci switch (event) { 45862306a36Sopenharmony_ci case NETDEV_PRECHANGEUPPER: 45962306a36Sopenharmony_ci err = lan966x_port_prechangeupper(dev, dev, ptr); 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci case NETDEV_CHANGEUPPER: 46262306a36Sopenharmony_ci err = lan966x_bridge_check(dev, ptr); 46362306a36Sopenharmony_ci if (err) 46462306a36Sopenharmony_ci return err; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci err = lan966x_port_changeupper(dev, dev, ptr); 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci case NETDEV_CHANGELOWERSTATE: 46962306a36Sopenharmony_ci err = lan966x_lag_port_changelowerstate(dev, ptr); 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return err; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int lan966x_netdevice_event(struct notifier_block *nb, 47762306a36Sopenharmony_ci unsigned long event, void *ptr) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 48062306a36Sopenharmony_ci int ret; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci ret = lan966x_netdevice_port_event(dev, nb, event, ptr); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return notifier_from_errno(ret); 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic bool lan966x_foreign_dev_check(const struct net_device *dev, 48862306a36Sopenharmony_ci const struct net_device *foreign_dev) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 49162306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 49262306a36Sopenharmony_ci int i; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (netif_is_bridge_master(foreign_dev)) 49562306a36Sopenharmony_ci if (lan966x->bridge == foreign_dev) 49662306a36Sopenharmony_ci return false; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (netif_is_lag_master(foreign_dev)) 49962306a36Sopenharmony_ci for (i = 0; i < lan966x->num_phys_ports; ++i) 50062306a36Sopenharmony_ci if (lan966x->ports[i] && 50162306a36Sopenharmony_ci lan966x->ports[i]->bond == foreign_dev) 50262306a36Sopenharmony_ci return false; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return true; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic int lan966x_switchdev_event(struct notifier_block *nb, 50862306a36Sopenharmony_ci unsigned long event, void *ptr) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 51162306a36Sopenharmony_ci int err; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci switch (event) { 51462306a36Sopenharmony_ci case SWITCHDEV_PORT_ATTR_SET: 51562306a36Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 51662306a36Sopenharmony_ci lan966x_netdevice_check, 51762306a36Sopenharmony_ci lan966x_port_attr_set); 51862306a36Sopenharmony_ci return notifier_from_errno(err); 51962306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 52062306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 52162306a36Sopenharmony_ci err = switchdev_handle_fdb_event_to_device(dev, event, ptr, 52262306a36Sopenharmony_ci lan966x_netdevice_check, 52362306a36Sopenharmony_ci lan966x_foreign_dev_check, 52462306a36Sopenharmony_ci lan966x_handle_fdb); 52562306a36Sopenharmony_ci return notifier_from_errno(err); 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci return NOTIFY_DONE; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic int lan966x_handle_port_vlan_add(struct lan966x_port *port, 53262306a36Sopenharmony_ci const struct switchdev_obj *obj) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *v = SWITCHDEV_OBJ_PORT_VLAN(obj); 53562306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!netif_is_bridge_master(obj->orig_dev)) 53862306a36Sopenharmony_ci lan966x_vlan_port_add_vlan(port, v->vid, 53962306a36Sopenharmony_ci v->flags & BRIDGE_VLAN_INFO_PVID, 54062306a36Sopenharmony_ci v->flags & BRIDGE_VLAN_INFO_UNTAGGED); 54162306a36Sopenharmony_ci else 54262306a36Sopenharmony_ci lan966x_vlan_cpu_add_vlan(lan966x, v->vid); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int lan966x_handle_port_obj_add(struct net_device *dev, const void *ctx, 54862306a36Sopenharmony_ci const struct switchdev_obj *obj, 54962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 55262306a36Sopenharmony_ci int err; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (ctx && ctx != port) 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci switch (obj->id) { 55862306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 55962306a36Sopenharmony_ci err = lan966x_handle_port_vlan_add(port, obj); 56062306a36Sopenharmony_ci break; 56162306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 56262306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_HOST_MDB: 56362306a36Sopenharmony_ci err = lan966x_handle_port_mdb_add(port, obj); 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci default: 56662306a36Sopenharmony_ci err = -EOPNOTSUPP; 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return err; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int lan966x_handle_port_vlan_del(struct lan966x_port *port, 57462306a36Sopenharmony_ci const struct switchdev_obj *obj) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *v = SWITCHDEV_OBJ_PORT_VLAN(obj); 57762306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!netif_is_bridge_master(obj->orig_dev)) 58062306a36Sopenharmony_ci lan966x_vlan_port_del_vlan(port, v->vid); 58162306a36Sopenharmony_ci else 58262306a36Sopenharmony_ci lan966x_vlan_cpu_del_vlan(lan966x, v->vid); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci return 0; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int lan966x_handle_port_obj_del(struct net_device *dev, const void *ctx, 58862306a36Sopenharmony_ci const struct switchdev_obj *obj) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 59162306a36Sopenharmony_ci int err; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (ctx && ctx != port) 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci switch (obj->id) { 59762306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 59862306a36Sopenharmony_ci err = lan966x_handle_port_vlan_del(port, obj); 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 60162306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_HOST_MDB: 60262306a36Sopenharmony_ci err = lan966x_handle_port_mdb_del(port, obj); 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci default: 60562306a36Sopenharmony_ci err = -EOPNOTSUPP; 60662306a36Sopenharmony_ci break; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci return err; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int lan966x_switchdev_blocking_event(struct notifier_block *nb, 61362306a36Sopenharmony_ci unsigned long event, 61462306a36Sopenharmony_ci void *ptr) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 61762306a36Sopenharmony_ci int err; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci switch (event) { 62062306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_ADD: 62162306a36Sopenharmony_ci err = switchdev_handle_port_obj_add(dev, ptr, 62262306a36Sopenharmony_ci lan966x_netdevice_check, 62362306a36Sopenharmony_ci lan966x_handle_port_obj_add); 62462306a36Sopenharmony_ci return notifier_from_errno(err); 62562306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_DEL: 62662306a36Sopenharmony_ci err = switchdev_handle_port_obj_del(dev, ptr, 62762306a36Sopenharmony_ci lan966x_netdevice_check, 62862306a36Sopenharmony_ci lan966x_handle_port_obj_del); 62962306a36Sopenharmony_ci return notifier_from_errno(err); 63062306a36Sopenharmony_ci case SWITCHDEV_PORT_ATTR_SET: 63162306a36Sopenharmony_ci err = switchdev_handle_port_attr_set(dev, ptr, 63262306a36Sopenharmony_ci lan966x_netdevice_check, 63362306a36Sopenharmony_ci lan966x_port_attr_set); 63462306a36Sopenharmony_ci return notifier_from_errno(err); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return NOTIFY_DONE; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic struct notifier_block lan966x_netdevice_nb __read_mostly = { 64162306a36Sopenharmony_ci .notifier_call = lan966x_netdevice_event, 64262306a36Sopenharmony_ci}; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistruct notifier_block lan966x_switchdev_nb __read_mostly = { 64562306a36Sopenharmony_ci .notifier_call = lan966x_switchdev_event, 64662306a36Sopenharmony_ci}; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistruct notifier_block lan966x_switchdev_blocking_nb __read_mostly = { 64962306a36Sopenharmony_ci .notifier_call = lan966x_switchdev_blocking_event, 65062306a36Sopenharmony_ci}; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_civoid lan966x_register_notifier_blocks(void) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci register_netdevice_notifier(&lan966x_netdevice_nb); 65562306a36Sopenharmony_ci register_switchdev_notifier(&lan966x_switchdev_nb); 65662306a36Sopenharmony_ci register_switchdev_blocking_notifier(&lan966x_switchdev_blocking_nb); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_civoid lan966x_unregister_notifier_blocks(void) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci unregister_switchdev_blocking_notifier(&lan966x_switchdev_blocking_nb); 66262306a36Sopenharmony_ci unregister_switchdev_notifier(&lan966x_switchdev_nb); 66362306a36Sopenharmony_ci unregister_netdevice_notifier(&lan966x_netdevice_nb); 66462306a36Sopenharmony_ci} 665