162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT)
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Microsemi Ocelot Switch driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2017 Microsemi Corporation
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/dsa/ocelot.h>
862306a36Sopenharmony_ci#include <linux/if_bridge.h>
962306a36Sopenharmony_ci#include <linux/iopoll.h>
1062306a36Sopenharmony_ci#include <linux/phy/phy.h>
1162306a36Sopenharmony_ci#include <net/pkt_sched.h>
1262306a36Sopenharmony_ci#include <soc/mscc/ocelot_hsio.h>
1362306a36Sopenharmony_ci#include <soc/mscc/ocelot_vcap.h>
1462306a36Sopenharmony_ci#include "ocelot.h"
1562306a36Sopenharmony_ci#include "ocelot_vcap.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define TABLE_UPDATE_SLEEP_US	10
1862306a36Sopenharmony_ci#define TABLE_UPDATE_TIMEOUT_US	100000
1962306a36Sopenharmony_ci#define MEM_INIT_SLEEP_US	1000
2062306a36Sopenharmony_ci#define MEM_INIT_TIMEOUT_US	100000
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define OCELOT_RSV_VLAN_RANGE_START 4000
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct ocelot_mact_entry {
2562306a36Sopenharmony_ci	u8 mac[ETH_ALEN];
2662306a36Sopenharmony_ci	u16 vid;
2762306a36Sopenharmony_ci	enum macaccess_entry_type type;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Caller must hold &ocelot->mact_lock */
3162306a36Sopenharmony_cistatic inline u32 ocelot_mact_read_macaccess(struct ocelot *ocelot)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	return ocelot_read(ocelot, ANA_TABLES_MACACCESS);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* Caller must hold &ocelot->mact_lock */
3762306a36Sopenharmony_cistatic inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	u32 val;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return readx_poll_timeout(ocelot_mact_read_macaccess,
4262306a36Sopenharmony_ci		ocelot, val,
4362306a36Sopenharmony_ci		(val & ANA_TABLES_MACACCESS_MAC_TABLE_CMD_M) ==
4462306a36Sopenharmony_ci		MACACCESS_CMD_IDLE,
4562306a36Sopenharmony_ci		TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Caller must hold &ocelot->mact_lock */
4962306a36Sopenharmony_cistatic void ocelot_mact_select(struct ocelot *ocelot,
5062306a36Sopenharmony_ci			       const unsigned char mac[ETH_ALEN],
5162306a36Sopenharmony_ci			       unsigned int vid)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	u32 macl = 0, mach = 0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* Set the MAC address to handle and the vlan associated in a format
5662306a36Sopenharmony_ci	 * understood by the hardware.
5762306a36Sopenharmony_ci	 */
5862306a36Sopenharmony_ci	mach |= vid    << 16;
5962306a36Sopenharmony_ci	mach |= mac[0] << 8;
6062306a36Sopenharmony_ci	mach |= mac[1] << 0;
6162306a36Sopenharmony_ci	macl |= mac[2] << 24;
6262306a36Sopenharmony_ci	macl |= mac[3] << 16;
6362306a36Sopenharmony_ci	macl |= mac[4] << 8;
6462306a36Sopenharmony_ci	macl |= mac[5] << 0;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	ocelot_write(ocelot, macl, ANA_TABLES_MACLDATA);
6762306a36Sopenharmony_ci	ocelot_write(ocelot, mach, ANA_TABLES_MACHDATA);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int __ocelot_mact_learn(struct ocelot *ocelot, int port,
7262306a36Sopenharmony_ci			       const unsigned char mac[ETH_ALEN],
7362306a36Sopenharmony_ci			       unsigned int vid, enum macaccess_entry_type type)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	u32 cmd = ANA_TABLES_MACACCESS_VALID |
7662306a36Sopenharmony_ci		ANA_TABLES_MACACCESS_DEST_IDX(port) |
7762306a36Sopenharmony_ci		ANA_TABLES_MACACCESS_ENTRYTYPE(type) |
7862306a36Sopenharmony_ci		ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_LEARN);
7962306a36Sopenharmony_ci	unsigned int mc_ports;
8062306a36Sopenharmony_ci	int err;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* Set MAC_CPU_COPY if the CPU port is used by a multicast entry */
8362306a36Sopenharmony_ci	if (type == ENTRYTYPE_MACv4)
8462306a36Sopenharmony_ci		mc_ports = (mac[1] << 8) | mac[2];
8562306a36Sopenharmony_ci	else if (type == ENTRYTYPE_MACv6)
8662306a36Sopenharmony_ci		mc_ports = (mac[0] << 8) | mac[1];
8762306a36Sopenharmony_ci	else
8862306a36Sopenharmony_ci		mc_ports = 0;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (mc_ports & BIT(ocelot->num_phys_ports))
9162306a36Sopenharmony_ci		cmd |= ANA_TABLES_MACACCESS_MAC_CPU_COPY;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	ocelot_mact_select(ocelot, mac, vid);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* Issue a write command */
9662306a36Sopenharmony_ci	ocelot_write(ocelot, cmd, ANA_TABLES_MACACCESS);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	err = ocelot_mact_wait_for_completion(ocelot);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return err;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ciint ocelot_mact_learn(struct ocelot *ocelot, int port,
10462306a36Sopenharmony_ci		      const unsigned char mac[ETH_ALEN],
10562306a36Sopenharmony_ci		      unsigned int vid, enum macaccess_entry_type type)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	int ret;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	mutex_lock(&ocelot->mact_lock);
11062306a36Sopenharmony_ci	ret = __ocelot_mact_learn(ocelot, port, mac, vid, type);
11162306a36Sopenharmony_ci	mutex_unlock(&ocelot->mact_lock);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return ret;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_mact_learn);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ciint ocelot_mact_forget(struct ocelot *ocelot,
11862306a36Sopenharmony_ci		       const unsigned char mac[ETH_ALEN], unsigned int vid)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	int err;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	mutex_lock(&ocelot->mact_lock);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	ocelot_mact_select(ocelot, mac, vid);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* Issue a forget command */
12762306a36Sopenharmony_ci	ocelot_write(ocelot,
12862306a36Sopenharmony_ci		     ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_FORGET),
12962306a36Sopenharmony_ci		     ANA_TABLES_MACACCESS);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	err = ocelot_mact_wait_for_completion(ocelot);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	mutex_unlock(&ocelot->mact_lock);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return err;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_mact_forget);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ciint ocelot_mact_lookup(struct ocelot *ocelot, int *dst_idx,
14062306a36Sopenharmony_ci		       const unsigned char mac[ETH_ALEN],
14162306a36Sopenharmony_ci		       unsigned int vid, enum macaccess_entry_type *type)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	int val;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	mutex_lock(&ocelot->mact_lock);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	ocelot_mact_select(ocelot, mac, vid);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Issue a read command with MACACCESS_VALID=1. */
15062306a36Sopenharmony_ci	ocelot_write(ocelot, ANA_TABLES_MACACCESS_VALID |
15162306a36Sopenharmony_ci		     ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_READ),
15262306a36Sopenharmony_ci		     ANA_TABLES_MACACCESS);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (ocelot_mact_wait_for_completion(ocelot)) {
15562306a36Sopenharmony_ci		mutex_unlock(&ocelot->mact_lock);
15662306a36Sopenharmony_ci		return -ETIMEDOUT;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* Read back the entry flags */
16062306a36Sopenharmony_ci	val = ocelot_read(ocelot, ANA_TABLES_MACACCESS);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	mutex_unlock(&ocelot->mact_lock);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (!(val & ANA_TABLES_MACACCESS_VALID))
16562306a36Sopenharmony_ci		return -ENOENT;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	*dst_idx = ANA_TABLES_MACACCESS_DEST_IDX_X(val);
16862306a36Sopenharmony_ci	*type = ANA_TABLES_MACACCESS_ENTRYTYPE_X(val);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_mact_lookup);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciint ocelot_mact_learn_streamdata(struct ocelot *ocelot, int dst_idx,
17562306a36Sopenharmony_ci				 const unsigned char mac[ETH_ALEN],
17662306a36Sopenharmony_ci				 unsigned int vid,
17762306a36Sopenharmony_ci				 enum macaccess_entry_type type,
17862306a36Sopenharmony_ci				 int sfid, int ssid)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	int ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	mutex_lock(&ocelot->mact_lock);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	ocelot_write(ocelot,
18562306a36Sopenharmony_ci		     (sfid < 0 ? 0 : ANA_TABLES_STREAMDATA_SFID_VALID) |
18662306a36Sopenharmony_ci		     ANA_TABLES_STREAMDATA_SFID(sfid) |
18762306a36Sopenharmony_ci		     (ssid < 0 ? 0 : ANA_TABLES_STREAMDATA_SSID_VALID) |
18862306a36Sopenharmony_ci		     ANA_TABLES_STREAMDATA_SSID(ssid),
18962306a36Sopenharmony_ci		     ANA_TABLES_STREAMDATA);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	ret = __ocelot_mact_learn(ocelot, dst_idx, mac, vid, type);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	mutex_unlock(&ocelot->mact_lock);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return ret;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_mact_learn_streamdata);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void ocelot_mact_init(struct ocelot *ocelot)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	/* Configure the learning mode entries attributes:
20262306a36Sopenharmony_ci	 * - Do not copy the frame to the CPU extraction queues.
20362306a36Sopenharmony_ci	 * - Use the vlan and mac_cpoy for dmac lookup.
20462306a36Sopenharmony_ci	 */
20562306a36Sopenharmony_ci	ocelot_rmw(ocelot, 0,
20662306a36Sopenharmony_ci		   ANA_AGENCTRL_LEARN_CPU_COPY | ANA_AGENCTRL_IGNORE_DMAC_FLAGS
20762306a36Sopenharmony_ci		   | ANA_AGENCTRL_LEARN_FWD_KILL
20862306a36Sopenharmony_ci		   | ANA_AGENCTRL_LEARN_IGNORE_VLAN,
20962306a36Sopenharmony_ci		   ANA_AGENCTRL);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* Clear the MAC table. We are not concurrent with anyone, so
21262306a36Sopenharmony_ci	 * holding &ocelot->mact_lock is pointless.
21362306a36Sopenharmony_ci	 */
21462306a36Sopenharmony_ci	ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_civoid ocelot_pll5_init(struct ocelot *ocelot)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	/* Configure PLL5. This will need a proper CCF driver
22062306a36Sopenharmony_ci	 * The values are coming from the VTSS API for Ocelot
22162306a36Sopenharmony_ci	 */
22262306a36Sopenharmony_ci	regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG4,
22362306a36Sopenharmony_ci		     HSIO_PLL5G_CFG4_IB_CTRL(0x7600) |
22462306a36Sopenharmony_ci		     HSIO_PLL5G_CFG4_IB_BIAS_CTRL(0x8));
22562306a36Sopenharmony_ci	regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG0,
22662306a36Sopenharmony_ci		     HSIO_PLL5G_CFG0_CORE_CLK_DIV(0x11) |
22762306a36Sopenharmony_ci		     HSIO_PLL5G_CFG0_CPU_CLK_DIV(2) |
22862306a36Sopenharmony_ci		     HSIO_PLL5G_CFG0_ENA_BIAS |
22962306a36Sopenharmony_ci		     HSIO_PLL5G_CFG0_ENA_VCO_BUF |
23062306a36Sopenharmony_ci		     HSIO_PLL5G_CFG0_ENA_CP1 |
23162306a36Sopenharmony_ci		     HSIO_PLL5G_CFG0_SELCPI(2) |
23262306a36Sopenharmony_ci		     HSIO_PLL5G_CFG0_LOOP_BW_RES(0xe) |
23362306a36Sopenharmony_ci		     HSIO_PLL5G_CFG0_SELBGV820(4) |
23462306a36Sopenharmony_ci		     HSIO_PLL5G_CFG0_DIV4 |
23562306a36Sopenharmony_ci		     HSIO_PLL5G_CFG0_ENA_CLKTREE |
23662306a36Sopenharmony_ci		     HSIO_PLL5G_CFG0_ENA_LANE);
23762306a36Sopenharmony_ci	regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG2,
23862306a36Sopenharmony_ci		     HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET |
23962306a36Sopenharmony_ci		     HSIO_PLL5G_CFG2_EN_RESET_OVERRUN |
24062306a36Sopenharmony_ci		     HSIO_PLL5G_CFG2_GAIN_TEST(0x8) |
24162306a36Sopenharmony_ci		     HSIO_PLL5G_CFG2_ENA_AMPCTRL |
24262306a36Sopenharmony_ci		     HSIO_PLL5G_CFG2_PWD_AMPCTRL_N |
24362306a36Sopenharmony_ci		     HSIO_PLL5G_CFG2_AMPC_SEL(0x10));
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_pll5_init);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic void ocelot_vcap_enable(struct ocelot *ocelot, int port)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	ocelot_write_gix(ocelot, ANA_PORT_VCAP_S2_CFG_S2_ENA |
25062306a36Sopenharmony_ci			 ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG(0xa),
25162306a36Sopenharmony_ci			 ANA_PORT_VCAP_S2_CFG, port);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	ocelot_write_gix(ocelot, ANA_PORT_VCAP_CFG_S1_ENA,
25462306a36Sopenharmony_ci			 ANA_PORT_VCAP_CFG, port);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, REW_PORT_CFG_ES0_EN,
25762306a36Sopenharmony_ci		       REW_PORT_CFG_ES0_EN,
25862306a36Sopenharmony_ci		       REW_PORT_CFG, port);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic int ocelot_single_vlan_aware_bridge(struct ocelot *ocelot,
26262306a36Sopenharmony_ci					   struct netlink_ext_ack *extack)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct net_device *bridge = NULL;
26562306a36Sopenharmony_ci	int port;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
26862306a36Sopenharmony_ci		struct ocelot_port *ocelot_port = ocelot->ports[port];
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		if (!ocelot_port || !ocelot_port->bridge ||
27162306a36Sopenharmony_ci		    !br_vlan_enabled(ocelot_port->bridge))
27262306a36Sopenharmony_ci			continue;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		if (!bridge) {
27562306a36Sopenharmony_ci			bridge = ocelot_port->bridge;
27662306a36Sopenharmony_ci			continue;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		if (bridge == ocelot_port->bridge)
28062306a36Sopenharmony_ci			continue;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
28362306a36Sopenharmony_ci				   "Only one VLAN-aware bridge is supported");
28462306a36Sopenharmony_ci		return -EBUSY;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	return ocelot_read(ocelot, ANA_TABLES_VLANACCESS);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic inline int ocelot_vlant_wait_for_completion(struct ocelot *ocelot)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	u32 val;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return readx_poll_timeout(ocelot_vlant_read_vlanaccess,
30062306a36Sopenharmony_ci		ocelot,
30162306a36Sopenharmony_ci		val,
30262306a36Sopenharmony_ci		(val & ANA_TABLES_VLANACCESS_VLAN_TBL_CMD_M) ==
30362306a36Sopenharmony_ci		ANA_TABLES_VLANACCESS_CMD_IDLE,
30462306a36Sopenharmony_ci		TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	/* Select the VID to configure */
31062306a36Sopenharmony_ci	ocelot_write(ocelot, ANA_TABLES_VLANTIDX_V_INDEX(vid),
31162306a36Sopenharmony_ci		     ANA_TABLES_VLANTIDX);
31262306a36Sopenharmony_ci	/* Set the vlan port members mask and issue a write command */
31362306a36Sopenharmony_ci	ocelot_write(ocelot, ANA_TABLES_VLANACCESS_VLAN_PORT_MASK(mask) |
31462306a36Sopenharmony_ci			     ANA_TABLES_VLANACCESS_CMD_WRITE,
31562306a36Sopenharmony_ci		     ANA_TABLES_VLANACCESS);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	return ocelot_vlant_wait_for_completion(ocelot);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int ocelot_port_num_untagged_vlans(struct ocelot *ocelot, int port)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct ocelot_bridge_vlan *vlan;
32362306a36Sopenharmony_ci	int num_untagged = 0;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	list_for_each_entry(vlan, &ocelot->vlans, list) {
32662306a36Sopenharmony_ci		if (!(vlan->portmask & BIT(port)))
32762306a36Sopenharmony_ci			continue;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		/* Ignore the VLAN added by ocelot_add_vlan_unaware_pvid(),
33062306a36Sopenharmony_ci		 * because this is never active in hardware at the same time as
33162306a36Sopenharmony_ci		 * the bridge VLANs, which only matter in VLAN-aware mode.
33262306a36Sopenharmony_ci		 */
33362306a36Sopenharmony_ci		if (vlan->vid >= OCELOT_RSV_VLAN_RANGE_START)
33462306a36Sopenharmony_ci			continue;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		if (vlan->untagged & BIT(port))
33762306a36Sopenharmony_ci			num_untagged++;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return num_untagged;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int ocelot_port_num_tagged_vlans(struct ocelot *ocelot, int port)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct ocelot_bridge_vlan *vlan;
34662306a36Sopenharmony_ci	int num_tagged = 0;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	list_for_each_entry(vlan, &ocelot->vlans, list) {
34962306a36Sopenharmony_ci		if (!(vlan->portmask & BIT(port)))
35062306a36Sopenharmony_ci			continue;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		if (!(vlan->untagged & BIT(port)))
35362306a36Sopenharmony_ci			num_tagged++;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return num_tagged;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/* We use native VLAN when we have to mix egress-tagged VLANs with exactly
36062306a36Sopenharmony_ci * _one_ egress-untagged VLAN (_the_ native VLAN)
36162306a36Sopenharmony_ci */
36262306a36Sopenharmony_cistatic bool ocelot_port_uses_native_vlan(struct ocelot *ocelot, int port)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	return ocelot_port_num_tagged_vlans(ocelot, port) &&
36562306a36Sopenharmony_ci	       ocelot_port_num_untagged_vlans(ocelot, port) == 1;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic struct ocelot_bridge_vlan *
36962306a36Sopenharmony_ciocelot_port_find_native_vlan(struct ocelot *ocelot, int port)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct ocelot_bridge_vlan *vlan;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	list_for_each_entry(vlan, &ocelot->vlans, list)
37462306a36Sopenharmony_ci		if (vlan->portmask & BIT(port) && vlan->untagged & BIT(port))
37562306a36Sopenharmony_ci			return vlan;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return NULL;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci/* Keep in sync REW_TAG_CFG_TAG_CFG and, if applicable,
38162306a36Sopenharmony_ci * REW_PORT_VLAN_CFG_PORT_VID, with the bridge VLAN table and VLAN awareness
38262306a36Sopenharmony_ci * state of the port.
38362306a36Sopenharmony_ci */
38462306a36Sopenharmony_cistatic void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
38762306a36Sopenharmony_ci	enum ocelot_port_tag_config tag_cfg;
38862306a36Sopenharmony_ci	bool uses_native_vlan = false;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (ocelot_port->vlan_aware) {
39162306a36Sopenharmony_ci		uses_native_vlan = ocelot_port_uses_native_vlan(ocelot, port);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		if (uses_native_vlan)
39462306a36Sopenharmony_ci			tag_cfg = OCELOT_PORT_TAG_NATIVE;
39562306a36Sopenharmony_ci		else if (ocelot_port_num_untagged_vlans(ocelot, port))
39662306a36Sopenharmony_ci			tag_cfg = OCELOT_PORT_TAG_DISABLED;
39762306a36Sopenharmony_ci		else
39862306a36Sopenharmony_ci			tag_cfg = OCELOT_PORT_TAG_TRUNK;
39962306a36Sopenharmony_ci	} else {
40062306a36Sopenharmony_ci		tag_cfg = OCELOT_PORT_TAG_DISABLED;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, REW_TAG_CFG_TAG_CFG(tag_cfg),
40462306a36Sopenharmony_ci		       REW_TAG_CFG_TAG_CFG_M,
40562306a36Sopenharmony_ci		       REW_TAG_CFG, port);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (uses_native_vlan) {
40862306a36Sopenharmony_ci		struct ocelot_bridge_vlan *native_vlan;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		/* Not having a native VLAN is impossible, because
41162306a36Sopenharmony_ci		 * ocelot_port_num_untagged_vlans has returned 1.
41262306a36Sopenharmony_ci		 * So there is no use in checking for NULL here.
41362306a36Sopenharmony_ci		 */
41462306a36Sopenharmony_ci		native_vlan = ocelot_port_find_native_vlan(ocelot, port);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		ocelot_rmw_gix(ocelot,
41762306a36Sopenharmony_ci			       REW_PORT_VLAN_CFG_PORT_VID(native_vlan->vid),
41862306a36Sopenharmony_ci			       REW_PORT_VLAN_CFG_PORT_VID_M,
41962306a36Sopenharmony_ci			       REW_PORT_VLAN_CFG, port);
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ciint ocelot_bridge_num_find(struct ocelot *ocelot,
42462306a36Sopenharmony_ci			   const struct net_device *bridge)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	int port;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
42962306a36Sopenharmony_ci		struct ocelot_port *ocelot_port = ocelot->ports[port];
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		if (ocelot_port && ocelot_port->bridge == bridge)
43262306a36Sopenharmony_ci			return ocelot_port->bridge_num;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	return -1;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_bridge_num_find);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic u16 ocelot_vlan_unaware_pvid(struct ocelot *ocelot,
44062306a36Sopenharmony_ci				    const struct net_device *bridge)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	int bridge_num;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* Standalone ports use VID 0 */
44562306a36Sopenharmony_ci	if (!bridge)
44662306a36Sopenharmony_ci		return 0;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	bridge_num = ocelot_bridge_num_find(ocelot, bridge);
44962306a36Sopenharmony_ci	if (WARN_ON(bridge_num < 0))
45062306a36Sopenharmony_ci		return 0;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/* VLAN-unaware bridges use a reserved VID going from 4095 downwards */
45362306a36Sopenharmony_ci	return VLAN_N_VID - bridge_num - 1;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci/* Default vlan to clasify for untagged frames (may be zero) */
45762306a36Sopenharmony_cistatic void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
45862306a36Sopenharmony_ci				 const struct ocelot_bridge_vlan *pvid_vlan)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
46162306a36Sopenharmony_ci	u16 pvid = ocelot_vlan_unaware_pvid(ocelot, ocelot_port->bridge);
46262306a36Sopenharmony_ci	u32 val = 0;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	ocelot_port->pvid_vlan = pvid_vlan;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (ocelot_port->vlan_aware && pvid_vlan)
46762306a36Sopenharmony_ci		pvid = pvid_vlan->vid;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot,
47062306a36Sopenharmony_ci		       ANA_PORT_VLAN_CFG_VLAN_VID(pvid),
47162306a36Sopenharmony_ci		       ANA_PORT_VLAN_CFG_VLAN_VID_M,
47262306a36Sopenharmony_ci		       ANA_PORT_VLAN_CFG, port);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* If there's no pvid, we should drop not only untagged traffic (which
47562306a36Sopenharmony_ci	 * happens automatically), but also 802.1p traffic which gets
47662306a36Sopenharmony_ci	 * classified to VLAN 0, but that is always in our RX filter, so it
47762306a36Sopenharmony_ci	 * would get accepted were it not for this setting.
47862306a36Sopenharmony_ci	 */
47962306a36Sopenharmony_ci	if (!pvid_vlan && ocelot_port->vlan_aware)
48062306a36Sopenharmony_ci		val = ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
48162306a36Sopenharmony_ci		      ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, val,
48462306a36Sopenharmony_ci		       ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
48562306a36Sopenharmony_ci		       ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA,
48662306a36Sopenharmony_ci		       ANA_PORT_DROP_CFG, port);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic struct ocelot_bridge_vlan *ocelot_bridge_vlan_find(struct ocelot *ocelot,
49062306a36Sopenharmony_ci							  u16 vid)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct ocelot_bridge_vlan *vlan;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	list_for_each_entry(vlan, &ocelot->vlans, list)
49562306a36Sopenharmony_ci		if (vlan->vid == vid)
49662306a36Sopenharmony_ci			return vlan;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	return NULL;
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic int ocelot_vlan_member_add(struct ocelot *ocelot, int port, u16 vid,
50262306a36Sopenharmony_ci				  bool untagged)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct ocelot_bridge_vlan *vlan = ocelot_bridge_vlan_find(ocelot, vid);
50562306a36Sopenharmony_ci	unsigned long portmask;
50662306a36Sopenharmony_ci	int err;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	if (vlan) {
50962306a36Sopenharmony_ci		portmask = vlan->portmask | BIT(port);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		err = ocelot_vlant_set_mask(ocelot, vid, portmask);
51262306a36Sopenharmony_ci		if (err)
51362306a36Sopenharmony_ci			return err;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		vlan->portmask = portmask;
51662306a36Sopenharmony_ci		/* Bridge VLANs can be overwritten with a different
51762306a36Sopenharmony_ci		 * egress-tagging setting, so make sure to override an untagged
51862306a36Sopenharmony_ci		 * with a tagged VID if that's going on.
51962306a36Sopenharmony_ci		 */
52062306a36Sopenharmony_ci		if (untagged)
52162306a36Sopenharmony_ci			vlan->untagged |= BIT(port);
52262306a36Sopenharmony_ci		else
52362306a36Sopenharmony_ci			vlan->untagged &= ~BIT(port);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		return 0;
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
52962306a36Sopenharmony_ci	if (!vlan)
53062306a36Sopenharmony_ci		return -ENOMEM;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	portmask = BIT(port);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	err = ocelot_vlant_set_mask(ocelot, vid, portmask);
53562306a36Sopenharmony_ci	if (err) {
53662306a36Sopenharmony_ci		kfree(vlan);
53762306a36Sopenharmony_ci		return err;
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	vlan->vid = vid;
54162306a36Sopenharmony_ci	vlan->portmask = portmask;
54262306a36Sopenharmony_ci	if (untagged)
54362306a36Sopenharmony_ci		vlan->untagged = BIT(port);
54462306a36Sopenharmony_ci	INIT_LIST_HEAD(&vlan->list);
54562306a36Sopenharmony_ci	list_add_tail(&vlan->list, &ocelot->vlans);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return 0;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic int ocelot_vlan_member_del(struct ocelot *ocelot, int port, u16 vid)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	struct ocelot_bridge_vlan *vlan = ocelot_bridge_vlan_find(ocelot, vid);
55362306a36Sopenharmony_ci	unsigned long portmask;
55462306a36Sopenharmony_ci	int err;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (!vlan)
55762306a36Sopenharmony_ci		return 0;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	portmask = vlan->portmask & ~BIT(port);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	err = ocelot_vlant_set_mask(ocelot, vid, portmask);
56262306a36Sopenharmony_ci	if (err)
56362306a36Sopenharmony_ci		return err;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	vlan->portmask = portmask;
56662306a36Sopenharmony_ci	if (vlan->portmask)
56762306a36Sopenharmony_ci		return 0;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	list_del(&vlan->list);
57062306a36Sopenharmony_ci	kfree(vlan);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return 0;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic int ocelot_add_vlan_unaware_pvid(struct ocelot *ocelot, int port,
57662306a36Sopenharmony_ci					const struct net_device *bridge)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	u16 vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	return ocelot_vlan_member_add(ocelot, port, vid, true);
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic int ocelot_del_vlan_unaware_pvid(struct ocelot *ocelot, int port,
58462306a36Sopenharmony_ci					const struct net_device *bridge)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	u16 vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	return ocelot_vlan_member_del(ocelot, port, vid);
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ciint ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
59262306a36Sopenharmony_ci			       bool vlan_aware, struct netlink_ext_ack *extack)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1];
59562306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
59662306a36Sopenharmony_ci	struct ocelot_vcap_filter *filter;
59762306a36Sopenharmony_ci	int err = 0;
59862306a36Sopenharmony_ci	u32 val;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	list_for_each_entry(filter, &block->rules, list) {
60162306a36Sopenharmony_ci		if (filter->ingress_port_mask & BIT(port) &&
60262306a36Sopenharmony_ci		    filter->action.vid_replace_ena) {
60362306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
60462306a36Sopenharmony_ci					   "Cannot change VLAN state with vlan modify rules active");
60562306a36Sopenharmony_ci			return -EBUSY;
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	err = ocelot_single_vlan_aware_bridge(ocelot, extack);
61062306a36Sopenharmony_ci	if (err)
61162306a36Sopenharmony_ci		return err;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	if (vlan_aware)
61462306a36Sopenharmony_ci		err = ocelot_del_vlan_unaware_pvid(ocelot, port,
61562306a36Sopenharmony_ci						   ocelot_port->bridge);
61662306a36Sopenharmony_ci	else if (ocelot_port->bridge)
61762306a36Sopenharmony_ci		err = ocelot_add_vlan_unaware_pvid(ocelot, port,
61862306a36Sopenharmony_ci						   ocelot_port->bridge);
61962306a36Sopenharmony_ci	if (err)
62062306a36Sopenharmony_ci		return err;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	ocelot_port->vlan_aware = vlan_aware;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (vlan_aware)
62562306a36Sopenharmony_ci		val = ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
62662306a36Sopenharmony_ci		      ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1);
62762306a36Sopenharmony_ci	else
62862306a36Sopenharmony_ci		val = 0;
62962306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, val,
63062306a36Sopenharmony_ci		       ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
63162306a36Sopenharmony_ci		       ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M,
63262306a36Sopenharmony_ci		       ANA_PORT_VLAN_CFG, port);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	ocelot_port_set_pvid(ocelot, port, ocelot_port->pvid_vlan);
63562306a36Sopenharmony_ci	ocelot_port_manage_port_tag(ocelot, port);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	return 0;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_vlan_filtering);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ciint ocelot_vlan_prepare(struct ocelot *ocelot, int port, u16 vid, bool pvid,
64262306a36Sopenharmony_ci			bool untagged, struct netlink_ext_ack *extack)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	if (untagged) {
64562306a36Sopenharmony_ci		/* We are adding an egress-tagged VLAN */
64662306a36Sopenharmony_ci		if (ocelot_port_uses_native_vlan(ocelot, port)) {
64762306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
64862306a36Sopenharmony_ci					   "Port with egress-tagged VLANs cannot have more than one egress-untagged (native) VLAN");
64962306a36Sopenharmony_ci			return -EBUSY;
65062306a36Sopenharmony_ci		}
65162306a36Sopenharmony_ci	} else {
65262306a36Sopenharmony_ci		/* We are adding an egress-tagged VLAN */
65362306a36Sopenharmony_ci		if (ocelot_port_num_untagged_vlans(ocelot, port) > 1) {
65462306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
65562306a36Sopenharmony_ci					   "Port with more than one egress-untagged VLAN cannot have egress-tagged VLANs");
65662306a36Sopenharmony_ci			return -EBUSY;
65762306a36Sopenharmony_ci		}
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (vid > OCELOT_RSV_VLAN_RANGE_START) {
66162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
66262306a36Sopenharmony_ci				   "VLAN range 4000-4095 reserved for VLAN-unaware bridging");
66362306a36Sopenharmony_ci		return -EBUSY;
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	return 0;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_vlan_prepare);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ciint ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
67162306a36Sopenharmony_ci		    bool untagged)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	int err;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	/* Ignore VID 0 added to our RX filter by the 8021q module, since
67662306a36Sopenharmony_ci	 * that collides with OCELOT_STANDALONE_PVID and changes it from
67762306a36Sopenharmony_ci	 * egress-untagged to egress-tagged.
67862306a36Sopenharmony_ci	 */
67962306a36Sopenharmony_ci	if (!vid)
68062306a36Sopenharmony_ci		return 0;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	err = ocelot_vlan_member_add(ocelot, port, vid, untagged);
68362306a36Sopenharmony_ci	if (err)
68462306a36Sopenharmony_ci		return err;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* Default ingress vlan classification */
68762306a36Sopenharmony_ci	if (pvid)
68862306a36Sopenharmony_ci		ocelot_port_set_pvid(ocelot, port,
68962306a36Sopenharmony_ci				     ocelot_bridge_vlan_find(ocelot, vid));
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	/* Untagged egress vlan clasification */
69262306a36Sopenharmony_ci	ocelot_port_manage_port_tag(ocelot, port);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	return 0;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_vlan_add);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ciint ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
70162306a36Sopenharmony_ci	bool del_pvid = false;
70262306a36Sopenharmony_ci	int err;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (!vid)
70562306a36Sopenharmony_ci		return 0;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	if (ocelot_port->pvid_vlan && ocelot_port->pvid_vlan->vid == vid)
70862306a36Sopenharmony_ci		del_pvid = true;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	err = ocelot_vlan_member_del(ocelot, port, vid);
71162306a36Sopenharmony_ci	if (err)
71262306a36Sopenharmony_ci		return err;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	/* Ingress */
71562306a36Sopenharmony_ci	if (del_pvid)
71662306a36Sopenharmony_ci		ocelot_port_set_pvid(ocelot, port, NULL);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* Egress */
71962306a36Sopenharmony_ci	ocelot_port_manage_port_tag(ocelot, port);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	return 0;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_vlan_del);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_cistatic void ocelot_vlan_init(struct ocelot *ocelot)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	unsigned long all_ports = GENMASK(ocelot->num_phys_ports - 1, 0);
72862306a36Sopenharmony_ci	u16 port, vid;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* Clear VLAN table, by default all ports are members of all VLANs */
73162306a36Sopenharmony_ci	ocelot_write(ocelot, ANA_TABLES_VLANACCESS_CMD_INIT,
73262306a36Sopenharmony_ci		     ANA_TABLES_VLANACCESS);
73362306a36Sopenharmony_ci	ocelot_vlant_wait_for_completion(ocelot);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	/* Configure the port VLAN memberships */
73662306a36Sopenharmony_ci	for (vid = 1; vid < VLAN_N_VID; vid++)
73762306a36Sopenharmony_ci		ocelot_vlant_set_mask(ocelot, vid, 0);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/* We need VID 0 to get traffic on standalone ports.
74062306a36Sopenharmony_ci	 * It is added automatically if the 8021q module is loaded, but we
74162306a36Sopenharmony_ci	 * can't rely on that since it might not be.
74262306a36Sopenharmony_ci	 */
74362306a36Sopenharmony_ci	ocelot_vlant_set_mask(ocelot, OCELOT_STANDALONE_PVID, all_ports);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	/* Set vlan ingress filter mask to all ports but the CPU port by
74662306a36Sopenharmony_ci	 * default.
74762306a36Sopenharmony_ci	 */
74862306a36Sopenharmony_ci	ocelot_write(ocelot, all_ports, ANA_VLANMASK);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
75162306a36Sopenharmony_ci		ocelot_write_gix(ocelot, 0, REW_PORT_VLAN_CFG, port);
75262306a36Sopenharmony_ci		ocelot_write_gix(ocelot, 0, REW_TAG_CFG, port);
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic u32 ocelot_read_eq_avail(struct ocelot *ocelot, int port)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	return ocelot_read_rix(ocelot, QSYS_SW_STATUS, port);
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic int ocelot_port_flush(struct ocelot *ocelot, int port)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	unsigned int pause_ena;
76462306a36Sopenharmony_ci	int err, val;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/* Disable dequeuing from the egress queues */
76762306a36Sopenharmony_ci	ocelot_rmw_rix(ocelot, QSYS_PORT_MODE_DEQUEUE_DIS,
76862306a36Sopenharmony_ci		       QSYS_PORT_MODE_DEQUEUE_DIS,
76962306a36Sopenharmony_ci		       QSYS_PORT_MODE, port);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	/* Disable flow control */
77262306a36Sopenharmony_ci	ocelot_fields_read(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, &pause_ena);
77362306a36Sopenharmony_ci	ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 0);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/* Disable priority flow control */
77662306a36Sopenharmony_ci	ocelot_fields_write(ocelot, port,
77762306a36Sopenharmony_ci			    QSYS_SWITCH_PORT_MODE_TX_PFC_ENA, 0);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	/* Wait at least the time it takes to receive a frame of maximum length
78062306a36Sopenharmony_ci	 * at the port.
78162306a36Sopenharmony_ci	 * Worst-case delays for 10 kilobyte jumbo frames are:
78262306a36Sopenharmony_ci	 * 8 ms on a 10M port
78362306a36Sopenharmony_ci	 * 800 μs on a 100M port
78462306a36Sopenharmony_ci	 * 80 μs on a 1G port
78562306a36Sopenharmony_ci	 * 32 μs on a 2.5G port
78662306a36Sopenharmony_ci	 */
78762306a36Sopenharmony_ci	usleep_range(8000, 10000);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	/* Disable half duplex backpressure. */
79062306a36Sopenharmony_ci	ocelot_rmw_rix(ocelot, 0, SYS_FRONT_PORT_MODE_HDX_MODE,
79162306a36Sopenharmony_ci		       SYS_FRONT_PORT_MODE, port);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	/* Flush the queues associated with the port. */
79462306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, REW_PORT_CFG_FLUSH_ENA, REW_PORT_CFG_FLUSH_ENA,
79562306a36Sopenharmony_ci		       REW_PORT_CFG, port);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	/* Enable dequeuing from the egress queues. */
79862306a36Sopenharmony_ci	ocelot_rmw_rix(ocelot, 0, QSYS_PORT_MODE_DEQUEUE_DIS, QSYS_PORT_MODE,
79962306a36Sopenharmony_ci		       port);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	/* Wait until flushing is complete. */
80262306a36Sopenharmony_ci	err = read_poll_timeout(ocelot_read_eq_avail, val, !val,
80362306a36Sopenharmony_ci				100, 2000000, false, ocelot, port);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	/* Clear flushing again. */
80662306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, 0, REW_PORT_CFG_FLUSH_ENA, REW_PORT_CFG, port);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	/* Re-enable flow control */
80962306a36Sopenharmony_ci	ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, pause_ena);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	return err;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ciint ocelot_port_configure_serdes(struct ocelot *ocelot, int port,
81562306a36Sopenharmony_ci				 struct device_node *portnp)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
81862306a36Sopenharmony_ci	struct device *dev = ocelot->dev;
81962306a36Sopenharmony_ci	int err;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	/* Ensure clock signals and speed are set on all QSGMII links */
82262306a36Sopenharmony_ci	if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_QSGMII)
82362306a36Sopenharmony_ci		ocelot_port_rmwl(ocelot_port, 0,
82462306a36Sopenharmony_ci				 DEV_CLOCK_CFG_MAC_TX_RST |
82562306a36Sopenharmony_ci				 DEV_CLOCK_CFG_MAC_RX_RST,
82662306a36Sopenharmony_ci				 DEV_CLOCK_CFG);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	if (ocelot_port->phy_mode != PHY_INTERFACE_MODE_INTERNAL) {
82962306a36Sopenharmony_ci		struct phy *serdes = of_phy_get(portnp, NULL);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		if (IS_ERR(serdes)) {
83262306a36Sopenharmony_ci			err = PTR_ERR(serdes);
83362306a36Sopenharmony_ci			dev_err_probe(dev, err,
83462306a36Sopenharmony_ci				      "missing SerDes phys for port %d\n",
83562306a36Sopenharmony_ci				      port);
83662306a36Sopenharmony_ci			return err;
83762306a36Sopenharmony_ci		}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci		err = phy_set_mode_ext(serdes, PHY_MODE_ETHERNET,
84062306a36Sopenharmony_ci				       ocelot_port->phy_mode);
84162306a36Sopenharmony_ci		of_phy_put(serdes);
84262306a36Sopenharmony_ci		if (err) {
84362306a36Sopenharmony_ci			dev_err(dev, "Could not SerDes mode on port %d: %pe\n",
84462306a36Sopenharmony_ci				port, ERR_PTR(err));
84562306a36Sopenharmony_ci			return err;
84662306a36Sopenharmony_ci		}
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	return 0;
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_configure_serdes);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_civoid ocelot_phylink_mac_config(struct ocelot *ocelot, int port,
85462306a36Sopenharmony_ci			       unsigned int link_an_mode,
85562306a36Sopenharmony_ci			       const struct phylink_link_state *state)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	/* Disable HDX fast control */
86062306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, DEV_PORT_MISC_HDX_FAST_DIS,
86162306a36Sopenharmony_ci			   DEV_PORT_MISC);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	/* SGMII only for now */
86462306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, PCS1G_MODE_CFG_SGMII_MODE_ENA,
86562306a36Sopenharmony_ci			   PCS1G_MODE_CFG);
86662306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	/* Enable PCS */
86962306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, PCS1G_CFG_PCS_ENA, PCS1G_CFG);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	/* No aneg on SGMII */
87262306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, 0, PCS1G_ANEG_CFG);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	/* No loopback */
87562306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG);
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_phylink_mac_config);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_civoid ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port,
88062306a36Sopenharmony_ci				  unsigned int link_an_mode,
88162306a36Sopenharmony_ci				  phy_interface_t interface,
88262306a36Sopenharmony_ci				  unsigned long quirks)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
88562306a36Sopenharmony_ci	int err;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	ocelot_port->speed = SPEED_UNKNOWN;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	ocelot_port_rmwl(ocelot_port, 0, DEV_MAC_ENA_CFG_RX_ENA,
89062306a36Sopenharmony_ci			 DEV_MAC_ENA_CFG);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (ocelot->ops->cut_through_fwd) {
89362306a36Sopenharmony_ci		mutex_lock(&ocelot->fwd_domain_lock);
89462306a36Sopenharmony_ci		ocelot->ops->cut_through_fwd(ocelot);
89562306a36Sopenharmony_ci		mutex_unlock(&ocelot->fwd_domain_lock);
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	ocelot_fields_write(ocelot, port, QSYS_SWITCH_PORT_MODE_PORT_ENA, 0);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	err = ocelot_port_flush(ocelot, port);
90162306a36Sopenharmony_ci	if (err)
90262306a36Sopenharmony_ci		dev_err(ocelot->dev, "failed to flush port %d: %d\n",
90362306a36Sopenharmony_ci			port, err);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	/* Put the port in reset. */
90662306a36Sopenharmony_ci	if (interface != PHY_INTERFACE_MODE_QSGMII ||
90762306a36Sopenharmony_ci	    !(quirks & OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP))
90862306a36Sopenharmony_ci		ocelot_port_rmwl(ocelot_port,
90962306a36Sopenharmony_ci				 DEV_CLOCK_CFG_MAC_TX_RST |
91062306a36Sopenharmony_ci				 DEV_CLOCK_CFG_MAC_RX_RST,
91162306a36Sopenharmony_ci				 DEV_CLOCK_CFG_MAC_TX_RST |
91262306a36Sopenharmony_ci				 DEV_CLOCK_CFG_MAC_RX_RST,
91362306a36Sopenharmony_ci				 DEV_CLOCK_CFG);
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_phylink_mac_link_down);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_civoid ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port,
91862306a36Sopenharmony_ci				struct phy_device *phydev,
91962306a36Sopenharmony_ci				unsigned int link_an_mode,
92062306a36Sopenharmony_ci				phy_interface_t interface,
92162306a36Sopenharmony_ci				int speed, int duplex,
92262306a36Sopenharmony_ci				bool tx_pause, bool rx_pause,
92362306a36Sopenharmony_ci				unsigned long quirks)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
92662306a36Sopenharmony_ci	int mac_speed, mode = 0;
92762306a36Sopenharmony_ci	u32 mac_fc_cfg;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	ocelot_port->speed = speed;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	/* The MAC might be integrated in systems where the MAC speed is fixed
93262306a36Sopenharmony_ci	 * and it's the PCS who is performing the rate adaptation, so we have
93362306a36Sopenharmony_ci	 * to write "1000Mbps" into the LINK_SPEED field of DEV_CLOCK_CFG
93462306a36Sopenharmony_ci	 * (which is also its default value).
93562306a36Sopenharmony_ci	 */
93662306a36Sopenharmony_ci	if ((quirks & OCELOT_QUIRK_PCS_PERFORMS_RATE_ADAPTATION) ||
93762306a36Sopenharmony_ci	    speed == SPEED_1000) {
93862306a36Sopenharmony_ci		mac_speed = OCELOT_SPEED_1000;
93962306a36Sopenharmony_ci		mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
94062306a36Sopenharmony_ci	} else if (speed == SPEED_2500) {
94162306a36Sopenharmony_ci		mac_speed = OCELOT_SPEED_2500;
94262306a36Sopenharmony_ci		mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
94362306a36Sopenharmony_ci	} else if (speed == SPEED_100) {
94462306a36Sopenharmony_ci		mac_speed = OCELOT_SPEED_100;
94562306a36Sopenharmony_ci	} else {
94662306a36Sopenharmony_ci		mac_speed = OCELOT_SPEED_10;
94762306a36Sopenharmony_ci	}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	if (duplex == DUPLEX_FULL)
95062306a36Sopenharmony_ci		mode |= DEV_MAC_MODE_CFG_FDX_ENA;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, mode, DEV_MAC_MODE_CFG);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	/* Take port out of reset by clearing the MAC_TX_RST, MAC_RX_RST and
95562306a36Sopenharmony_ci	 * PORT_RST bits in DEV_CLOCK_CFG.
95662306a36Sopenharmony_ci	 */
95762306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(mac_speed),
95862306a36Sopenharmony_ci			   DEV_CLOCK_CFG);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	switch (speed) {
96162306a36Sopenharmony_ci	case SPEED_10:
96262306a36Sopenharmony_ci		mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(OCELOT_SPEED_10);
96362306a36Sopenharmony_ci		break;
96462306a36Sopenharmony_ci	case SPEED_100:
96562306a36Sopenharmony_ci		mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(OCELOT_SPEED_100);
96662306a36Sopenharmony_ci		break;
96762306a36Sopenharmony_ci	case SPEED_1000:
96862306a36Sopenharmony_ci	case SPEED_2500:
96962306a36Sopenharmony_ci		mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(OCELOT_SPEED_1000);
97062306a36Sopenharmony_ci		break;
97162306a36Sopenharmony_ci	default:
97262306a36Sopenharmony_ci		dev_err(ocelot->dev, "Unsupported speed on port %d: %d\n",
97362306a36Sopenharmony_ci			port, speed);
97462306a36Sopenharmony_ci		return;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (rx_pause)
97862306a36Sopenharmony_ci		mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	if (tx_pause)
98162306a36Sopenharmony_ci		mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA |
98262306a36Sopenharmony_ci			      SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
98362306a36Sopenharmony_ci			      SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
98462306a36Sopenharmony_ci			      SYS_MAC_FC_CFG_ZERO_PAUSE_ENA;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	/* Flow control. Link speed is only used here to evaluate the time
98762306a36Sopenharmony_ci	 * specification in incoming pause frames.
98862306a36Sopenharmony_ci	 */
98962306a36Sopenharmony_ci	ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	/* Don't attempt to send PAUSE frames on the NPI port, it's broken */
99462306a36Sopenharmony_ci	if (port != ocelot->npi)
99562306a36Sopenharmony_ci		ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA,
99662306a36Sopenharmony_ci				    tx_pause);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	/* Undo the effects of ocelot_phylink_mac_link_down:
99962306a36Sopenharmony_ci	 * enable MAC module
100062306a36Sopenharmony_ci	 */
100162306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA |
100262306a36Sopenharmony_ci			   DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/* If the port supports cut-through forwarding, update the masks before
100562306a36Sopenharmony_ci	 * enabling forwarding on the port.
100662306a36Sopenharmony_ci	 */
100762306a36Sopenharmony_ci	if (ocelot->ops->cut_through_fwd) {
100862306a36Sopenharmony_ci		mutex_lock(&ocelot->fwd_domain_lock);
100962306a36Sopenharmony_ci		/* Workaround for hardware bug - FP doesn't work
101062306a36Sopenharmony_ci		 * at all link speeds for all PHY modes. The function
101162306a36Sopenharmony_ci		 * below also calls ocelot->ops->cut_through_fwd(),
101262306a36Sopenharmony_ci		 * so we don't need to do it twice.
101362306a36Sopenharmony_ci		 */
101462306a36Sopenharmony_ci		ocelot_port_update_active_preemptible_tcs(ocelot, port);
101562306a36Sopenharmony_ci		mutex_unlock(&ocelot->fwd_domain_lock);
101662306a36Sopenharmony_ci	}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	/* Core: Enable port for frame transfer */
101962306a36Sopenharmony_ci	ocelot_fields_write(ocelot, port,
102062306a36Sopenharmony_ci			    QSYS_SWITCH_PORT_MODE_PORT_ENA, 1);
102162306a36Sopenharmony_ci}
102262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_phylink_mac_link_up);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_cistatic int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh,
102562306a36Sopenharmony_ci				u32 *rval)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	u32 bytes_valid, val;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
103062306a36Sopenharmony_ci	if (val == XTR_NOT_READY) {
103162306a36Sopenharmony_ci		if (ifh)
103262306a36Sopenharmony_ci			return -EIO;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci		do {
103562306a36Sopenharmony_ci			val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
103662306a36Sopenharmony_ci		} while (val == XTR_NOT_READY);
103762306a36Sopenharmony_ci	}
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	switch (val) {
104062306a36Sopenharmony_ci	case XTR_ABORT:
104162306a36Sopenharmony_ci		return -EIO;
104262306a36Sopenharmony_ci	case XTR_EOF_0:
104362306a36Sopenharmony_ci	case XTR_EOF_1:
104462306a36Sopenharmony_ci	case XTR_EOF_2:
104562306a36Sopenharmony_ci	case XTR_EOF_3:
104662306a36Sopenharmony_ci	case XTR_PRUNED:
104762306a36Sopenharmony_ci		bytes_valid = XTR_VALID_BYTES(val);
104862306a36Sopenharmony_ci		val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
104962306a36Sopenharmony_ci		if (val == XTR_ESCAPE)
105062306a36Sopenharmony_ci			*rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
105162306a36Sopenharmony_ci		else
105262306a36Sopenharmony_ci			*rval = val;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci		return bytes_valid;
105562306a36Sopenharmony_ci	case XTR_ESCAPE:
105662306a36Sopenharmony_ci		*rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci		return 4;
105962306a36Sopenharmony_ci	default:
106062306a36Sopenharmony_ci		*rval = val;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci		return 4;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp, u32 *xfh)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	int i, err = 0;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	for (i = 0; i < OCELOT_TAG_LEN / 4; i++) {
107162306a36Sopenharmony_ci		err = ocelot_rx_frame_word(ocelot, grp, true, &xfh[i]);
107262306a36Sopenharmony_ci		if (err != 4)
107362306a36Sopenharmony_ci			return (err < 0) ? err : -EIO;
107462306a36Sopenharmony_ci	}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	return 0;
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_civoid ocelot_ptp_rx_timestamp(struct ocelot *ocelot, struct sk_buff *skb,
108062306a36Sopenharmony_ci			     u64 timestamp)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	struct skb_shared_hwtstamps *shhwtstamps;
108362306a36Sopenharmony_ci	u64 tod_in_ns, full_ts_in_ns;
108462306a36Sopenharmony_ci	struct timespec64 ts;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec);
108962306a36Sopenharmony_ci	if ((tod_in_ns & 0xffffffff) < timestamp)
109062306a36Sopenharmony_ci		full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) |
109162306a36Sopenharmony_ci				timestamp;
109262306a36Sopenharmony_ci	else
109362306a36Sopenharmony_ci		full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) |
109462306a36Sopenharmony_ci				timestamp;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	shhwtstamps = skb_hwtstamps(skb);
109762306a36Sopenharmony_ci	memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
109862306a36Sopenharmony_ci	shhwtstamps->hwtstamp = full_ts_in_ns;
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_ptp_rx_timestamp);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ciint ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	u64 timestamp, src_port, len;
110562306a36Sopenharmony_ci	u32 xfh[OCELOT_TAG_LEN / 4];
110662306a36Sopenharmony_ci	struct net_device *dev;
110762306a36Sopenharmony_ci	struct sk_buff *skb;
110862306a36Sopenharmony_ci	int sz, buf_len;
110962306a36Sopenharmony_ci	u32 val, *buf;
111062306a36Sopenharmony_ci	int err;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	err = ocelot_xtr_poll_xfh(ocelot, grp, xfh);
111362306a36Sopenharmony_ci	if (err)
111462306a36Sopenharmony_ci		return err;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	ocelot_xfh_get_src_port(xfh, &src_port);
111762306a36Sopenharmony_ci	ocelot_xfh_get_len(xfh, &len);
111862306a36Sopenharmony_ci	ocelot_xfh_get_rew_val(xfh, &timestamp);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	if (WARN_ON(src_port >= ocelot->num_phys_ports))
112162306a36Sopenharmony_ci		return -EINVAL;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	dev = ocelot->ops->port_to_netdev(ocelot, src_port);
112462306a36Sopenharmony_ci	if (!dev)
112562306a36Sopenharmony_ci		return -EINVAL;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	skb = netdev_alloc_skb(dev, len);
112862306a36Sopenharmony_ci	if (unlikely(!skb)) {
112962306a36Sopenharmony_ci		netdev_err(dev, "Unable to allocate sk_buff\n");
113062306a36Sopenharmony_ci		return -ENOMEM;
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	buf_len = len - ETH_FCS_LEN;
113462306a36Sopenharmony_ci	buf = (u32 *)skb_put(skb, buf_len);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	len = 0;
113762306a36Sopenharmony_ci	do {
113862306a36Sopenharmony_ci		sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
113962306a36Sopenharmony_ci		if (sz < 0) {
114062306a36Sopenharmony_ci			err = sz;
114162306a36Sopenharmony_ci			goto out_free_skb;
114262306a36Sopenharmony_ci		}
114362306a36Sopenharmony_ci		*buf++ = val;
114462306a36Sopenharmony_ci		len += sz;
114562306a36Sopenharmony_ci	} while (len < buf_len);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	/* Read the FCS */
114862306a36Sopenharmony_ci	sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
114962306a36Sopenharmony_ci	if (sz < 0) {
115062306a36Sopenharmony_ci		err = sz;
115162306a36Sopenharmony_ci		goto out_free_skb;
115262306a36Sopenharmony_ci	}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	/* Update the statistics if part of the FCS was read before */
115562306a36Sopenharmony_ci	len -= ETH_FCS_LEN - sz;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (unlikely(dev->features & NETIF_F_RXFCS)) {
115862306a36Sopenharmony_ci		buf = (u32 *)skb_put(skb, ETH_FCS_LEN);
115962306a36Sopenharmony_ci		*buf = val;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	if (ocelot->ptp)
116362306a36Sopenharmony_ci		ocelot_ptp_rx_timestamp(ocelot, skb, timestamp);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	/* Everything we see on an interface that is in the HW bridge
116662306a36Sopenharmony_ci	 * has already been forwarded.
116762306a36Sopenharmony_ci	 */
116862306a36Sopenharmony_ci	if (ocelot->ports[src_port]->bridge)
116962306a36Sopenharmony_ci		skb->offload_fwd_mark = 1;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	*nskb = skb;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	return 0;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ciout_free_skb:
117862306a36Sopenharmony_ci	kfree_skb(skb);
117962306a36Sopenharmony_ci	return err;
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_xtr_poll_frame);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cibool ocelot_can_inject(struct ocelot *ocelot, int grp)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	u32 val = ocelot_read(ocelot, QS_INJ_STATUS);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))))
118862306a36Sopenharmony_ci		return false;
118962306a36Sopenharmony_ci	if (val & QS_INJ_STATUS_WMARK_REACHED(BIT(grp)))
119062306a36Sopenharmony_ci		return false;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	return true;
119362306a36Sopenharmony_ci}
119462306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_can_inject);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_civoid ocelot_ifh_port_set(void *ifh, int port, u32 rew_op, u32 vlan_tag)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	ocelot_ifh_set_bypass(ifh, 1);
119962306a36Sopenharmony_ci	ocelot_ifh_set_dest(ifh, BIT_ULL(port));
120062306a36Sopenharmony_ci	ocelot_ifh_set_tag_type(ifh, IFH_TAG_TYPE_C);
120162306a36Sopenharmony_ci	if (vlan_tag)
120262306a36Sopenharmony_ci		ocelot_ifh_set_vlan_tci(ifh, vlan_tag);
120362306a36Sopenharmony_ci	if (rew_op)
120462306a36Sopenharmony_ci		ocelot_ifh_set_rew_op(ifh, rew_op);
120562306a36Sopenharmony_ci}
120662306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_ifh_port_set);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_civoid ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
120962306a36Sopenharmony_ci			      u32 rew_op, struct sk_buff *skb)
121062306a36Sopenharmony_ci{
121162306a36Sopenharmony_ci	u32 ifh[OCELOT_TAG_LEN / 4] = {0};
121262306a36Sopenharmony_ci	unsigned int i, count, last;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
121562306a36Sopenharmony_ci			 QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	ocelot_ifh_port_set(ifh, port, rew_op, skb_vlan_tag_get(skb));
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	for (i = 0; i < OCELOT_TAG_LEN / 4; i++)
122062306a36Sopenharmony_ci		ocelot_write_rix(ocelot, ifh[i], QS_INJ_WR, grp);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	count = DIV_ROUND_UP(skb->len, 4);
122362306a36Sopenharmony_ci	last = skb->len % 4;
122462306a36Sopenharmony_ci	for (i = 0; i < count; i++)
122562306a36Sopenharmony_ci		ocelot_write_rix(ocelot, ((u32 *)skb->data)[i], QS_INJ_WR, grp);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	/* Add padding */
122862306a36Sopenharmony_ci	while (i < (OCELOT_BUFFER_CELL_SZ / 4)) {
122962306a36Sopenharmony_ci		ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp);
123062306a36Sopenharmony_ci		i++;
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	/* Indicate EOF and valid bytes in last word */
123462306a36Sopenharmony_ci	ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
123562306a36Sopenharmony_ci			 QS_INJ_CTRL_VLD_BYTES(skb->len < OCELOT_BUFFER_CELL_SZ ? 0 : last) |
123662306a36Sopenharmony_ci			 QS_INJ_CTRL_EOF,
123762306a36Sopenharmony_ci			 QS_INJ_CTRL, grp);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	/* Add dummy CRC */
124062306a36Sopenharmony_ci	ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp);
124162306a36Sopenharmony_ci	skb_tx_timestamp(skb);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	skb->dev->stats.tx_packets++;
124462306a36Sopenharmony_ci	skb->dev->stats.tx_bytes += skb->len;
124562306a36Sopenharmony_ci}
124662306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_inject_frame);
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_civoid ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp))
125162306a36Sopenharmony_ci		ocelot_read_rix(ocelot, QS_XTR_RD, grp);
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_drain_cpu_queue);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ciint ocelot_fdb_add(struct ocelot *ocelot, int port, const unsigned char *addr,
125662306a36Sopenharmony_ci		   u16 vid, const struct net_device *bridge)
125762306a36Sopenharmony_ci{
125862306a36Sopenharmony_ci	if (!vid)
125962306a36Sopenharmony_ci		vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	return ocelot_mact_learn(ocelot, port, addr, vid, ENTRYTYPE_LOCKED);
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_fdb_add);
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ciint ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr,
126662306a36Sopenharmony_ci		   u16 vid, const struct net_device *bridge)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	if (!vid)
126962306a36Sopenharmony_ci		vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	return ocelot_mact_forget(ocelot, addr, vid);
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_fdb_del);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci/* Caller must hold &ocelot->mact_lock */
127662306a36Sopenharmony_cistatic int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
127762306a36Sopenharmony_ci			    struct ocelot_mact_entry *entry)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	u32 val, dst, macl, mach;
128062306a36Sopenharmony_ci	char mac[ETH_ALEN];
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	/* Set row and column to read from */
128362306a36Sopenharmony_ci	ocelot_field_write(ocelot, ANA_TABLES_MACTINDX_M_INDEX, row);
128462306a36Sopenharmony_ci	ocelot_field_write(ocelot, ANA_TABLES_MACTINDX_BUCKET, col);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	/* Issue a read command */
128762306a36Sopenharmony_ci	ocelot_write(ocelot,
128862306a36Sopenharmony_ci		     ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_READ),
128962306a36Sopenharmony_ci		     ANA_TABLES_MACACCESS);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	if (ocelot_mact_wait_for_completion(ocelot))
129262306a36Sopenharmony_ci		return -ETIMEDOUT;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	/* Read the entry flags */
129562306a36Sopenharmony_ci	val = ocelot_read(ocelot, ANA_TABLES_MACACCESS);
129662306a36Sopenharmony_ci	if (!(val & ANA_TABLES_MACACCESS_VALID))
129762306a36Sopenharmony_ci		return -EINVAL;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	/* If the entry read has another port configured as its destination,
130062306a36Sopenharmony_ci	 * do not report it.
130162306a36Sopenharmony_ci	 */
130262306a36Sopenharmony_ci	dst = (val & ANA_TABLES_MACACCESS_DEST_IDX_M) >> 3;
130362306a36Sopenharmony_ci	if (dst != port)
130462306a36Sopenharmony_ci		return -EINVAL;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	/* Get the entry's MAC address and VLAN id */
130762306a36Sopenharmony_ci	macl = ocelot_read(ocelot, ANA_TABLES_MACLDATA);
130862306a36Sopenharmony_ci	mach = ocelot_read(ocelot, ANA_TABLES_MACHDATA);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	mac[0] = (mach >> 8)  & 0xff;
131162306a36Sopenharmony_ci	mac[1] = (mach >> 0)  & 0xff;
131262306a36Sopenharmony_ci	mac[2] = (macl >> 24) & 0xff;
131362306a36Sopenharmony_ci	mac[3] = (macl >> 16) & 0xff;
131462306a36Sopenharmony_ci	mac[4] = (macl >> 8)  & 0xff;
131562306a36Sopenharmony_ci	mac[5] = (macl >> 0)  & 0xff;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	entry->vid = (mach >> 16) & 0xfff;
131862306a36Sopenharmony_ci	ether_addr_copy(entry->mac, mac);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	return 0;
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ciint ocelot_mact_flush(struct ocelot *ocelot, int port)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	int err;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	mutex_lock(&ocelot->mact_lock);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	/* Program ageing filter for a single port */
133062306a36Sopenharmony_ci	ocelot_write(ocelot, ANA_ANAGEFIL_PID_EN | ANA_ANAGEFIL_PID_VAL(port),
133162306a36Sopenharmony_ci		     ANA_ANAGEFIL);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	/* Flushing dynamic FDB entries requires two successive age scans */
133462306a36Sopenharmony_ci	ocelot_write(ocelot,
133562306a36Sopenharmony_ci		     ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_AGE),
133662306a36Sopenharmony_ci		     ANA_TABLES_MACACCESS);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	err = ocelot_mact_wait_for_completion(ocelot);
133962306a36Sopenharmony_ci	if (err) {
134062306a36Sopenharmony_ci		mutex_unlock(&ocelot->mact_lock);
134162306a36Sopenharmony_ci		return err;
134262306a36Sopenharmony_ci	}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	/* And second... */
134562306a36Sopenharmony_ci	ocelot_write(ocelot,
134662306a36Sopenharmony_ci		     ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_AGE),
134762306a36Sopenharmony_ci		     ANA_TABLES_MACACCESS);
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	err = ocelot_mact_wait_for_completion(ocelot);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	/* Restore ageing filter */
135262306a36Sopenharmony_ci	ocelot_write(ocelot, 0, ANA_ANAGEFIL);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	mutex_unlock(&ocelot->mact_lock);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	return err;
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_mact_flush);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ciint ocelot_fdb_dump(struct ocelot *ocelot, int port,
136162306a36Sopenharmony_ci		    dsa_fdb_dump_cb_t *cb, void *data)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	int err = 0;
136462306a36Sopenharmony_ci	int i, j;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	/* We could take the lock just around ocelot_mact_read, but doing so
136762306a36Sopenharmony_ci	 * thousands of times in a row seems rather pointless and inefficient.
136862306a36Sopenharmony_ci	 */
136962306a36Sopenharmony_ci	mutex_lock(&ocelot->mact_lock);
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	/* Loop through all the mac tables entries. */
137262306a36Sopenharmony_ci	for (i = 0; i < ocelot->num_mact_rows; i++) {
137362306a36Sopenharmony_ci		for (j = 0; j < 4; j++) {
137462306a36Sopenharmony_ci			struct ocelot_mact_entry entry;
137562306a36Sopenharmony_ci			bool is_static;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci			err = ocelot_mact_read(ocelot, port, i, j, &entry);
137862306a36Sopenharmony_ci			/* If the entry is invalid (wrong port, invalid...),
137962306a36Sopenharmony_ci			 * skip it.
138062306a36Sopenharmony_ci			 */
138162306a36Sopenharmony_ci			if (err == -EINVAL)
138262306a36Sopenharmony_ci				continue;
138362306a36Sopenharmony_ci			else if (err)
138462306a36Sopenharmony_ci				break;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci			is_static = (entry.type == ENTRYTYPE_LOCKED);
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci			/* Hide the reserved VLANs used for
138962306a36Sopenharmony_ci			 * VLAN-unaware bridging.
139062306a36Sopenharmony_ci			 */
139162306a36Sopenharmony_ci			if (entry.vid > OCELOT_RSV_VLAN_RANGE_START)
139262306a36Sopenharmony_ci				entry.vid = 0;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci			err = cb(entry.mac, entry.vid, is_static, data);
139562306a36Sopenharmony_ci			if (err)
139662306a36Sopenharmony_ci				break;
139762306a36Sopenharmony_ci		}
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	mutex_unlock(&ocelot->mact_lock);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	return err;
140362306a36Sopenharmony_ci}
140462306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_fdb_dump);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ciint ocelot_trap_add(struct ocelot *ocelot, int port,
140762306a36Sopenharmony_ci		    unsigned long cookie, bool take_ts,
140862306a36Sopenharmony_ci		    void (*populate)(struct ocelot_vcap_filter *f))
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	struct ocelot_vcap_block *block_vcap_is2;
141162306a36Sopenharmony_ci	struct ocelot_vcap_filter *trap;
141262306a36Sopenharmony_ci	bool new = false;
141362306a36Sopenharmony_ci	int err;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	block_vcap_is2 = &ocelot->block[VCAP_IS2];
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	trap = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, cookie,
141862306a36Sopenharmony_ci						   false);
141962306a36Sopenharmony_ci	if (!trap) {
142062306a36Sopenharmony_ci		trap = kzalloc(sizeof(*trap), GFP_KERNEL);
142162306a36Sopenharmony_ci		if (!trap)
142262306a36Sopenharmony_ci			return -ENOMEM;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci		populate(trap);
142562306a36Sopenharmony_ci		trap->prio = 1;
142662306a36Sopenharmony_ci		trap->id.cookie = cookie;
142762306a36Sopenharmony_ci		trap->id.tc_offload = false;
142862306a36Sopenharmony_ci		trap->block_id = VCAP_IS2;
142962306a36Sopenharmony_ci		trap->type = OCELOT_VCAP_FILTER_OFFLOAD;
143062306a36Sopenharmony_ci		trap->lookup = 0;
143162306a36Sopenharmony_ci		trap->action.cpu_copy_ena = true;
143262306a36Sopenharmony_ci		trap->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
143362306a36Sopenharmony_ci		trap->action.port_mask = 0;
143462306a36Sopenharmony_ci		trap->take_ts = take_ts;
143562306a36Sopenharmony_ci		trap->is_trap = true;
143662306a36Sopenharmony_ci		new = true;
143762306a36Sopenharmony_ci	}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	trap->ingress_port_mask |= BIT(port);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	if (new)
144262306a36Sopenharmony_ci		err = ocelot_vcap_filter_add(ocelot, trap, NULL);
144362306a36Sopenharmony_ci	else
144462306a36Sopenharmony_ci		err = ocelot_vcap_filter_replace(ocelot, trap);
144562306a36Sopenharmony_ci	if (err) {
144662306a36Sopenharmony_ci		trap->ingress_port_mask &= ~BIT(port);
144762306a36Sopenharmony_ci		if (!trap->ingress_port_mask)
144862306a36Sopenharmony_ci			kfree(trap);
144962306a36Sopenharmony_ci		return err;
145062306a36Sopenharmony_ci	}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	return 0;
145362306a36Sopenharmony_ci}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ciint ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie)
145662306a36Sopenharmony_ci{
145762306a36Sopenharmony_ci	struct ocelot_vcap_block *block_vcap_is2;
145862306a36Sopenharmony_ci	struct ocelot_vcap_filter *trap;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	block_vcap_is2 = &ocelot->block[VCAP_IS2];
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	trap = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, cookie,
146362306a36Sopenharmony_ci						   false);
146462306a36Sopenharmony_ci	if (!trap)
146562306a36Sopenharmony_ci		return 0;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	trap->ingress_port_mask &= ~BIT(port);
146862306a36Sopenharmony_ci	if (!trap->ingress_port_mask)
146962306a36Sopenharmony_ci		return ocelot_vcap_filter_del(ocelot, trap);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	return ocelot_vcap_filter_replace(ocelot, trap);
147262306a36Sopenharmony_ci}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_cistatic u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	u32 mask = 0;
147762306a36Sopenharmony_ci	int port;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	lockdep_assert_held(&ocelot->fwd_domain_lock);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
148262306a36Sopenharmony_ci		struct ocelot_port *ocelot_port = ocelot->ports[port];
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci		if (!ocelot_port)
148562306a36Sopenharmony_ci			continue;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		if (ocelot_port->bond == bond)
148862306a36Sopenharmony_ci			mask |= BIT(port);
148962306a36Sopenharmony_ci	}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	return mask;
149262306a36Sopenharmony_ci}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci/* The logical port number of a LAG is equal to the lowest numbered physical
149562306a36Sopenharmony_ci * port ID present in that LAG. It may change if that port ever leaves the LAG.
149662306a36Sopenharmony_ci */
149762306a36Sopenharmony_ciint ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	int bond_mask = ocelot_get_bond_mask(ocelot, bond);
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	if (!bond_mask)
150262306a36Sopenharmony_ci		return -ENOENT;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	return __ffs(bond_mask);
150562306a36Sopenharmony_ci}
150662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_bond_get_id);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci/* Returns the mask of user ports assigned to this DSA tag_8021q CPU port.
150962306a36Sopenharmony_ci * Note that when CPU ports are in a LAG, the user ports are assigned to the
151062306a36Sopenharmony_ci * 'primary' CPU port, the one whose physical port number gives the logical
151162306a36Sopenharmony_ci * port number of the LAG.
151262306a36Sopenharmony_ci *
151362306a36Sopenharmony_ci * We leave PGID_SRC poorly configured for the 'secondary' CPU port in the LAG
151462306a36Sopenharmony_ci * (to which no user port is assigned), but it appears that forwarding from
151562306a36Sopenharmony_ci * this secondary CPU port looks at the PGID_SRC associated with the logical
151662306a36Sopenharmony_ci * port ID that it's assigned to, which *is* configured properly.
151762306a36Sopenharmony_ci */
151862306a36Sopenharmony_cistatic u32 ocelot_dsa_8021q_cpu_assigned_ports(struct ocelot *ocelot,
151962306a36Sopenharmony_ci					       struct ocelot_port *cpu)
152062306a36Sopenharmony_ci{
152162306a36Sopenharmony_ci	u32 mask = 0;
152262306a36Sopenharmony_ci	int port;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
152562306a36Sopenharmony_ci		struct ocelot_port *ocelot_port = ocelot->ports[port];
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci		if (!ocelot_port)
152862306a36Sopenharmony_ci			continue;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci		if (ocelot_port->dsa_8021q_cpu == cpu)
153162306a36Sopenharmony_ci			mask |= BIT(port);
153262306a36Sopenharmony_ci	}
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	if (cpu->bond)
153562306a36Sopenharmony_ci		mask &= ~ocelot_get_bond_mask(ocelot, cpu->bond);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	return mask;
153862306a36Sopenharmony_ci}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci/* Returns the DSA tag_8021q CPU port that the given port is assigned to,
154162306a36Sopenharmony_ci * or the bit mask of CPU ports if said CPU port is in a LAG.
154262306a36Sopenharmony_ci */
154362306a36Sopenharmony_ciu32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port)
154462306a36Sopenharmony_ci{
154562306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
154662306a36Sopenharmony_ci	struct ocelot_port *cpu_port = ocelot_port->dsa_8021q_cpu;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	if (!cpu_port)
154962306a36Sopenharmony_ci		return 0;
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	if (cpu_port->bond)
155262306a36Sopenharmony_ci		return ocelot_get_bond_mask(ocelot, cpu_port->bond);
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	return BIT(cpu_port->index);
155562306a36Sopenharmony_ci}
155662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_assigned_dsa_8021q_cpu_mask);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ciu32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port)
155962306a36Sopenharmony_ci{
156062306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[src_port];
156162306a36Sopenharmony_ci	const struct net_device *bridge;
156262306a36Sopenharmony_ci	u32 mask = 0;
156362306a36Sopenharmony_ci	int port;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	if (!ocelot_port || ocelot_port->stp_state != BR_STATE_FORWARDING)
156662306a36Sopenharmony_ci		return 0;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	bridge = ocelot_port->bridge;
156962306a36Sopenharmony_ci	if (!bridge)
157062306a36Sopenharmony_ci		return 0;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
157362306a36Sopenharmony_ci		ocelot_port = ocelot->ports[port];
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci		if (!ocelot_port)
157662306a36Sopenharmony_ci			continue;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci		if (ocelot_port->stp_state == BR_STATE_FORWARDING &&
157962306a36Sopenharmony_ci		    ocelot_port->bridge == bridge)
158062306a36Sopenharmony_ci			mask |= BIT(port);
158162306a36Sopenharmony_ci	}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	return mask;
158462306a36Sopenharmony_ci}
158562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_get_bridge_fwd_mask);
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_cistatic void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
158862306a36Sopenharmony_ci{
158962306a36Sopenharmony_ci	int port;
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	lockdep_assert_held(&ocelot->fwd_domain_lock);
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	/* If cut-through forwarding is supported, update the masks before a
159462306a36Sopenharmony_ci	 * port joins the forwarding domain, to avoid potential underruns if it
159562306a36Sopenharmony_ci	 * has the highest speed from the new domain.
159662306a36Sopenharmony_ci	 */
159762306a36Sopenharmony_ci	if (joining && ocelot->ops->cut_through_fwd)
159862306a36Sopenharmony_ci		ocelot->ops->cut_through_fwd(ocelot);
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	/* Apply FWD mask. The loop is needed to add/remove the current port as
160162306a36Sopenharmony_ci	 * a source for the other ports.
160262306a36Sopenharmony_ci	 */
160362306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
160462306a36Sopenharmony_ci		struct ocelot_port *ocelot_port = ocelot->ports[port];
160562306a36Sopenharmony_ci		unsigned long mask;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci		if (!ocelot_port) {
160862306a36Sopenharmony_ci			/* Unused ports can't send anywhere */
160962306a36Sopenharmony_ci			mask = 0;
161062306a36Sopenharmony_ci		} else if (ocelot_port->is_dsa_8021q_cpu) {
161162306a36Sopenharmony_ci			/* The DSA tag_8021q CPU ports need to be able to
161262306a36Sopenharmony_ci			 * forward packets to all ports assigned to them.
161362306a36Sopenharmony_ci			 */
161462306a36Sopenharmony_ci			mask = ocelot_dsa_8021q_cpu_assigned_ports(ocelot,
161562306a36Sopenharmony_ci								   ocelot_port);
161662306a36Sopenharmony_ci		} else if (ocelot_port->bridge) {
161762306a36Sopenharmony_ci			struct net_device *bond = ocelot_port->bond;
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci			mask = ocelot_get_bridge_fwd_mask(ocelot, port);
162062306a36Sopenharmony_ci			mask &= ~BIT(port);
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci			mask |= ocelot_port_assigned_dsa_8021q_cpu_mask(ocelot,
162362306a36Sopenharmony_ci									port);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci			if (bond)
162662306a36Sopenharmony_ci				mask &= ~ocelot_get_bond_mask(ocelot, bond);
162762306a36Sopenharmony_ci		} else {
162862306a36Sopenharmony_ci			/* Standalone ports forward only to DSA tag_8021q CPU
162962306a36Sopenharmony_ci			 * ports (if those exist), or to the hardware CPU port
163062306a36Sopenharmony_ci			 * module otherwise.
163162306a36Sopenharmony_ci			 */
163262306a36Sopenharmony_ci			mask = ocelot_port_assigned_dsa_8021q_cpu_mask(ocelot,
163362306a36Sopenharmony_ci								       port);
163462306a36Sopenharmony_ci		}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci		ocelot_write_rix(ocelot, mask, ANA_PGID_PGID, PGID_SRC + port);
163762306a36Sopenharmony_ci	}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	/* If cut-through forwarding is supported and a port is leaving, there
164062306a36Sopenharmony_ci	 * is a chance that cut-through was disabled on the other ports due to
164162306a36Sopenharmony_ci	 * the port which is leaving (it has a higher link speed). We need to
164262306a36Sopenharmony_ci	 * update the cut-through masks of the remaining ports no earlier than
164362306a36Sopenharmony_ci	 * after the port has left, to prevent underruns from happening between
164462306a36Sopenharmony_ci	 * the cut-through update and the forwarding domain update.
164562306a36Sopenharmony_ci	 */
164662306a36Sopenharmony_ci	if (!joining && ocelot->ops->cut_through_fwd)
164762306a36Sopenharmony_ci		ocelot->ops->cut_through_fwd(ocelot);
164862306a36Sopenharmony_ci}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci/* Update PGID_CPU which is the destination port mask used for whitelisting
165162306a36Sopenharmony_ci * unicast addresses filtered towards the host. In the normal and NPI modes,
165262306a36Sopenharmony_ci * this points to the analyzer entry for the CPU port module, while in DSA
165362306a36Sopenharmony_ci * tag_8021q mode, it is a bit mask of all active CPU ports.
165462306a36Sopenharmony_ci * PGID_SRC will take care of forwarding a packet from one user port to
165562306a36Sopenharmony_ci * no more than a single CPU port.
165662306a36Sopenharmony_ci */
165762306a36Sopenharmony_cistatic void ocelot_update_pgid_cpu(struct ocelot *ocelot)
165862306a36Sopenharmony_ci{
165962306a36Sopenharmony_ci	int pgid_cpu = 0;
166062306a36Sopenharmony_ci	int port;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
166362306a36Sopenharmony_ci		struct ocelot_port *ocelot_port = ocelot->ports[port];
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci		if (!ocelot_port || !ocelot_port->is_dsa_8021q_cpu)
166662306a36Sopenharmony_ci			continue;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci		pgid_cpu |= BIT(port);
166962306a36Sopenharmony_ci	}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	if (!pgid_cpu)
167262306a36Sopenharmony_ci		pgid_cpu = BIT(ocelot->num_phys_ports);
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	ocelot_write_rix(ocelot, pgid_cpu, ANA_PGID_PGID, PGID_CPU);
167562306a36Sopenharmony_ci}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_civoid ocelot_port_setup_dsa_8021q_cpu(struct ocelot *ocelot, int cpu)
167862306a36Sopenharmony_ci{
167962306a36Sopenharmony_ci	struct ocelot_port *cpu_port = ocelot->ports[cpu];
168062306a36Sopenharmony_ci	u16 vid;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	cpu_port->is_dsa_8021q_cpu = true;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++)
168762306a36Sopenharmony_ci		ocelot_vlan_member_add(ocelot, cpu, vid, true);
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	ocelot_update_pgid_cpu(ocelot);
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
169262306a36Sopenharmony_ci}
169362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_setup_dsa_8021q_cpu);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_civoid ocelot_port_teardown_dsa_8021q_cpu(struct ocelot *ocelot, int cpu)
169662306a36Sopenharmony_ci{
169762306a36Sopenharmony_ci	struct ocelot_port *cpu_port = ocelot->ports[cpu];
169862306a36Sopenharmony_ci	u16 vid;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	cpu_port->is_dsa_8021q_cpu = false;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++)
170562306a36Sopenharmony_ci		ocelot_vlan_member_del(ocelot, cpu_port->index, vid);
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	ocelot_update_pgid_cpu(ocelot);
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
171062306a36Sopenharmony_ci}
171162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_teardown_dsa_8021q_cpu);
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_civoid ocelot_port_assign_dsa_8021q_cpu(struct ocelot *ocelot, int port,
171462306a36Sopenharmony_ci				      int cpu)
171562306a36Sopenharmony_ci{
171662306a36Sopenharmony_ci	struct ocelot_port *cpu_port = ocelot->ports[cpu];
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	ocelot->ports[port]->dsa_8021q_cpu = cpu_port;
172162306a36Sopenharmony_ci	ocelot_apply_bridge_fwd_mask(ocelot, true);
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
172462306a36Sopenharmony_ci}
172562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_assign_dsa_8021q_cpu);
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_civoid ocelot_port_unassign_dsa_8021q_cpu(struct ocelot *ocelot, int port)
172862306a36Sopenharmony_ci{
172962306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	ocelot->ports[port]->dsa_8021q_cpu = NULL;
173262306a36Sopenharmony_ci	ocelot_apply_bridge_fwd_mask(ocelot, true);
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
173562306a36Sopenharmony_ci}
173662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_unassign_dsa_8021q_cpu);
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_civoid ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
173962306a36Sopenharmony_ci{
174062306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
174162306a36Sopenharmony_ci	u32 learn_ena = 0;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	ocelot_port->stp_state = state;
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	if ((state == BR_STATE_LEARNING || state == BR_STATE_FORWARDING) &&
174862306a36Sopenharmony_ci	    ocelot_port->learn_ena)
174962306a36Sopenharmony_ci		learn_ena = ANA_PORT_PORT_CFG_LEARN_ENA;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, learn_ena, ANA_PORT_PORT_CFG_LEARN_ENA,
175262306a36Sopenharmony_ci		       ANA_PORT_PORT_CFG, port);
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	ocelot_apply_bridge_fwd_mask(ocelot, state == BR_STATE_FORWARDING);
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
175762306a36Sopenharmony_ci}
175862306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_bridge_stp_state_set);
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_civoid ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs)
176162306a36Sopenharmony_ci{
176262306a36Sopenharmony_ci	unsigned int age_period = ANA_AUTOAGE_AGE_PERIOD(msecs / 2000);
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	/* Setting AGE_PERIOD to zero effectively disables automatic aging,
176562306a36Sopenharmony_ci	 * which is clearly not what our intention is. So avoid that.
176662306a36Sopenharmony_ci	 */
176762306a36Sopenharmony_ci	if (!age_period)
176862306a36Sopenharmony_ci		age_period = 1;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	ocelot_rmw(ocelot, age_period, ANA_AUTOAGE_AGE_PERIOD_M, ANA_AUTOAGE);
177162306a36Sopenharmony_ci}
177262306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_set_ageing_time);
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_cistatic struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot,
177562306a36Sopenharmony_ci						     const unsigned char *addr,
177662306a36Sopenharmony_ci						     u16 vid)
177762306a36Sopenharmony_ci{
177862306a36Sopenharmony_ci	struct ocelot_multicast *mc;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	list_for_each_entry(mc, &ocelot->multicast, list) {
178162306a36Sopenharmony_ci		if (ether_addr_equal(mc->addr, addr) && mc->vid == vid)
178262306a36Sopenharmony_ci			return mc;
178362306a36Sopenharmony_ci	}
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	return NULL;
178662306a36Sopenharmony_ci}
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_cistatic enum macaccess_entry_type ocelot_classify_mdb(const unsigned char *addr)
178962306a36Sopenharmony_ci{
179062306a36Sopenharmony_ci	if (addr[0] == 0x01 && addr[1] == 0x00 && addr[2] == 0x5e)
179162306a36Sopenharmony_ci		return ENTRYTYPE_MACv4;
179262306a36Sopenharmony_ci	if (addr[0] == 0x33 && addr[1] == 0x33)
179362306a36Sopenharmony_ci		return ENTRYTYPE_MACv6;
179462306a36Sopenharmony_ci	return ENTRYTYPE_LOCKED;
179562306a36Sopenharmony_ci}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_cistatic struct ocelot_pgid *ocelot_pgid_alloc(struct ocelot *ocelot, int index,
179862306a36Sopenharmony_ci					     unsigned long ports)
179962306a36Sopenharmony_ci{
180062306a36Sopenharmony_ci	struct ocelot_pgid *pgid;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	pgid = kzalloc(sizeof(*pgid), GFP_KERNEL);
180362306a36Sopenharmony_ci	if (!pgid)
180462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	pgid->ports = ports;
180762306a36Sopenharmony_ci	pgid->index = index;
180862306a36Sopenharmony_ci	refcount_set(&pgid->refcount, 1);
180962306a36Sopenharmony_ci	list_add_tail(&pgid->list, &ocelot->pgids);
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	return pgid;
181262306a36Sopenharmony_ci}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_cistatic void ocelot_pgid_free(struct ocelot *ocelot, struct ocelot_pgid *pgid)
181562306a36Sopenharmony_ci{
181662306a36Sopenharmony_ci	if (!refcount_dec_and_test(&pgid->refcount))
181762306a36Sopenharmony_ci		return;
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	list_del(&pgid->list);
182062306a36Sopenharmony_ci	kfree(pgid);
182162306a36Sopenharmony_ci}
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_cistatic struct ocelot_pgid *ocelot_mdb_get_pgid(struct ocelot *ocelot,
182462306a36Sopenharmony_ci					       const struct ocelot_multicast *mc)
182562306a36Sopenharmony_ci{
182662306a36Sopenharmony_ci	struct ocelot_pgid *pgid;
182762306a36Sopenharmony_ci	int index;
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	/* According to VSC7514 datasheet 3.9.1.5 IPv4 Multicast Entries and
183062306a36Sopenharmony_ci	 * 3.9.1.6 IPv6 Multicast Entries, "Instead of a lookup in the
183162306a36Sopenharmony_ci	 * destination mask table (PGID), the destination set is programmed as
183262306a36Sopenharmony_ci	 * part of the entry MAC address.", and the DEST_IDX is set to 0.
183362306a36Sopenharmony_ci	 */
183462306a36Sopenharmony_ci	if (mc->entry_type == ENTRYTYPE_MACv4 ||
183562306a36Sopenharmony_ci	    mc->entry_type == ENTRYTYPE_MACv6)
183662306a36Sopenharmony_ci		return ocelot_pgid_alloc(ocelot, 0, mc->ports);
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	list_for_each_entry(pgid, &ocelot->pgids, list) {
183962306a36Sopenharmony_ci		/* When searching for a nonreserved multicast PGID, ignore the
184062306a36Sopenharmony_ci		 * dummy PGID of zero that we have for MACv4/MACv6 entries
184162306a36Sopenharmony_ci		 */
184262306a36Sopenharmony_ci		if (pgid->index && pgid->ports == mc->ports) {
184362306a36Sopenharmony_ci			refcount_inc(&pgid->refcount);
184462306a36Sopenharmony_ci			return pgid;
184562306a36Sopenharmony_ci		}
184662306a36Sopenharmony_ci	}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	/* Search for a free index in the nonreserved multicast PGID area */
184962306a36Sopenharmony_ci	for_each_nonreserved_multicast_dest_pgid(ocelot, index) {
185062306a36Sopenharmony_ci		bool used = false;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci		list_for_each_entry(pgid, &ocelot->pgids, list) {
185362306a36Sopenharmony_ci			if (pgid->index == index) {
185462306a36Sopenharmony_ci				used = true;
185562306a36Sopenharmony_ci				break;
185662306a36Sopenharmony_ci			}
185762306a36Sopenharmony_ci		}
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci		if (!used)
186062306a36Sopenharmony_ci			return ocelot_pgid_alloc(ocelot, index, mc->ports);
186162306a36Sopenharmony_ci	}
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	return ERR_PTR(-ENOSPC);
186462306a36Sopenharmony_ci}
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_cistatic void ocelot_encode_ports_to_mdb(unsigned char *addr,
186762306a36Sopenharmony_ci				       struct ocelot_multicast *mc)
186862306a36Sopenharmony_ci{
186962306a36Sopenharmony_ci	ether_addr_copy(addr, mc->addr);
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	if (mc->entry_type == ENTRYTYPE_MACv4) {
187262306a36Sopenharmony_ci		addr[0] = 0;
187362306a36Sopenharmony_ci		addr[1] = mc->ports >> 8;
187462306a36Sopenharmony_ci		addr[2] = mc->ports & 0xff;
187562306a36Sopenharmony_ci	} else if (mc->entry_type == ENTRYTYPE_MACv6) {
187662306a36Sopenharmony_ci		addr[0] = mc->ports >> 8;
187762306a36Sopenharmony_ci		addr[1] = mc->ports & 0xff;
187862306a36Sopenharmony_ci	}
187962306a36Sopenharmony_ci}
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ciint ocelot_port_mdb_add(struct ocelot *ocelot, int port,
188262306a36Sopenharmony_ci			const struct switchdev_obj_port_mdb *mdb,
188362306a36Sopenharmony_ci			const struct net_device *bridge)
188462306a36Sopenharmony_ci{
188562306a36Sopenharmony_ci	unsigned char addr[ETH_ALEN];
188662306a36Sopenharmony_ci	struct ocelot_multicast *mc;
188762306a36Sopenharmony_ci	struct ocelot_pgid *pgid;
188862306a36Sopenharmony_ci	u16 vid = mdb->vid;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	if (!vid)
189162306a36Sopenharmony_ci		vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
189462306a36Sopenharmony_ci	if (!mc) {
189562306a36Sopenharmony_ci		/* New entry */
189662306a36Sopenharmony_ci		mc = devm_kzalloc(ocelot->dev, sizeof(*mc), GFP_KERNEL);
189762306a36Sopenharmony_ci		if (!mc)
189862306a36Sopenharmony_ci			return -ENOMEM;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci		mc->entry_type = ocelot_classify_mdb(mdb->addr);
190162306a36Sopenharmony_ci		ether_addr_copy(mc->addr, mdb->addr);
190262306a36Sopenharmony_ci		mc->vid = vid;
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci		list_add_tail(&mc->list, &ocelot->multicast);
190562306a36Sopenharmony_ci	} else {
190662306a36Sopenharmony_ci		/* Existing entry. Clean up the current port mask from
190762306a36Sopenharmony_ci		 * hardware now, because we'll be modifying it.
190862306a36Sopenharmony_ci		 */
190962306a36Sopenharmony_ci		ocelot_pgid_free(ocelot, mc->pgid);
191062306a36Sopenharmony_ci		ocelot_encode_ports_to_mdb(addr, mc);
191162306a36Sopenharmony_ci		ocelot_mact_forget(ocelot, addr, vid);
191262306a36Sopenharmony_ci	}
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	mc->ports |= BIT(port);
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	pgid = ocelot_mdb_get_pgid(ocelot, mc);
191762306a36Sopenharmony_ci	if (IS_ERR(pgid)) {
191862306a36Sopenharmony_ci		dev_err(ocelot->dev,
191962306a36Sopenharmony_ci			"Cannot allocate PGID for mdb %pM vid %d\n",
192062306a36Sopenharmony_ci			mc->addr, mc->vid);
192162306a36Sopenharmony_ci		devm_kfree(ocelot->dev, mc);
192262306a36Sopenharmony_ci		return PTR_ERR(pgid);
192362306a36Sopenharmony_ci	}
192462306a36Sopenharmony_ci	mc->pgid = pgid;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	ocelot_encode_ports_to_mdb(addr, mc);
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	if (mc->entry_type != ENTRYTYPE_MACv4 &&
192962306a36Sopenharmony_ci	    mc->entry_type != ENTRYTYPE_MACv6)
193062306a36Sopenharmony_ci		ocelot_write_rix(ocelot, pgid->ports, ANA_PGID_PGID,
193162306a36Sopenharmony_ci				 pgid->index);
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	return ocelot_mact_learn(ocelot, pgid->index, addr, vid,
193462306a36Sopenharmony_ci				 mc->entry_type);
193562306a36Sopenharmony_ci}
193662306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_mdb_add);
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ciint ocelot_port_mdb_del(struct ocelot *ocelot, int port,
193962306a36Sopenharmony_ci			const struct switchdev_obj_port_mdb *mdb,
194062306a36Sopenharmony_ci			const struct net_device *bridge)
194162306a36Sopenharmony_ci{
194262306a36Sopenharmony_ci	unsigned char addr[ETH_ALEN];
194362306a36Sopenharmony_ci	struct ocelot_multicast *mc;
194462306a36Sopenharmony_ci	struct ocelot_pgid *pgid;
194562306a36Sopenharmony_ci	u16 vid = mdb->vid;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	if (!vid)
194862306a36Sopenharmony_ci		vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
195162306a36Sopenharmony_ci	if (!mc)
195262306a36Sopenharmony_ci		return -ENOENT;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	ocelot_encode_ports_to_mdb(addr, mc);
195562306a36Sopenharmony_ci	ocelot_mact_forget(ocelot, addr, vid);
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	ocelot_pgid_free(ocelot, mc->pgid);
195862306a36Sopenharmony_ci	mc->ports &= ~BIT(port);
195962306a36Sopenharmony_ci	if (!mc->ports) {
196062306a36Sopenharmony_ci		list_del(&mc->list);
196162306a36Sopenharmony_ci		devm_kfree(ocelot->dev, mc);
196262306a36Sopenharmony_ci		return 0;
196362306a36Sopenharmony_ci	}
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	/* We have a PGID with fewer ports now */
196662306a36Sopenharmony_ci	pgid = ocelot_mdb_get_pgid(ocelot, mc);
196762306a36Sopenharmony_ci	if (IS_ERR(pgid))
196862306a36Sopenharmony_ci		return PTR_ERR(pgid);
196962306a36Sopenharmony_ci	mc->pgid = pgid;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	ocelot_encode_ports_to_mdb(addr, mc);
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	if (mc->entry_type != ENTRYTYPE_MACv4 &&
197462306a36Sopenharmony_ci	    mc->entry_type != ENTRYTYPE_MACv6)
197562306a36Sopenharmony_ci		ocelot_write_rix(ocelot, pgid->ports, ANA_PGID_PGID,
197662306a36Sopenharmony_ci				 pgid->index);
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	return ocelot_mact_learn(ocelot, pgid->index, addr, vid,
197962306a36Sopenharmony_ci				 mc->entry_type);
198062306a36Sopenharmony_ci}
198162306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_mdb_del);
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ciint ocelot_port_bridge_join(struct ocelot *ocelot, int port,
198462306a36Sopenharmony_ci			    struct net_device *bridge, int bridge_num,
198562306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
198662306a36Sopenharmony_ci{
198762306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
198862306a36Sopenharmony_ci	int err;
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	err = ocelot_single_vlan_aware_bridge(ocelot, extack);
199162306a36Sopenharmony_ci	if (err)
199262306a36Sopenharmony_ci		return err;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	ocelot_port->bridge = bridge;
199762306a36Sopenharmony_ci	ocelot_port->bridge_num = bridge_num;
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	ocelot_apply_bridge_fwd_mask(ocelot, true);
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	if (br_vlan_enabled(bridge))
200462306a36Sopenharmony_ci		return 0;
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	return ocelot_add_vlan_unaware_pvid(ocelot, port, bridge);
200762306a36Sopenharmony_ci}
200862306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_bridge_join);
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_civoid ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
201162306a36Sopenharmony_ci			      struct net_device *bridge)
201262306a36Sopenharmony_ci{
201362306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	if (!br_vlan_enabled(bridge))
201862306a36Sopenharmony_ci		ocelot_del_vlan_unaware_pvid(ocelot, port, bridge);
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	ocelot_port->bridge = NULL;
202162306a36Sopenharmony_ci	ocelot_port->bridge_num = -1;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	ocelot_port_set_pvid(ocelot, port, NULL);
202462306a36Sopenharmony_ci	ocelot_port_manage_port_tag(ocelot, port);
202562306a36Sopenharmony_ci	ocelot_apply_bridge_fwd_mask(ocelot, false);
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
202862306a36Sopenharmony_ci}
202962306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_bridge_leave);
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_cistatic void ocelot_set_aggr_pgids(struct ocelot *ocelot)
203262306a36Sopenharmony_ci{
203362306a36Sopenharmony_ci	unsigned long visited = GENMASK(ocelot->num_phys_ports - 1, 0);
203462306a36Sopenharmony_ci	int i, port, lag;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	/* Reset destination and aggregation PGIDS */
203762306a36Sopenharmony_ci	for_each_unicast_dest_pgid(ocelot, port)
203862306a36Sopenharmony_ci		ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, port);
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	for_each_aggr_pgid(ocelot, i)
204162306a36Sopenharmony_ci		ocelot_write_rix(ocelot, GENMASK(ocelot->num_phys_ports - 1, 0),
204262306a36Sopenharmony_ci				 ANA_PGID_PGID, i);
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci	/* The visited ports bitmask holds the list of ports offloading any
204562306a36Sopenharmony_ci	 * bonding interface. Initially we mark all these ports as unvisited,
204662306a36Sopenharmony_ci	 * then every time we visit a port in this bitmask, we know that it is
204762306a36Sopenharmony_ci	 * the lowest numbered port, i.e. the one whose logical ID == physical
204862306a36Sopenharmony_ci	 * port ID == LAG ID. So we mark as visited all further ports in the
204962306a36Sopenharmony_ci	 * bitmask that are offloading the same bonding interface. This way,
205062306a36Sopenharmony_ci	 * we set up the aggregation PGIDs only once per bonding interface.
205162306a36Sopenharmony_ci	 */
205262306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
205362306a36Sopenharmony_ci		struct ocelot_port *ocelot_port = ocelot->ports[port];
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci		if (!ocelot_port || !ocelot_port->bond)
205662306a36Sopenharmony_ci			continue;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci		visited &= ~BIT(port);
205962306a36Sopenharmony_ci	}
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	/* Now, set PGIDs for each active LAG */
206262306a36Sopenharmony_ci	for (lag = 0; lag < ocelot->num_phys_ports; lag++) {
206362306a36Sopenharmony_ci		struct net_device *bond = ocelot->ports[lag]->bond;
206462306a36Sopenharmony_ci		int num_active_ports = 0;
206562306a36Sopenharmony_ci		unsigned long bond_mask;
206662306a36Sopenharmony_ci		u8 aggr_idx[16];
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci		if (!bond || (visited & BIT(lag)))
206962306a36Sopenharmony_ci			continue;
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci		bond_mask = ocelot_get_bond_mask(ocelot, bond);
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci		for_each_set_bit(port, &bond_mask, ocelot->num_phys_ports) {
207462306a36Sopenharmony_ci			struct ocelot_port *ocelot_port = ocelot->ports[port];
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci			// Destination mask
207762306a36Sopenharmony_ci			ocelot_write_rix(ocelot, bond_mask,
207862306a36Sopenharmony_ci					 ANA_PGID_PGID, port);
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci			if (ocelot_port->lag_tx_active)
208162306a36Sopenharmony_ci				aggr_idx[num_active_ports++] = port;
208262306a36Sopenharmony_ci		}
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci		for_each_aggr_pgid(ocelot, i) {
208562306a36Sopenharmony_ci			u32 ac;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci			ac = ocelot_read_rix(ocelot, ANA_PGID_PGID, i);
208862306a36Sopenharmony_ci			ac &= ~bond_mask;
208962306a36Sopenharmony_ci			/* Don't do division by zero if there was no active
209062306a36Sopenharmony_ci			 * port. Just make all aggregation codes zero.
209162306a36Sopenharmony_ci			 */
209262306a36Sopenharmony_ci			if (num_active_ports)
209362306a36Sopenharmony_ci				ac |= BIT(aggr_idx[i % num_active_ports]);
209462306a36Sopenharmony_ci			ocelot_write_rix(ocelot, ac, ANA_PGID_PGID, i);
209562306a36Sopenharmony_ci		}
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci		/* Mark all ports in the same LAG as visited to avoid applying
209862306a36Sopenharmony_ci		 * the same config again.
209962306a36Sopenharmony_ci		 */
210062306a36Sopenharmony_ci		for (port = lag; port < ocelot->num_phys_ports; port++) {
210162306a36Sopenharmony_ci			struct ocelot_port *ocelot_port = ocelot->ports[port];
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci			if (!ocelot_port)
210462306a36Sopenharmony_ci				continue;
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci			if (ocelot_port->bond == bond)
210762306a36Sopenharmony_ci				visited |= BIT(port);
210862306a36Sopenharmony_ci		}
210962306a36Sopenharmony_ci	}
211062306a36Sopenharmony_ci}
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci/* When offloading a bonding interface, the switch ports configured under the
211362306a36Sopenharmony_ci * same bond must have the same logical port ID, equal to the physical port ID
211462306a36Sopenharmony_ci * of the lowest numbered physical port in that bond. Otherwise, in standalone/
211562306a36Sopenharmony_ci * bridged mode, each port has a logical port ID equal to its physical port ID.
211662306a36Sopenharmony_ci */
211762306a36Sopenharmony_cistatic void ocelot_setup_logical_port_ids(struct ocelot *ocelot)
211862306a36Sopenharmony_ci{
211962306a36Sopenharmony_ci	int port;
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
212262306a36Sopenharmony_ci		struct ocelot_port *ocelot_port = ocelot->ports[port];
212362306a36Sopenharmony_ci		struct net_device *bond;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci		if (!ocelot_port)
212662306a36Sopenharmony_ci			continue;
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci		bond = ocelot_port->bond;
212962306a36Sopenharmony_ci		if (bond) {
213062306a36Sopenharmony_ci			int lag = ocelot_bond_get_id(ocelot, bond);
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci			ocelot_rmw_gix(ocelot,
213362306a36Sopenharmony_ci				       ANA_PORT_PORT_CFG_PORTID_VAL(lag),
213462306a36Sopenharmony_ci				       ANA_PORT_PORT_CFG_PORTID_VAL_M,
213562306a36Sopenharmony_ci				       ANA_PORT_PORT_CFG, port);
213662306a36Sopenharmony_ci		} else {
213762306a36Sopenharmony_ci			ocelot_rmw_gix(ocelot,
213862306a36Sopenharmony_ci				       ANA_PORT_PORT_CFG_PORTID_VAL(port),
213962306a36Sopenharmony_ci				       ANA_PORT_PORT_CFG_PORTID_VAL_M,
214062306a36Sopenharmony_ci				       ANA_PORT_PORT_CFG, port);
214162306a36Sopenharmony_ci		}
214262306a36Sopenharmony_ci	}
214362306a36Sopenharmony_ci}
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_cistatic int ocelot_migrate_mc(struct ocelot *ocelot, struct ocelot_multicast *mc,
214662306a36Sopenharmony_ci			     unsigned long from_mask, unsigned long to_mask)
214762306a36Sopenharmony_ci{
214862306a36Sopenharmony_ci	unsigned char addr[ETH_ALEN];
214962306a36Sopenharmony_ci	struct ocelot_pgid *pgid;
215062306a36Sopenharmony_ci	u16 vid = mc->vid;
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	dev_dbg(ocelot->dev,
215362306a36Sopenharmony_ci		"Migrating multicast %pM vid %d from port mask 0x%lx to 0x%lx\n",
215462306a36Sopenharmony_ci		mc->addr, mc->vid, from_mask, to_mask);
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	/* First clean up the current port mask from hardware, because
215762306a36Sopenharmony_ci	 * we'll be modifying it.
215862306a36Sopenharmony_ci	 */
215962306a36Sopenharmony_ci	ocelot_pgid_free(ocelot, mc->pgid);
216062306a36Sopenharmony_ci	ocelot_encode_ports_to_mdb(addr, mc);
216162306a36Sopenharmony_ci	ocelot_mact_forget(ocelot, addr, vid);
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	mc->ports &= ~from_mask;
216462306a36Sopenharmony_ci	mc->ports |= to_mask;
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	pgid = ocelot_mdb_get_pgid(ocelot, mc);
216762306a36Sopenharmony_ci	if (IS_ERR(pgid)) {
216862306a36Sopenharmony_ci		dev_err(ocelot->dev,
216962306a36Sopenharmony_ci			"Cannot allocate PGID for mdb %pM vid %d\n",
217062306a36Sopenharmony_ci			mc->addr, mc->vid);
217162306a36Sopenharmony_ci		devm_kfree(ocelot->dev, mc);
217262306a36Sopenharmony_ci		return PTR_ERR(pgid);
217362306a36Sopenharmony_ci	}
217462306a36Sopenharmony_ci	mc->pgid = pgid;
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	ocelot_encode_ports_to_mdb(addr, mc);
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	if (mc->entry_type != ENTRYTYPE_MACv4 &&
217962306a36Sopenharmony_ci	    mc->entry_type != ENTRYTYPE_MACv6)
218062306a36Sopenharmony_ci		ocelot_write_rix(ocelot, pgid->ports, ANA_PGID_PGID,
218162306a36Sopenharmony_ci				 pgid->index);
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	return ocelot_mact_learn(ocelot, pgid->index, addr, vid,
218462306a36Sopenharmony_ci				 mc->entry_type);
218562306a36Sopenharmony_ci}
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ciint ocelot_migrate_mdbs(struct ocelot *ocelot, unsigned long from_mask,
218862306a36Sopenharmony_ci			unsigned long to_mask)
218962306a36Sopenharmony_ci{
219062306a36Sopenharmony_ci	struct ocelot_multicast *mc;
219162306a36Sopenharmony_ci	int err;
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	list_for_each_entry(mc, &ocelot->multicast, list) {
219462306a36Sopenharmony_ci		if (!(mc->ports & from_mask))
219562306a36Sopenharmony_ci			continue;
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci		err = ocelot_migrate_mc(ocelot, mc, from_mask, to_mask);
219862306a36Sopenharmony_ci		if (err)
219962306a36Sopenharmony_ci			return err;
220062306a36Sopenharmony_ci	}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	return 0;
220362306a36Sopenharmony_ci}
220462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_migrate_mdbs);
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci/* Documentation for PORTID_VAL says:
220762306a36Sopenharmony_ci *     Logical port number for front port. If port is not a member of a LLAG,
220862306a36Sopenharmony_ci *     then PORTID must be set to the physical port number.
220962306a36Sopenharmony_ci *     If port is a member of a LLAG, then PORTID must be set to the common
221062306a36Sopenharmony_ci *     PORTID_VAL used for all member ports of the LLAG.
221162306a36Sopenharmony_ci *     The value must not exceed the number of physical ports on the device.
221262306a36Sopenharmony_ci *
221362306a36Sopenharmony_ci * This means we have little choice but to migrate FDB entries pointing towards
221462306a36Sopenharmony_ci * a logical port when that changes.
221562306a36Sopenharmony_ci */
221662306a36Sopenharmony_cistatic void ocelot_migrate_lag_fdbs(struct ocelot *ocelot,
221762306a36Sopenharmony_ci				    struct net_device *bond,
221862306a36Sopenharmony_ci				    int lag)
221962306a36Sopenharmony_ci{
222062306a36Sopenharmony_ci	struct ocelot_lag_fdb *fdb;
222162306a36Sopenharmony_ci	int err;
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	lockdep_assert_held(&ocelot->fwd_domain_lock);
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	list_for_each_entry(fdb, &ocelot->lag_fdbs, list) {
222662306a36Sopenharmony_ci		if (fdb->bond != bond)
222762306a36Sopenharmony_ci			continue;
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci		err = ocelot_mact_forget(ocelot, fdb->addr, fdb->vid);
223062306a36Sopenharmony_ci		if (err) {
223162306a36Sopenharmony_ci			dev_err(ocelot->dev,
223262306a36Sopenharmony_ci				"failed to delete LAG %s FDB %pM vid %d: %pe\n",
223362306a36Sopenharmony_ci				bond->name, fdb->addr, fdb->vid, ERR_PTR(err));
223462306a36Sopenharmony_ci		}
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci		err = ocelot_mact_learn(ocelot, lag, fdb->addr, fdb->vid,
223762306a36Sopenharmony_ci					ENTRYTYPE_LOCKED);
223862306a36Sopenharmony_ci		if (err) {
223962306a36Sopenharmony_ci			dev_err(ocelot->dev,
224062306a36Sopenharmony_ci				"failed to migrate LAG %s FDB %pM vid %d: %pe\n",
224162306a36Sopenharmony_ci				bond->name, fdb->addr, fdb->vid, ERR_PTR(err));
224262306a36Sopenharmony_ci		}
224362306a36Sopenharmony_ci	}
224462306a36Sopenharmony_ci}
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ciint ocelot_port_lag_join(struct ocelot *ocelot, int port,
224762306a36Sopenharmony_ci			 struct net_device *bond,
224862306a36Sopenharmony_ci			 struct netdev_lag_upper_info *info,
224962306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
225062306a36Sopenharmony_ci{
225162306a36Sopenharmony_ci	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
225262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
225362306a36Sopenharmony_ci				   "Can only offload LAG using hash TX type");
225462306a36Sopenharmony_ci		return -EOPNOTSUPP;
225562306a36Sopenharmony_ci	}
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	ocelot->ports[port]->bond = bond;
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	ocelot_setup_logical_port_ids(ocelot);
226262306a36Sopenharmony_ci	ocelot_apply_bridge_fwd_mask(ocelot, true);
226362306a36Sopenharmony_ci	ocelot_set_aggr_pgids(ocelot);
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	return 0;
226862306a36Sopenharmony_ci}
226962306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_lag_join);
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_civoid ocelot_port_lag_leave(struct ocelot *ocelot, int port,
227262306a36Sopenharmony_ci			   struct net_device *bond)
227362306a36Sopenharmony_ci{
227462306a36Sopenharmony_ci	int old_lag_id, new_lag_id;
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	old_lag_id = ocelot_bond_get_id(ocelot, bond);
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	ocelot->ports[port]->bond = NULL;
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci	ocelot_setup_logical_port_ids(ocelot);
228362306a36Sopenharmony_ci	ocelot_apply_bridge_fwd_mask(ocelot, false);
228462306a36Sopenharmony_ci	ocelot_set_aggr_pgids(ocelot);
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	new_lag_id = ocelot_bond_get_id(ocelot, bond);
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	if (new_lag_id >= 0 && old_lag_id != new_lag_id)
228962306a36Sopenharmony_ci		ocelot_migrate_lag_fdbs(ocelot, bond, new_lag_id);
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
229262306a36Sopenharmony_ci}
229362306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_lag_leave);
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_civoid ocelot_port_lag_change(struct ocelot *ocelot, int port, bool lag_tx_active)
229662306a36Sopenharmony_ci{
229762306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	ocelot_port->lag_tx_active = lag_tx_active;
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	/* Rebalance the LAGs */
230462306a36Sopenharmony_ci	ocelot_set_aggr_pgids(ocelot);
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
230762306a36Sopenharmony_ci}
230862306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_lag_change);
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ciint ocelot_lag_fdb_add(struct ocelot *ocelot, struct net_device *bond,
231162306a36Sopenharmony_ci		       const unsigned char *addr, u16 vid,
231262306a36Sopenharmony_ci		       const struct net_device *bridge)
231362306a36Sopenharmony_ci{
231462306a36Sopenharmony_ci	struct ocelot_lag_fdb *fdb;
231562306a36Sopenharmony_ci	int lag, err;
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	fdb = kzalloc(sizeof(*fdb), GFP_KERNEL);
231862306a36Sopenharmony_ci	if (!fdb)
231962306a36Sopenharmony_ci		return -ENOMEM;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	if (!vid)
232462306a36Sopenharmony_ci		vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	ether_addr_copy(fdb->addr, addr);
232762306a36Sopenharmony_ci	fdb->vid = vid;
232862306a36Sopenharmony_ci	fdb->bond = bond;
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	lag = ocelot_bond_get_id(ocelot, bond);
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	err = ocelot_mact_learn(ocelot, lag, addr, vid, ENTRYTYPE_LOCKED);
233362306a36Sopenharmony_ci	if (err) {
233462306a36Sopenharmony_ci		mutex_unlock(&ocelot->fwd_domain_lock);
233562306a36Sopenharmony_ci		kfree(fdb);
233662306a36Sopenharmony_ci		return err;
233762306a36Sopenharmony_ci	}
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	list_add_tail(&fdb->list, &ocelot->lag_fdbs);
234062306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_ci	return 0;
234362306a36Sopenharmony_ci}
234462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_lag_fdb_add);
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ciint ocelot_lag_fdb_del(struct ocelot *ocelot, struct net_device *bond,
234762306a36Sopenharmony_ci		       const unsigned char *addr, u16 vid,
234862306a36Sopenharmony_ci		       const struct net_device *bridge)
234962306a36Sopenharmony_ci{
235062306a36Sopenharmony_ci	struct ocelot_lag_fdb *fdb, *tmp;
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	mutex_lock(&ocelot->fwd_domain_lock);
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	if (!vid)
235562306a36Sopenharmony_ci		vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	list_for_each_entry_safe(fdb, tmp, &ocelot->lag_fdbs, list) {
235862306a36Sopenharmony_ci		if (!ether_addr_equal(fdb->addr, addr) || fdb->vid != vid ||
235962306a36Sopenharmony_ci		    fdb->bond != bond)
236062306a36Sopenharmony_ci			continue;
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci		ocelot_mact_forget(ocelot, addr, vid);
236362306a36Sopenharmony_ci		list_del(&fdb->list);
236462306a36Sopenharmony_ci		mutex_unlock(&ocelot->fwd_domain_lock);
236562306a36Sopenharmony_ci		kfree(fdb);
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci		return 0;
236862306a36Sopenharmony_ci	}
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	mutex_unlock(&ocelot->fwd_domain_lock);
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	return -ENOENT;
237362306a36Sopenharmony_ci}
237462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_lag_fdb_del);
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci/* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu.
237762306a36Sopenharmony_ci * The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG.
237862306a36Sopenharmony_ci * In the special case that it's the NPI port that we're configuring, the
237962306a36Sopenharmony_ci * length of the tag and optional prefix needs to be accounted for privately,
238062306a36Sopenharmony_ci * in order to be able to sustain communication at the requested @sdu.
238162306a36Sopenharmony_ci */
238262306a36Sopenharmony_civoid ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu)
238362306a36Sopenharmony_ci{
238462306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
238562306a36Sopenharmony_ci	int maxlen = sdu + ETH_HLEN + ETH_FCS_LEN;
238662306a36Sopenharmony_ci	int pause_start, pause_stop;
238762306a36Sopenharmony_ci	int atop, atop_tot;
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci	if (port == ocelot->npi) {
239062306a36Sopenharmony_ci		maxlen += OCELOT_TAG_LEN;
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci		if (ocelot->npi_inj_prefix == OCELOT_TAG_PREFIX_SHORT)
239362306a36Sopenharmony_ci			maxlen += OCELOT_SHORT_PREFIX_LEN;
239462306a36Sopenharmony_ci		else if (ocelot->npi_inj_prefix == OCELOT_TAG_PREFIX_LONG)
239562306a36Sopenharmony_ci			maxlen += OCELOT_LONG_PREFIX_LEN;
239662306a36Sopenharmony_ci	}
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, maxlen, DEV_MAC_MAXLEN_CFG);
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	/* Set Pause watermark hysteresis */
240162306a36Sopenharmony_ci	pause_start = 6 * maxlen / OCELOT_BUFFER_CELL_SZ;
240262306a36Sopenharmony_ci	pause_stop = 4 * maxlen / OCELOT_BUFFER_CELL_SZ;
240362306a36Sopenharmony_ci	ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_START,
240462306a36Sopenharmony_ci			    pause_start);
240562306a36Sopenharmony_ci	ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_STOP,
240662306a36Sopenharmony_ci			    pause_stop);
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	/* Tail dropping watermarks */
240962306a36Sopenharmony_ci	atop_tot = (ocelot->packet_buffer_size - 9 * maxlen) /
241062306a36Sopenharmony_ci		   OCELOT_BUFFER_CELL_SZ;
241162306a36Sopenharmony_ci	atop = (9 * maxlen) / OCELOT_BUFFER_CELL_SZ;
241262306a36Sopenharmony_ci	ocelot_write_rix(ocelot, ocelot->ops->wm_enc(atop), SYS_ATOP, port);
241362306a36Sopenharmony_ci	ocelot_write(ocelot, ocelot->ops->wm_enc(atop_tot), SYS_ATOP_TOT_CFG);
241462306a36Sopenharmony_ci}
241562306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_set_maxlen);
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ciint ocelot_get_max_mtu(struct ocelot *ocelot, int port)
241862306a36Sopenharmony_ci{
241962306a36Sopenharmony_ci	int max_mtu = 65535 - ETH_HLEN - ETH_FCS_LEN;
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	if (port == ocelot->npi) {
242262306a36Sopenharmony_ci		max_mtu -= OCELOT_TAG_LEN;
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci		if (ocelot->npi_inj_prefix == OCELOT_TAG_PREFIX_SHORT)
242562306a36Sopenharmony_ci			max_mtu -= OCELOT_SHORT_PREFIX_LEN;
242662306a36Sopenharmony_ci		else if (ocelot->npi_inj_prefix == OCELOT_TAG_PREFIX_LONG)
242762306a36Sopenharmony_ci			max_mtu -= OCELOT_LONG_PREFIX_LEN;
242862306a36Sopenharmony_ci	}
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	return max_mtu;
243162306a36Sopenharmony_ci}
243262306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_get_max_mtu);
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_cistatic void ocelot_port_set_learning(struct ocelot *ocelot, int port,
243562306a36Sopenharmony_ci				     bool enabled)
243662306a36Sopenharmony_ci{
243762306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
243862306a36Sopenharmony_ci	u32 val = 0;
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci	if (enabled)
244162306a36Sopenharmony_ci		val = ANA_PORT_PORT_CFG_LEARN_ENA;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, val, ANA_PORT_PORT_CFG_LEARN_ENA,
244462306a36Sopenharmony_ci		       ANA_PORT_PORT_CFG, port);
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	ocelot_port->learn_ena = enabled;
244762306a36Sopenharmony_ci}
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_cistatic void ocelot_port_set_ucast_flood(struct ocelot *ocelot, int port,
245062306a36Sopenharmony_ci					bool enabled)
245162306a36Sopenharmony_ci{
245262306a36Sopenharmony_ci	u32 val = 0;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	if (enabled)
245562306a36Sopenharmony_ci		val = BIT(port);
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci	ocelot_rmw_rix(ocelot, val, BIT(port), ANA_PGID_PGID, PGID_UC);
245862306a36Sopenharmony_ci}
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_cistatic void ocelot_port_set_mcast_flood(struct ocelot *ocelot, int port,
246162306a36Sopenharmony_ci					bool enabled)
246262306a36Sopenharmony_ci{
246362306a36Sopenharmony_ci	u32 val = 0;
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	if (enabled)
246662306a36Sopenharmony_ci		val = BIT(port);
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	ocelot_rmw_rix(ocelot, val, BIT(port), ANA_PGID_PGID, PGID_MC);
246962306a36Sopenharmony_ci	ocelot_rmw_rix(ocelot, val, BIT(port), ANA_PGID_PGID, PGID_MCIPV4);
247062306a36Sopenharmony_ci	ocelot_rmw_rix(ocelot, val, BIT(port), ANA_PGID_PGID, PGID_MCIPV6);
247162306a36Sopenharmony_ci}
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_cistatic void ocelot_port_set_bcast_flood(struct ocelot *ocelot, int port,
247462306a36Sopenharmony_ci					bool enabled)
247562306a36Sopenharmony_ci{
247662306a36Sopenharmony_ci	u32 val = 0;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	if (enabled)
247962306a36Sopenharmony_ci		val = BIT(port);
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci	ocelot_rmw_rix(ocelot, val, BIT(port), ANA_PGID_PGID, PGID_BC);
248262306a36Sopenharmony_ci}
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ciint ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port,
248562306a36Sopenharmony_ci				 struct switchdev_brport_flags flags)
248662306a36Sopenharmony_ci{
248762306a36Sopenharmony_ci	if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD |
248862306a36Sopenharmony_ci			   BR_BCAST_FLOOD))
248962306a36Sopenharmony_ci		return -EINVAL;
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	return 0;
249262306a36Sopenharmony_ci}
249362306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_pre_bridge_flags);
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_civoid ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
249662306a36Sopenharmony_ci			      struct switchdev_brport_flags flags)
249762306a36Sopenharmony_ci{
249862306a36Sopenharmony_ci	if (flags.mask & BR_LEARNING)
249962306a36Sopenharmony_ci		ocelot_port_set_learning(ocelot, port,
250062306a36Sopenharmony_ci					 !!(flags.val & BR_LEARNING));
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci	if (flags.mask & BR_FLOOD)
250362306a36Sopenharmony_ci		ocelot_port_set_ucast_flood(ocelot, port,
250462306a36Sopenharmony_ci					    !!(flags.val & BR_FLOOD));
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	if (flags.mask & BR_MCAST_FLOOD)
250762306a36Sopenharmony_ci		ocelot_port_set_mcast_flood(ocelot, port,
250862306a36Sopenharmony_ci					    !!(flags.val & BR_MCAST_FLOOD));
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	if (flags.mask & BR_BCAST_FLOOD)
251162306a36Sopenharmony_ci		ocelot_port_set_bcast_flood(ocelot, port,
251262306a36Sopenharmony_ci					    !!(flags.val & BR_BCAST_FLOOD));
251362306a36Sopenharmony_ci}
251462306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_port_bridge_flags);
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ciint ocelot_port_get_default_prio(struct ocelot *ocelot, int port)
251762306a36Sopenharmony_ci{
251862306a36Sopenharmony_ci	int val = ocelot_read_gix(ocelot, ANA_PORT_QOS_CFG, port);
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	return ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL_X(val);
252162306a36Sopenharmony_ci}
252262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_get_default_prio);
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ciint ocelot_port_set_default_prio(struct ocelot *ocelot, int port, u8 prio)
252562306a36Sopenharmony_ci{
252662306a36Sopenharmony_ci	if (prio >= OCELOT_NUM_TC)
252762306a36Sopenharmony_ci		return -ERANGE;
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot,
253062306a36Sopenharmony_ci		       ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL(prio),
253162306a36Sopenharmony_ci		       ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL_M,
253262306a36Sopenharmony_ci		       ANA_PORT_QOS_CFG,
253362306a36Sopenharmony_ci		       port);
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	return 0;
253662306a36Sopenharmony_ci}
253762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_set_default_prio);
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ciint ocelot_port_get_dscp_prio(struct ocelot *ocelot, int port, u8 dscp)
254062306a36Sopenharmony_ci{
254162306a36Sopenharmony_ci	int qos_cfg = ocelot_read_gix(ocelot, ANA_PORT_QOS_CFG, port);
254262306a36Sopenharmony_ci	int dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, dscp);
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	/* Return error if DSCP prioritization isn't enabled */
254562306a36Sopenharmony_ci	if (!(qos_cfg & ANA_PORT_QOS_CFG_QOS_DSCP_ENA))
254662306a36Sopenharmony_ci		return -EOPNOTSUPP;
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci	if (qos_cfg & ANA_PORT_QOS_CFG_DSCP_TRANSLATE_ENA) {
254962306a36Sopenharmony_ci		dscp = ANA_DSCP_CFG_DSCP_TRANSLATE_VAL_X(dscp_cfg);
255062306a36Sopenharmony_ci		/* Re-read ANA_DSCP_CFG for the translated DSCP */
255162306a36Sopenharmony_ci		dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, dscp);
255262306a36Sopenharmony_ci	}
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	/* If the DSCP value is not trusted, the QoS classification falls back
255562306a36Sopenharmony_ci	 * to VLAN PCP or port-based default.
255662306a36Sopenharmony_ci	 */
255762306a36Sopenharmony_ci	if (!(dscp_cfg & ANA_DSCP_CFG_DSCP_TRUST_ENA))
255862306a36Sopenharmony_ci		return -EOPNOTSUPP;
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_ci	return ANA_DSCP_CFG_QOS_DSCP_VAL_X(dscp_cfg);
256162306a36Sopenharmony_ci}
256262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_get_dscp_prio);
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ciint ocelot_port_add_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio)
256562306a36Sopenharmony_ci{
256662306a36Sopenharmony_ci	int mask, val;
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci	if (prio >= OCELOT_NUM_TC)
256962306a36Sopenharmony_ci		return -ERANGE;
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	/* There is at least one app table priority (this one), so we need to
257262306a36Sopenharmony_ci	 * make sure DSCP prioritization is enabled on the port.
257362306a36Sopenharmony_ci	 * Also make sure DSCP translation is disabled
257462306a36Sopenharmony_ci	 * (dcbnl doesn't support it).
257562306a36Sopenharmony_ci	 */
257662306a36Sopenharmony_ci	mask = ANA_PORT_QOS_CFG_QOS_DSCP_ENA |
257762306a36Sopenharmony_ci	       ANA_PORT_QOS_CFG_DSCP_TRANSLATE_ENA;
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, ANA_PORT_QOS_CFG_QOS_DSCP_ENA, mask,
258062306a36Sopenharmony_ci		       ANA_PORT_QOS_CFG, port);
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	/* Trust this DSCP value and map it to the given QoS class */
258362306a36Sopenharmony_ci	val = ANA_DSCP_CFG_DSCP_TRUST_ENA | ANA_DSCP_CFG_QOS_DSCP_VAL(prio);
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	ocelot_write_rix(ocelot, val, ANA_DSCP_CFG, dscp);
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci	return 0;
258862306a36Sopenharmony_ci}
258962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_add_dscp_prio);
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ciint ocelot_port_del_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio)
259262306a36Sopenharmony_ci{
259362306a36Sopenharmony_ci	int dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, dscp);
259462306a36Sopenharmony_ci	int mask, i;
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	/* During a "dcb app replace" command, the new app table entry will be
259762306a36Sopenharmony_ci	 * added first, then the old one will be deleted. But the hardware only
259862306a36Sopenharmony_ci	 * supports one QoS class per DSCP value (duh), so if we blindly delete
259962306a36Sopenharmony_ci	 * the app table entry for this DSCP value, we end up deleting the
260062306a36Sopenharmony_ci	 * entry with the new priority. Avoid that by checking whether user
260162306a36Sopenharmony_ci	 * space wants to delete the priority which is currently configured, or
260262306a36Sopenharmony_ci	 * something else which is no longer current.
260362306a36Sopenharmony_ci	 */
260462306a36Sopenharmony_ci	if (ANA_DSCP_CFG_QOS_DSCP_VAL_X(dscp_cfg) != prio)
260562306a36Sopenharmony_ci		return 0;
260662306a36Sopenharmony_ci
260762306a36Sopenharmony_ci	/* Untrust this DSCP value */
260862306a36Sopenharmony_ci	ocelot_write_rix(ocelot, 0, ANA_DSCP_CFG, dscp);
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci	for (i = 0; i < 64; i++) {
261162306a36Sopenharmony_ci		int dscp_cfg = ocelot_read_rix(ocelot, ANA_DSCP_CFG, i);
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci		/* There are still app table entries on the port, so we need to
261462306a36Sopenharmony_ci		 * keep DSCP enabled, nothing to do.
261562306a36Sopenharmony_ci		 */
261662306a36Sopenharmony_ci		if (dscp_cfg & ANA_DSCP_CFG_DSCP_TRUST_ENA)
261762306a36Sopenharmony_ci			return 0;
261862306a36Sopenharmony_ci	}
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	/* Disable DSCP QoS classification if there isn't any trusted
262162306a36Sopenharmony_ci	 * DSCP value left.
262262306a36Sopenharmony_ci	 */
262362306a36Sopenharmony_ci	mask = ANA_PORT_QOS_CFG_QOS_DSCP_ENA |
262462306a36Sopenharmony_ci	       ANA_PORT_QOS_CFG_DSCP_TRANSLATE_ENA;
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, 0, mask, ANA_PORT_QOS_CFG, port);
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	return 0;
262962306a36Sopenharmony_ci}
263062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_del_dscp_prio);
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_cistruct ocelot_mirror *ocelot_mirror_get(struct ocelot *ocelot, int to,
263362306a36Sopenharmony_ci					struct netlink_ext_ack *extack)
263462306a36Sopenharmony_ci{
263562306a36Sopenharmony_ci	struct ocelot_mirror *m = ocelot->mirror;
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci	if (m) {
263862306a36Sopenharmony_ci		if (m->to != to) {
263962306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
264062306a36Sopenharmony_ci					   "Mirroring already configured towards different egress port");
264162306a36Sopenharmony_ci			return ERR_PTR(-EBUSY);
264262306a36Sopenharmony_ci		}
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci		refcount_inc(&m->refcount);
264562306a36Sopenharmony_ci		return m;
264662306a36Sopenharmony_ci	}
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	m = kzalloc(sizeof(*m), GFP_KERNEL);
264962306a36Sopenharmony_ci	if (!m)
265062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci	m->to = to;
265362306a36Sopenharmony_ci	refcount_set(&m->refcount, 1);
265462306a36Sopenharmony_ci	ocelot->mirror = m;
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci	/* Program the mirror port to hardware */
265762306a36Sopenharmony_ci	ocelot_write(ocelot, BIT(to), ANA_MIRRORPORTS);
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_ci	return m;
266062306a36Sopenharmony_ci}
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_civoid ocelot_mirror_put(struct ocelot *ocelot)
266362306a36Sopenharmony_ci{
266462306a36Sopenharmony_ci	struct ocelot_mirror *m = ocelot->mirror;
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_ci	if (!refcount_dec_and_test(&m->refcount))
266762306a36Sopenharmony_ci		return;
266862306a36Sopenharmony_ci
266962306a36Sopenharmony_ci	ocelot_write(ocelot, 0, ANA_MIRRORPORTS);
267062306a36Sopenharmony_ci	ocelot->mirror = NULL;
267162306a36Sopenharmony_ci	kfree(m);
267262306a36Sopenharmony_ci}
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ciint ocelot_port_mirror_add(struct ocelot *ocelot, int from, int to,
267562306a36Sopenharmony_ci			   bool ingress, struct netlink_ext_ack *extack)
267662306a36Sopenharmony_ci{
267762306a36Sopenharmony_ci	struct ocelot_mirror *m = ocelot_mirror_get(ocelot, to, extack);
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	if (IS_ERR(m))
268062306a36Sopenharmony_ci		return PTR_ERR(m);
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	if (ingress) {
268362306a36Sopenharmony_ci		ocelot_rmw_gix(ocelot, ANA_PORT_PORT_CFG_SRC_MIRROR_ENA,
268462306a36Sopenharmony_ci			       ANA_PORT_PORT_CFG_SRC_MIRROR_ENA,
268562306a36Sopenharmony_ci			       ANA_PORT_PORT_CFG, from);
268662306a36Sopenharmony_ci	} else {
268762306a36Sopenharmony_ci		ocelot_rmw(ocelot, BIT(from), BIT(from),
268862306a36Sopenharmony_ci			   ANA_EMIRRORPORTS);
268962306a36Sopenharmony_ci	}
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci	return 0;
269262306a36Sopenharmony_ci}
269362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_mirror_add);
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_civoid ocelot_port_mirror_del(struct ocelot *ocelot, int from, bool ingress)
269662306a36Sopenharmony_ci{
269762306a36Sopenharmony_ci	if (ingress) {
269862306a36Sopenharmony_ci		ocelot_rmw_gix(ocelot, 0, ANA_PORT_PORT_CFG_SRC_MIRROR_ENA,
269962306a36Sopenharmony_ci			       ANA_PORT_PORT_CFG, from);
270062306a36Sopenharmony_ci	} else {
270162306a36Sopenharmony_ci		ocelot_rmw(ocelot, 0, BIT(from), ANA_EMIRRORPORTS);
270262306a36Sopenharmony_ci	}
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	ocelot_mirror_put(ocelot);
270562306a36Sopenharmony_ci}
270662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_mirror_del);
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_cistatic void ocelot_port_reset_mqprio(struct ocelot *ocelot, int port)
270962306a36Sopenharmony_ci{
271062306a36Sopenharmony_ci	struct net_device *dev = ocelot->ops->port_to_netdev(ocelot, port);
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	netdev_reset_tc(dev);
271362306a36Sopenharmony_ci	ocelot_port_change_fp(ocelot, port, 0);
271462306a36Sopenharmony_ci}
271562306a36Sopenharmony_ci
271662306a36Sopenharmony_ciint ocelot_port_mqprio(struct ocelot *ocelot, int port,
271762306a36Sopenharmony_ci		       struct tc_mqprio_qopt_offload *mqprio)
271862306a36Sopenharmony_ci{
271962306a36Sopenharmony_ci	struct net_device *dev = ocelot->ops->port_to_netdev(ocelot, port);
272062306a36Sopenharmony_ci	struct netlink_ext_ack *extack = mqprio->extack;
272162306a36Sopenharmony_ci	struct tc_mqprio_qopt *qopt = &mqprio->qopt;
272262306a36Sopenharmony_ci	int num_tc = qopt->num_tc;
272362306a36Sopenharmony_ci	int tc, err;
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci	if (!num_tc) {
272662306a36Sopenharmony_ci		ocelot_port_reset_mqprio(ocelot, port);
272762306a36Sopenharmony_ci		return 0;
272862306a36Sopenharmony_ci	}
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	err = netdev_set_num_tc(dev, num_tc);
273162306a36Sopenharmony_ci	if (err)
273262306a36Sopenharmony_ci		return err;
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci	for (tc = 0; tc < num_tc; tc++) {
273562306a36Sopenharmony_ci		if (qopt->count[tc] != 1) {
273662306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
273762306a36Sopenharmony_ci					   "Only one TXQ per TC supported");
273862306a36Sopenharmony_ci			return -EINVAL;
273962306a36Sopenharmony_ci		}
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci		err = netdev_set_tc_queue(dev, tc, 1, qopt->offset[tc]);
274262306a36Sopenharmony_ci		if (err)
274362306a36Sopenharmony_ci			goto err_reset_tc;
274462306a36Sopenharmony_ci	}
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	err = netif_set_real_num_tx_queues(dev, num_tc);
274762306a36Sopenharmony_ci	if (err)
274862306a36Sopenharmony_ci		goto err_reset_tc;
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci	ocelot_port_change_fp(ocelot, port, mqprio->preemptible_tcs);
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	return 0;
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_cierr_reset_tc:
275562306a36Sopenharmony_ci	ocelot_port_reset_mqprio(ocelot, port);
275662306a36Sopenharmony_ci	return err;
275762306a36Sopenharmony_ci}
275862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ocelot_port_mqprio);
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_civoid ocelot_init_port(struct ocelot *ocelot, int port)
276162306a36Sopenharmony_ci{
276262306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci	skb_queue_head_init(&ocelot_port->tx_skbs);
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_ci	/* Basic L2 initialization */
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_ci	/* Set MAC IFG Gaps
276962306a36Sopenharmony_ci	 * FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 0
277062306a36Sopenharmony_ci	 * !FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 5
277162306a36Sopenharmony_ci	 */
277262306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, DEV_MAC_IFG_CFG_TX_IFG(5),
277362306a36Sopenharmony_ci			   DEV_MAC_IFG_CFG);
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci	/* Load seed (0) and set MAC HDX late collision  */
277662306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, DEV_MAC_HDX_CFG_LATE_COL_POS(67) |
277762306a36Sopenharmony_ci			   DEV_MAC_HDX_CFG_SEED_LOAD,
277862306a36Sopenharmony_ci			   DEV_MAC_HDX_CFG);
277962306a36Sopenharmony_ci	mdelay(1);
278062306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, DEV_MAC_HDX_CFG_LATE_COL_POS(67),
278162306a36Sopenharmony_ci			   DEV_MAC_HDX_CFG);
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_ci	/* Set Max Length and maximum tags allowed */
278462306a36Sopenharmony_ci	ocelot_port_set_maxlen(ocelot, port, ETH_DATA_LEN);
278562306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, DEV_MAC_TAGS_CFG_TAG_ID(ETH_P_8021AD) |
278662306a36Sopenharmony_ci			   DEV_MAC_TAGS_CFG_VLAN_AWR_ENA |
278762306a36Sopenharmony_ci			   DEV_MAC_TAGS_CFG_VLAN_DBL_AWR_ENA |
278862306a36Sopenharmony_ci			   DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA,
278962306a36Sopenharmony_ci			   DEV_MAC_TAGS_CFG);
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci	/* Set SMAC of Pause frame (00:00:00:00:00:00) */
279262306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_HIGH_CFG);
279362306a36Sopenharmony_ci	ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_LOW_CFG);
279462306a36Sopenharmony_ci
279562306a36Sopenharmony_ci	/* Enable transmission of pause frames */
279662306a36Sopenharmony_ci	ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 1);
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	/* Drop frames with multicast source address */
279962306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA,
280062306a36Sopenharmony_ci		       ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA,
280162306a36Sopenharmony_ci		       ANA_PORT_DROP_CFG, port);
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	/* Set default VLAN and tag type to 8021Q. */
280462306a36Sopenharmony_ci	ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021Q),
280562306a36Sopenharmony_ci		       REW_PORT_VLAN_CFG_PORT_TPID_M,
280662306a36Sopenharmony_ci		       REW_PORT_VLAN_CFG, port);
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	/* Disable source address learning for standalone mode */
280962306a36Sopenharmony_ci	ocelot_port_set_learning(ocelot, port, false);
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci	/* Set the port's initial logical port ID value, enable receiving
281262306a36Sopenharmony_ci	 * frames on it, and configure the MAC address learning type to
281362306a36Sopenharmony_ci	 * automatic.
281462306a36Sopenharmony_ci	 */
281562306a36Sopenharmony_ci	ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO |
281662306a36Sopenharmony_ci			 ANA_PORT_PORT_CFG_RECV_ENA |
281762306a36Sopenharmony_ci			 ANA_PORT_PORT_CFG_PORTID_VAL(port),
281862306a36Sopenharmony_ci			 ANA_PORT_PORT_CFG, port);
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	/* Enable vcap lookups */
282162306a36Sopenharmony_ci	ocelot_vcap_enable(ocelot, port);
282262306a36Sopenharmony_ci}
282362306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_init_port);
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci/* Configure and enable the CPU port module, which is a set of queues
282662306a36Sopenharmony_ci * accessible through register MMIO, frame DMA or Ethernet (in case
282762306a36Sopenharmony_ci * NPI mode is used).
282862306a36Sopenharmony_ci */
282962306a36Sopenharmony_cistatic void ocelot_cpu_port_init(struct ocelot *ocelot)
283062306a36Sopenharmony_ci{
283162306a36Sopenharmony_ci	int cpu = ocelot->num_phys_ports;
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_ci	/* The unicast destination PGID for the CPU port module is unused */
283462306a36Sopenharmony_ci	ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, cpu);
283562306a36Sopenharmony_ci	/* Instead set up a multicast destination PGID for traffic copied to
283662306a36Sopenharmony_ci	 * the CPU. Whitelisted MAC addresses like the port netdevice MAC
283762306a36Sopenharmony_ci	 * addresses will be copied to the CPU via this PGID.
283862306a36Sopenharmony_ci	 */
283962306a36Sopenharmony_ci	ocelot_write_rix(ocelot, BIT(cpu), ANA_PGID_PGID, PGID_CPU);
284062306a36Sopenharmony_ci	ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_RECV_ENA |
284162306a36Sopenharmony_ci			 ANA_PORT_PORT_CFG_PORTID_VAL(cpu),
284262306a36Sopenharmony_ci			 ANA_PORT_PORT_CFG, cpu);
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci	/* Enable CPU port module */
284562306a36Sopenharmony_ci	ocelot_fields_write(ocelot, cpu, QSYS_SWITCH_PORT_MODE_PORT_ENA, 1);
284662306a36Sopenharmony_ci	/* CPU port Injection/Extraction configuration */
284762306a36Sopenharmony_ci	ocelot_fields_write(ocelot, cpu, SYS_PORT_MODE_INCL_XTR_HDR,
284862306a36Sopenharmony_ci			    OCELOT_TAG_PREFIX_NONE);
284962306a36Sopenharmony_ci	ocelot_fields_write(ocelot, cpu, SYS_PORT_MODE_INCL_INJ_HDR,
285062306a36Sopenharmony_ci			    OCELOT_TAG_PREFIX_NONE);
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_ci	/* Configure the CPU port to be VLAN aware */
285362306a36Sopenharmony_ci	ocelot_write_gix(ocelot,
285462306a36Sopenharmony_ci			 ANA_PORT_VLAN_CFG_VLAN_VID(OCELOT_STANDALONE_PVID) |
285562306a36Sopenharmony_ci			 ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
285662306a36Sopenharmony_ci			 ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1),
285762306a36Sopenharmony_ci			 ANA_PORT_VLAN_CFG, cpu);
285862306a36Sopenharmony_ci}
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_cistatic void ocelot_detect_features(struct ocelot *ocelot)
286162306a36Sopenharmony_ci{
286262306a36Sopenharmony_ci	int mmgt, eq_ctrl;
286362306a36Sopenharmony_ci
286462306a36Sopenharmony_ci	/* For Ocelot, Felix, Seville, Serval etc, SYS:MMGT:MMGT:FREECNT holds
286562306a36Sopenharmony_ci	 * the number of 240-byte free memory words (aka 4-cell chunks) and not
286662306a36Sopenharmony_ci	 * 192 bytes as the documentation incorrectly says.
286762306a36Sopenharmony_ci	 */
286862306a36Sopenharmony_ci	mmgt = ocelot_read(ocelot, SYS_MMGT);
286962306a36Sopenharmony_ci	ocelot->packet_buffer_size = 240 * SYS_MMGT_FREECNT(mmgt);
287062306a36Sopenharmony_ci
287162306a36Sopenharmony_ci	eq_ctrl = ocelot_read(ocelot, QSYS_EQ_CTRL);
287262306a36Sopenharmony_ci	ocelot->num_frame_refs = QSYS_MMGT_EQ_CTRL_FP_FREE_CNT(eq_ctrl);
287362306a36Sopenharmony_ci}
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_cistatic int ocelot_mem_init_status(struct ocelot *ocelot)
287662306a36Sopenharmony_ci{
287762306a36Sopenharmony_ci	unsigned int val;
287862306a36Sopenharmony_ci	int err;
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_ci	err = regmap_field_read(ocelot->regfields[SYS_RESET_CFG_MEM_INIT],
288162306a36Sopenharmony_ci				&val);
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci	return err ?: val;
288462306a36Sopenharmony_ci}
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ciint ocelot_reset(struct ocelot *ocelot)
288762306a36Sopenharmony_ci{
288862306a36Sopenharmony_ci	int err;
288962306a36Sopenharmony_ci	u32 val;
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_ci	err = regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], 1);
289262306a36Sopenharmony_ci	if (err)
289362306a36Sopenharmony_ci		return err;
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_ci	err = regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1);
289662306a36Sopenharmony_ci	if (err)
289762306a36Sopenharmony_ci		return err;
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci	/* MEM_INIT is a self-clearing bit. Wait for it to be cleared (should be
290062306a36Sopenharmony_ci	 * 100us) before enabling the switch core.
290162306a36Sopenharmony_ci	 */
290262306a36Sopenharmony_ci	err = readx_poll_timeout(ocelot_mem_init_status, ocelot, val, !val,
290362306a36Sopenharmony_ci				 MEM_INIT_SLEEP_US, MEM_INIT_TIMEOUT_US);
290462306a36Sopenharmony_ci	if (err)
290562306a36Sopenharmony_ci		return err;
290662306a36Sopenharmony_ci
290762306a36Sopenharmony_ci	err = regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1);
290862306a36Sopenharmony_ci	if (err)
290962306a36Sopenharmony_ci		return err;
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci	return regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1);
291262306a36Sopenharmony_ci}
291362306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_reset);
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ciint ocelot_init(struct ocelot *ocelot)
291662306a36Sopenharmony_ci{
291762306a36Sopenharmony_ci	int i, ret;
291862306a36Sopenharmony_ci	u32 port;
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	if (ocelot->ops->reset) {
292162306a36Sopenharmony_ci		ret = ocelot->ops->reset(ocelot);
292262306a36Sopenharmony_ci		if (ret) {
292362306a36Sopenharmony_ci			dev_err(ocelot->dev, "Switch reset failed\n");
292462306a36Sopenharmony_ci			return ret;
292562306a36Sopenharmony_ci		}
292662306a36Sopenharmony_ci	}
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci	mutex_init(&ocelot->mact_lock);
292962306a36Sopenharmony_ci	mutex_init(&ocelot->fwd_domain_lock);
293062306a36Sopenharmony_ci	spin_lock_init(&ocelot->ptp_clock_lock);
293162306a36Sopenharmony_ci	spin_lock_init(&ocelot->ts_id_lock);
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	ocelot->owq = alloc_ordered_workqueue("ocelot-owq", 0);
293462306a36Sopenharmony_ci	if (!ocelot->owq)
293562306a36Sopenharmony_ci		return -ENOMEM;
293662306a36Sopenharmony_ci
293762306a36Sopenharmony_ci	ret = ocelot_stats_init(ocelot);
293862306a36Sopenharmony_ci	if (ret)
293962306a36Sopenharmony_ci		goto err_stats_init;
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ci	INIT_LIST_HEAD(&ocelot->multicast);
294262306a36Sopenharmony_ci	INIT_LIST_HEAD(&ocelot->pgids);
294362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ocelot->vlans);
294462306a36Sopenharmony_ci	INIT_LIST_HEAD(&ocelot->lag_fdbs);
294562306a36Sopenharmony_ci	ocelot_detect_features(ocelot);
294662306a36Sopenharmony_ci	ocelot_mact_init(ocelot);
294762306a36Sopenharmony_ci	ocelot_vlan_init(ocelot);
294862306a36Sopenharmony_ci	ocelot_vcap_init(ocelot);
294962306a36Sopenharmony_ci	ocelot_cpu_port_init(ocelot);
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	if (ocelot->ops->psfp_init)
295262306a36Sopenharmony_ci		ocelot->ops->psfp_init(ocelot);
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	if (ocelot->mm_supported) {
295562306a36Sopenharmony_ci		ret = ocelot_mm_init(ocelot);
295662306a36Sopenharmony_ci		if (ret)
295762306a36Sopenharmony_ci			goto err_mm_init;
295862306a36Sopenharmony_ci	}
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
296162306a36Sopenharmony_ci		/* Clear all counters (5 groups) */
296262306a36Sopenharmony_ci		ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port) |
296362306a36Sopenharmony_ci				     SYS_STAT_CFG_STAT_CLEAR_SHOT(0x7f),
296462306a36Sopenharmony_ci			     SYS_STAT_CFG);
296562306a36Sopenharmony_ci	}
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	/* Only use S-Tag */
296862306a36Sopenharmony_ci	ocelot_write(ocelot, ETH_P_8021AD, SYS_VLAN_ETYPE_CFG);
296962306a36Sopenharmony_ci
297062306a36Sopenharmony_ci	/* Aggregation mode */
297162306a36Sopenharmony_ci	ocelot_write(ocelot, ANA_AGGR_CFG_AC_SMAC_ENA |
297262306a36Sopenharmony_ci			     ANA_AGGR_CFG_AC_DMAC_ENA |
297362306a36Sopenharmony_ci			     ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA |
297462306a36Sopenharmony_ci			     ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA |
297562306a36Sopenharmony_ci			     ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA |
297662306a36Sopenharmony_ci			     ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA,
297762306a36Sopenharmony_ci			     ANA_AGGR_CFG);
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	/* Set MAC age time to default value. The entry is aged after
298062306a36Sopenharmony_ci	 * 2*AGE_PERIOD
298162306a36Sopenharmony_ci	 */
298262306a36Sopenharmony_ci	ocelot_write(ocelot,
298362306a36Sopenharmony_ci		     ANA_AUTOAGE_AGE_PERIOD(BR_DEFAULT_AGEING_TIME / 2 / HZ),
298462306a36Sopenharmony_ci		     ANA_AUTOAGE);
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci	/* Disable learning for frames discarded by VLAN ingress filtering */
298762306a36Sopenharmony_ci	regmap_field_write(ocelot->regfields[ANA_ADVLEARN_VLAN_CHK], 1);
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci	/* Setup frame ageing - fixed value "2 sec" - in 6.5 us units */
299062306a36Sopenharmony_ci	ocelot_write(ocelot, SYS_FRM_AGING_AGE_TX_ENA |
299162306a36Sopenharmony_ci		     SYS_FRM_AGING_MAX_AGE(307692), SYS_FRM_AGING);
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_ci	/* Setup flooding PGIDs */
299462306a36Sopenharmony_ci	for (i = 0; i < ocelot->num_flooding_pgids; i++)
299562306a36Sopenharmony_ci		ocelot_write_rix(ocelot, ANA_FLOODING_FLD_MULTICAST(PGID_MC) |
299662306a36Sopenharmony_ci				 ANA_FLOODING_FLD_BROADCAST(PGID_BC) |
299762306a36Sopenharmony_ci				 ANA_FLOODING_FLD_UNICAST(PGID_UC),
299862306a36Sopenharmony_ci				 ANA_FLOODING, i);
299962306a36Sopenharmony_ci	ocelot_write(ocelot, ANA_FLOODING_IPMC_FLD_MC6_DATA(PGID_MCIPV6) |
300062306a36Sopenharmony_ci		     ANA_FLOODING_IPMC_FLD_MC6_CTRL(PGID_MC) |
300162306a36Sopenharmony_ci		     ANA_FLOODING_IPMC_FLD_MC4_DATA(PGID_MCIPV4) |
300262306a36Sopenharmony_ci		     ANA_FLOODING_IPMC_FLD_MC4_CTRL(PGID_MC),
300362306a36Sopenharmony_ci		     ANA_FLOODING_IPMC);
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
300662306a36Sopenharmony_ci		/* Transmit the frame to the local port. */
300762306a36Sopenharmony_ci		ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, port);
300862306a36Sopenharmony_ci		/* Do not forward BPDU frames to the front ports. */
300962306a36Sopenharmony_ci		ocelot_write_gix(ocelot,
301062306a36Sopenharmony_ci				 ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff),
301162306a36Sopenharmony_ci				 ANA_PORT_CPU_FWD_BPDU_CFG,
301262306a36Sopenharmony_ci				 port);
301362306a36Sopenharmony_ci		/* Ensure bridging is disabled */
301462306a36Sopenharmony_ci		ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_SRC + port);
301562306a36Sopenharmony_ci	}
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci	for_each_nonreserved_multicast_dest_pgid(ocelot, i) {
301862306a36Sopenharmony_ci		u32 val = ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports - 1, 0));
301962306a36Sopenharmony_ci
302062306a36Sopenharmony_ci		ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i);
302162306a36Sopenharmony_ci	}
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_ci	ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_BLACKHOLE);
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci	/* Allow broadcast and unknown L2 multicast to the CPU. */
302662306a36Sopenharmony_ci	ocelot_rmw_rix(ocelot, ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),
302762306a36Sopenharmony_ci		       ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),
302862306a36Sopenharmony_ci		       ANA_PGID_PGID, PGID_MC);
302962306a36Sopenharmony_ci	ocelot_rmw_rix(ocelot, ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),
303062306a36Sopenharmony_ci		       ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),
303162306a36Sopenharmony_ci		       ANA_PGID_PGID, PGID_BC);
303262306a36Sopenharmony_ci	ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV4);
303362306a36Sopenharmony_ci	ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV6);
303462306a36Sopenharmony_ci
303562306a36Sopenharmony_ci	/* Allow manual injection via DEVCPU_QS registers, and byte swap these
303662306a36Sopenharmony_ci	 * registers endianness.
303762306a36Sopenharmony_ci	 */
303862306a36Sopenharmony_ci	ocelot_write_rix(ocelot, QS_INJ_GRP_CFG_BYTE_SWAP |
303962306a36Sopenharmony_ci			 QS_INJ_GRP_CFG_MODE(1), QS_INJ_GRP_CFG, 0);
304062306a36Sopenharmony_ci	ocelot_write_rix(ocelot, QS_XTR_GRP_CFG_BYTE_SWAP |
304162306a36Sopenharmony_ci			 QS_XTR_GRP_CFG_MODE(1), QS_XTR_GRP_CFG, 0);
304262306a36Sopenharmony_ci	ocelot_write(ocelot, ANA_CPUQ_CFG_CPUQ_MIRROR(2) |
304362306a36Sopenharmony_ci		     ANA_CPUQ_CFG_CPUQ_LRN(2) |
304462306a36Sopenharmony_ci		     ANA_CPUQ_CFG_CPUQ_MAC_COPY(2) |
304562306a36Sopenharmony_ci		     ANA_CPUQ_CFG_CPUQ_SRC_COPY(2) |
304662306a36Sopenharmony_ci		     ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE(2) |
304762306a36Sopenharmony_ci		     ANA_CPUQ_CFG_CPUQ_ALLBRIDGE(6) |
304862306a36Sopenharmony_ci		     ANA_CPUQ_CFG_CPUQ_IPMC_CTRL(6) |
304962306a36Sopenharmony_ci		     ANA_CPUQ_CFG_CPUQ_IGMP(6) |
305062306a36Sopenharmony_ci		     ANA_CPUQ_CFG_CPUQ_MLD(6), ANA_CPUQ_CFG);
305162306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
305262306a36Sopenharmony_ci		ocelot_write_rix(ocelot, ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL(6) |
305362306a36Sopenharmony_ci				 ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL(6),
305462306a36Sopenharmony_ci				 ANA_CPUQ_8021_CFG, i);
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_ci	return 0;
305762306a36Sopenharmony_ci
305862306a36Sopenharmony_cierr_mm_init:
305962306a36Sopenharmony_ci	ocelot_stats_deinit(ocelot);
306062306a36Sopenharmony_cierr_stats_init:
306162306a36Sopenharmony_ci	destroy_workqueue(ocelot->owq);
306262306a36Sopenharmony_ci	return ret;
306362306a36Sopenharmony_ci}
306462306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_init);
306562306a36Sopenharmony_ci
306662306a36Sopenharmony_civoid ocelot_deinit(struct ocelot *ocelot)
306762306a36Sopenharmony_ci{
306862306a36Sopenharmony_ci	ocelot_stats_deinit(ocelot);
306962306a36Sopenharmony_ci	destroy_workqueue(ocelot->owq);
307062306a36Sopenharmony_ci}
307162306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_deinit);
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_civoid ocelot_deinit_port(struct ocelot *ocelot, int port)
307462306a36Sopenharmony_ci{
307562306a36Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci	skb_queue_purge(&ocelot_port->tx_skbs);
307862306a36Sopenharmony_ci}
307962306a36Sopenharmony_ciEXPORT_SYMBOL(ocelot_deinit_port);
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL");
3082