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