18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright 2019 NXP Semiconductors
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This is an umbrella module for all network switches that are
58c2ecf20Sopenharmony_ci * register-compatible with Ocelot and that perform I/O to their host CPU
68c2ecf20Sopenharmony_ci * through an NPI (Node Processor Interface) Ethernet port.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <uapi/linux/if_bridge.h>
98c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_vcap.h>
108c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_qsys.h>
118c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_sys.h>
128c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_dev.h>
138c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_ana.h>
148c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_ptp.h>
158c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/packing.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/of_net.h>
208c2ecf20Sopenharmony_ci#include <linux/pci.h>
218c2ecf20Sopenharmony_ci#include <linux/of.h>
228c2ecf20Sopenharmony_ci#include <linux/pcs-lynx.h>
238c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
248c2ecf20Sopenharmony_ci#include <net/dsa.h>
258c2ecf20Sopenharmony_ci#include "felix.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic enum dsa_tag_protocol felix_get_tag_protocol(struct dsa_switch *ds,
288c2ecf20Sopenharmony_ci						    int port,
298c2ecf20Sopenharmony_ci						    enum dsa_tag_protocol mp)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	return DSA_TAG_PROTO_OCELOT;
328c2ecf20Sopenharmony_ci}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic int felix_set_ageing_time(struct dsa_switch *ds,
358c2ecf20Sopenharmony_ci				 unsigned int ageing_time)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	ocelot_set_ageing_time(ocelot, ageing_time);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	return 0;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int felix_fdb_dump(struct dsa_switch *ds, int port,
458c2ecf20Sopenharmony_ci			  dsa_fdb_dump_cb_t *cb, void *data)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	return ocelot_fdb_dump(ocelot, port, cb, data);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic int felix_fdb_add(struct dsa_switch *ds, int port,
538c2ecf20Sopenharmony_ci			 const unsigned char *addr, u16 vid)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return ocelot_fdb_add(ocelot, port, addr, vid);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int felix_fdb_del(struct dsa_switch *ds, int port,
618c2ecf20Sopenharmony_ci			 const unsigned char *addr, u16 vid)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return ocelot_fdb_del(ocelot, port, addr, vid);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* This callback needs to be present */
698c2ecf20Sopenharmony_cistatic int felix_mdb_prepare(struct dsa_switch *ds, int port,
708c2ecf20Sopenharmony_ci			     const struct switchdev_obj_port_mdb *mdb)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	return 0;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic void felix_mdb_add(struct dsa_switch *ds, int port,
768c2ecf20Sopenharmony_ci			  const struct switchdev_obj_port_mdb *mdb)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	ocelot_port_mdb_add(ocelot, port, mdb);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int felix_mdb_del(struct dsa_switch *ds, int port,
848c2ecf20Sopenharmony_ci			 const struct switchdev_obj_port_mdb *mdb)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return ocelot_port_mdb_del(ocelot, port, mdb);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void felix_bridge_stp_state_set(struct dsa_switch *ds, int port,
928c2ecf20Sopenharmony_ci				       u8 state)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return ocelot_bridge_stp_state_set(ocelot, port, state);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int felix_bridge_join(struct dsa_switch *ds, int port,
1008c2ecf20Sopenharmony_ci			     struct net_device *br)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return ocelot_port_bridge_join(ocelot, port, br);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic void felix_bridge_leave(struct dsa_switch *ds, int port,
1088c2ecf20Sopenharmony_ci			       struct net_device *br)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	ocelot_port_bridge_leave(ocelot, port, br);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* This callback needs to be present */
1168c2ecf20Sopenharmony_cistatic int felix_vlan_prepare(struct dsa_switch *ds, int port,
1178c2ecf20Sopenharmony_ci			      const struct switchdev_obj_port_vlan *vlan)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	return 0;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
1238c2ecf20Sopenharmony_ci				struct switchdev_trans *trans)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return ocelot_port_vlan_filtering(ocelot, port, enabled, trans);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void felix_vlan_add(struct dsa_switch *ds, int port,
1318c2ecf20Sopenharmony_ci			   const struct switchdev_obj_port_vlan *vlan)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
1348c2ecf20Sopenharmony_ci	u16 flags = vlan->flags;
1358c2ecf20Sopenharmony_ci	u16 vid;
1368c2ecf20Sopenharmony_ci	int err;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (dsa_is_cpu_port(ds, port))
1398c2ecf20Sopenharmony_ci		flags &= ~BRIDGE_VLAN_INFO_UNTAGGED;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
1428c2ecf20Sopenharmony_ci		err = ocelot_vlan_add(ocelot, port, vid,
1438c2ecf20Sopenharmony_ci				      flags & BRIDGE_VLAN_INFO_PVID,
1448c2ecf20Sopenharmony_ci				      flags & BRIDGE_VLAN_INFO_UNTAGGED);
1458c2ecf20Sopenharmony_ci		if (err) {
1468c2ecf20Sopenharmony_ci			dev_err(ds->dev, "Failed to add VLAN %d to port %d: %d\n",
1478c2ecf20Sopenharmony_ci				vid, port, err);
1488c2ecf20Sopenharmony_ci			return;
1498c2ecf20Sopenharmony_ci		}
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int felix_vlan_del(struct dsa_switch *ds, int port,
1548c2ecf20Sopenharmony_ci			  const struct switchdev_obj_port_vlan *vlan)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
1578c2ecf20Sopenharmony_ci	u16 vid;
1588c2ecf20Sopenharmony_ci	int err;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
1618c2ecf20Sopenharmony_ci		err = ocelot_vlan_del(ocelot, port, vid);
1628c2ecf20Sopenharmony_ci		if (err) {
1638c2ecf20Sopenharmony_ci			dev_err(ds->dev, "Failed to remove VLAN %d from port %d: %d\n",
1648c2ecf20Sopenharmony_ci				vid, port, err);
1658c2ecf20Sopenharmony_ci			return err;
1668c2ecf20Sopenharmony_ci		}
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int felix_port_enable(struct dsa_switch *ds, int port,
1728c2ecf20Sopenharmony_ci			     struct phy_device *phy)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	ocelot_port_enable(ocelot, port, phy);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic void felix_port_disable(struct dsa_switch *ds, int port)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return ocelot_port_disable(ocelot, port);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void felix_phylink_validate(struct dsa_switch *ds, int port,
1898c2ecf20Sopenharmony_ci				   unsigned long *supported,
1908c2ecf20Sopenharmony_ci				   struct phylink_link_state *state)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
1938c2ecf20Sopenharmony_ci	struct felix *felix = ocelot_to_felix(ocelot);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (felix->info->phylink_validate)
1968c2ecf20Sopenharmony_ci		felix->info->phylink_validate(ocelot, port, supported, state);
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic void felix_phylink_mac_config(struct dsa_switch *ds, int port,
2008c2ecf20Sopenharmony_ci				     unsigned int link_an_mode,
2018c2ecf20Sopenharmony_ci				     const struct phylink_link_state *state)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
2048c2ecf20Sopenharmony_ci	struct felix *felix = ocelot_to_felix(ocelot);
2058c2ecf20Sopenharmony_ci	struct dsa_port *dp = dsa_to_port(ds, port);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (felix->pcs[port])
2088c2ecf20Sopenharmony_ci		phylink_set_pcs(dp->pl, &felix->pcs[port]->pcs);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,
2128c2ecf20Sopenharmony_ci					unsigned int link_an_mode,
2138c2ecf20Sopenharmony_ci					phy_interface_t interface)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
2168c2ecf20Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
2178c2ecf20Sopenharmony_ci	int err;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	ocelot_port_rmwl(ocelot_port, 0, DEV_MAC_ENA_CFG_RX_ENA,
2208c2ecf20Sopenharmony_ci			 DEV_MAC_ENA_CFG);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	ocelot_fields_write(ocelot, port, QSYS_SWITCH_PORT_MODE_PORT_ENA, 0);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	err = ocelot_port_flush(ocelot, port);
2258c2ecf20Sopenharmony_ci	if (err)
2268c2ecf20Sopenharmony_ci		dev_err(ocelot->dev, "failed to flush port %d: %d\n",
2278c2ecf20Sopenharmony_ci			port, err);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	/* Put the port in reset. */
2308c2ecf20Sopenharmony_ci	ocelot_port_writel(ocelot_port,
2318c2ecf20Sopenharmony_ci			   DEV_CLOCK_CFG_MAC_TX_RST |
2328c2ecf20Sopenharmony_ci			   DEV_CLOCK_CFG_MAC_RX_RST |
2338c2ecf20Sopenharmony_ci			   DEV_CLOCK_CFG_LINK_SPEED(OCELOT_SPEED_1000),
2348c2ecf20Sopenharmony_ci			   DEV_CLOCK_CFG);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
2388c2ecf20Sopenharmony_ci				      unsigned int link_an_mode,
2398c2ecf20Sopenharmony_ci				      phy_interface_t interface,
2408c2ecf20Sopenharmony_ci				      struct phy_device *phydev,
2418c2ecf20Sopenharmony_ci				      int speed, int duplex,
2428c2ecf20Sopenharmony_ci				      bool tx_pause, bool rx_pause)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
2458c2ecf20Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
2468c2ecf20Sopenharmony_ci	struct felix *felix = ocelot_to_felix(ocelot);
2478c2ecf20Sopenharmony_ci	u32 mac_fc_cfg;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/* Take port out of reset by clearing the MAC_TX_RST, MAC_RX_RST and
2508c2ecf20Sopenharmony_ci	 * PORT_RST bits in DEV_CLOCK_CFG. Note that the way this system is
2518c2ecf20Sopenharmony_ci	 * integrated is that the MAC speed is fixed and it's the PCS who is
2528c2ecf20Sopenharmony_ci	 * performing the rate adaptation, so we have to write "1000Mbps" into
2538c2ecf20Sopenharmony_ci	 * the LINK_SPEED field of DEV_CLOCK_CFG (which is also its default
2548c2ecf20Sopenharmony_ci	 * value).
2558c2ecf20Sopenharmony_ci	 */
2568c2ecf20Sopenharmony_ci	ocelot_port_writel(ocelot_port,
2578c2ecf20Sopenharmony_ci			   DEV_CLOCK_CFG_LINK_SPEED(OCELOT_SPEED_1000),
2588c2ecf20Sopenharmony_ci			   DEV_CLOCK_CFG);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	switch (speed) {
2618c2ecf20Sopenharmony_ci	case SPEED_10:
2628c2ecf20Sopenharmony_ci		mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(3);
2638c2ecf20Sopenharmony_ci		break;
2648c2ecf20Sopenharmony_ci	case SPEED_100:
2658c2ecf20Sopenharmony_ci		mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(2);
2668c2ecf20Sopenharmony_ci		break;
2678c2ecf20Sopenharmony_ci	case SPEED_1000:
2688c2ecf20Sopenharmony_ci	case SPEED_2500:
2698c2ecf20Sopenharmony_ci		mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(1);
2708c2ecf20Sopenharmony_ci		break;
2718c2ecf20Sopenharmony_ci	default:
2728c2ecf20Sopenharmony_ci		dev_err(ocelot->dev, "Unsupported speed on port %d: %d\n",
2738c2ecf20Sopenharmony_ci			port, speed);
2748c2ecf20Sopenharmony_ci		return;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* handle Rx pause in all cases, with 2500base-X this is used for rate
2788c2ecf20Sopenharmony_ci	 * adaptation.
2798c2ecf20Sopenharmony_ci	 */
2808c2ecf20Sopenharmony_ci	mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (tx_pause)
2838c2ecf20Sopenharmony_ci		mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA |
2848c2ecf20Sopenharmony_ci			      SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
2858c2ecf20Sopenharmony_ci			      SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
2868c2ecf20Sopenharmony_ci			      SYS_MAC_FC_CFG_ZERO_PAUSE_ENA;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* Flow control. Link speed is only used here to evaluate the time
2898c2ecf20Sopenharmony_ci	 * specification in incoming pause frames.
2908c2ecf20Sopenharmony_ci	 */
2918c2ecf20Sopenharmony_ci	ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* Undo the effects of felix_phylink_mac_link_down:
2968c2ecf20Sopenharmony_ci	 * enable MAC module
2978c2ecf20Sopenharmony_ci	 */
2988c2ecf20Sopenharmony_ci	ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA |
2998c2ecf20Sopenharmony_ci			   DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* Enable receiving frames on the port, and activate auto-learning of
3028c2ecf20Sopenharmony_ci	 * MAC addresses.
3038c2ecf20Sopenharmony_ci	 */
3048c2ecf20Sopenharmony_ci	ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO |
3058c2ecf20Sopenharmony_ci			 ANA_PORT_PORT_CFG_RECV_ENA |
3068c2ecf20Sopenharmony_ci			 ANA_PORT_PORT_CFG_PORTID_VAL(port),
3078c2ecf20Sopenharmony_ci			 ANA_PORT_PORT_CFG, port);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* Core: Enable port for frame transfer */
3108c2ecf20Sopenharmony_ci	ocelot_fields_write(ocelot, port,
3118c2ecf20Sopenharmony_ci			    QSYS_SWITCH_PORT_MODE_PORT_ENA, 1);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (felix->info->port_sched_speed_set)
3148c2ecf20Sopenharmony_ci		felix->info->port_sched_speed_set(ocelot, port, speed);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic void felix_port_qos_map_init(struct ocelot *ocelot, int port)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	int i;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	ocelot_rmw_gix(ocelot,
3228c2ecf20Sopenharmony_ci		       ANA_PORT_QOS_CFG_QOS_PCP_ENA,
3238c2ecf20Sopenharmony_ci		       ANA_PORT_QOS_CFG_QOS_PCP_ENA,
3248c2ecf20Sopenharmony_ci		       ANA_PORT_QOS_CFG,
3258c2ecf20Sopenharmony_ci		       port);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	for (i = 0; i < FELIX_NUM_TC * 2; i++) {
3288c2ecf20Sopenharmony_ci		ocelot_rmw_ix(ocelot,
3298c2ecf20Sopenharmony_ci			      (ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL & i) |
3308c2ecf20Sopenharmony_ci			      ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL(i),
3318c2ecf20Sopenharmony_ci			      ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL |
3328c2ecf20Sopenharmony_ci			      ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL_M,
3338c2ecf20Sopenharmony_ci			      ANA_PORT_PCP_DEI_MAP,
3348c2ecf20Sopenharmony_ci			      port, i);
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic void felix_get_strings(struct dsa_switch *ds, int port,
3398c2ecf20Sopenharmony_ci			      u32 stringset, u8 *data)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return ocelot_get_strings(ocelot, port, stringset, data);
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic void felix_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	ocelot_get_ethtool_stats(ocelot, port, data);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic int felix_get_sset_count(struct dsa_switch *ds, int port, int sset)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return ocelot_get_sset_count(ocelot, port, sset);
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int felix_get_ts_info(struct dsa_switch *ds, int port,
3618c2ecf20Sopenharmony_ci			     struct ethtool_ts_info *info)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return ocelot_get_ts_info(ocelot, port, info);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic int felix_parse_ports_node(struct felix *felix,
3698c2ecf20Sopenharmony_ci				  struct device_node *ports_node,
3708c2ecf20Sopenharmony_ci				  phy_interface_t *port_phy_modes)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct ocelot *ocelot = &felix->ocelot;
3738c2ecf20Sopenharmony_ci	struct device *dev = felix->ocelot.dev;
3748c2ecf20Sopenharmony_ci	struct device_node *child;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	for_each_available_child_of_node(ports_node, child) {
3778c2ecf20Sopenharmony_ci		phy_interface_t phy_mode;
3788c2ecf20Sopenharmony_ci		u32 port;
3798c2ecf20Sopenharmony_ci		int err;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		/* Get switch port number from DT */
3828c2ecf20Sopenharmony_ci		if (of_property_read_u32(child, "reg", &port) < 0) {
3838c2ecf20Sopenharmony_ci			dev_err(dev, "Port number not defined in device tree "
3848c2ecf20Sopenharmony_ci				"(property \"reg\")\n");
3858c2ecf20Sopenharmony_ci			of_node_put(child);
3868c2ecf20Sopenharmony_ci			return -ENODEV;
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		/* Get PHY mode from DT */
3908c2ecf20Sopenharmony_ci		err = of_get_phy_mode(child, &phy_mode);
3918c2ecf20Sopenharmony_ci		if (err) {
3928c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to read phy-mode or "
3938c2ecf20Sopenharmony_ci				"phy-interface-type property for port %d\n",
3948c2ecf20Sopenharmony_ci				port);
3958c2ecf20Sopenharmony_ci			of_node_put(child);
3968c2ecf20Sopenharmony_ci			return -ENODEV;
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		err = felix->info->prevalidate_phy_mode(ocelot, port, phy_mode);
4008c2ecf20Sopenharmony_ci		if (err < 0) {
4018c2ecf20Sopenharmony_ci			dev_err(dev, "Unsupported PHY mode %s on port %d\n",
4028c2ecf20Sopenharmony_ci				phy_modes(phy_mode), port);
4038c2ecf20Sopenharmony_ci			of_node_put(child);
4048c2ecf20Sopenharmony_ci			return err;
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		port_phy_modes[port] = phy_mode;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int felix_parse_dt(struct felix *felix, phy_interface_t *port_phy_modes)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct device *dev = felix->ocelot.dev;
4168c2ecf20Sopenharmony_ci	struct device_node *switch_node;
4178c2ecf20Sopenharmony_ci	struct device_node *ports_node;
4188c2ecf20Sopenharmony_ci	int err;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	switch_node = dev->of_node;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	ports_node = of_get_child_by_name(switch_node, "ports");
4238c2ecf20Sopenharmony_ci	if (!ports_node) {
4248c2ecf20Sopenharmony_ci		dev_err(dev, "Incorrect bindings: absent \"ports\" node\n");
4258c2ecf20Sopenharmony_ci		return -ENODEV;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	err = felix_parse_ports_node(felix, ports_node, port_phy_modes);
4298c2ecf20Sopenharmony_ci	of_node_put(ports_node);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return err;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic int felix_init_structs(struct felix *felix, int num_phys_ports)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	struct ocelot *ocelot = &felix->ocelot;
4378c2ecf20Sopenharmony_ci	phy_interface_t *port_phy_modes;
4388c2ecf20Sopenharmony_ci	struct resource res;
4398c2ecf20Sopenharmony_ci	int port, i, err;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	ocelot->num_phys_ports = num_phys_ports;
4428c2ecf20Sopenharmony_ci	ocelot->ports = devm_kcalloc(ocelot->dev, num_phys_ports,
4438c2ecf20Sopenharmony_ci				     sizeof(struct ocelot_port *), GFP_KERNEL);
4448c2ecf20Sopenharmony_ci	if (!ocelot->ports)
4458c2ecf20Sopenharmony_ci		return -ENOMEM;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	ocelot->map		= felix->info->map;
4488c2ecf20Sopenharmony_ci	ocelot->stats_layout	= felix->info->stats_layout;
4498c2ecf20Sopenharmony_ci	ocelot->num_stats	= felix->info->num_stats;
4508c2ecf20Sopenharmony_ci	ocelot->shared_queue_sz	= felix->info->shared_queue_sz;
4518c2ecf20Sopenharmony_ci	ocelot->num_mact_rows	= felix->info->num_mact_rows;
4528c2ecf20Sopenharmony_ci	ocelot->vcap		= felix->info->vcap;
4538c2ecf20Sopenharmony_ci	ocelot->ops		= felix->info->ops;
4548c2ecf20Sopenharmony_ci	ocelot->inj_prefix	= OCELOT_TAG_PREFIX_SHORT;
4558c2ecf20Sopenharmony_ci	ocelot->xtr_prefix	= OCELOT_TAG_PREFIX_SHORT;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	port_phy_modes = kcalloc(num_phys_ports, sizeof(phy_interface_t),
4588c2ecf20Sopenharmony_ci				 GFP_KERNEL);
4598c2ecf20Sopenharmony_ci	if (!port_phy_modes)
4608c2ecf20Sopenharmony_ci		return -ENOMEM;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	err = felix_parse_dt(felix, port_phy_modes);
4638c2ecf20Sopenharmony_ci	if (err) {
4648c2ecf20Sopenharmony_ci		kfree(port_phy_modes);
4658c2ecf20Sopenharmony_ci		return err;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	for (i = 0; i < TARGET_MAX; i++) {
4698c2ecf20Sopenharmony_ci		struct regmap *target;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		if (!felix->info->target_io_res[i].name)
4728c2ecf20Sopenharmony_ci			continue;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		memcpy(&res, &felix->info->target_io_res[i], sizeof(res));
4758c2ecf20Sopenharmony_ci		res.flags = IORESOURCE_MEM;
4768c2ecf20Sopenharmony_ci		res.start += felix->switch_base;
4778c2ecf20Sopenharmony_ci		res.end += felix->switch_base;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci		target = ocelot_regmap_init(ocelot, &res);
4808c2ecf20Sopenharmony_ci		if (IS_ERR(target)) {
4818c2ecf20Sopenharmony_ci			dev_err(ocelot->dev,
4828c2ecf20Sopenharmony_ci				"Failed to map device memory space\n");
4838c2ecf20Sopenharmony_ci			kfree(port_phy_modes);
4848c2ecf20Sopenharmony_ci			return PTR_ERR(target);
4858c2ecf20Sopenharmony_ci		}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		ocelot->targets[i] = target;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	err = ocelot_regfields_init(ocelot, felix->info->regfields);
4918c2ecf20Sopenharmony_ci	if (err) {
4928c2ecf20Sopenharmony_ci		dev_err(ocelot->dev, "failed to init reg fields map\n");
4938c2ecf20Sopenharmony_ci		kfree(port_phy_modes);
4948c2ecf20Sopenharmony_ci		return err;
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	for (port = 0; port < num_phys_ports; port++) {
4988c2ecf20Sopenharmony_ci		struct ocelot_port *ocelot_port;
4998c2ecf20Sopenharmony_ci		struct regmap *target;
5008c2ecf20Sopenharmony_ci		u8 *template;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		ocelot_port = devm_kzalloc(ocelot->dev,
5038c2ecf20Sopenharmony_ci					   sizeof(struct ocelot_port),
5048c2ecf20Sopenharmony_ci					   GFP_KERNEL);
5058c2ecf20Sopenharmony_ci		if (!ocelot_port) {
5068c2ecf20Sopenharmony_ci			dev_err(ocelot->dev,
5078c2ecf20Sopenharmony_ci				"failed to allocate port memory\n");
5088c2ecf20Sopenharmony_ci			kfree(port_phy_modes);
5098c2ecf20Sopenharmony_ci			return -ENOMEM;
5108c2ecf20Sopenharmony_ci		}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		memcpy(&res, &felix->info->port_io_res[port], sizeof(res));
5138c2ecf20Sopenharmony_ci		res.flags = IORESOURCE_MEM;
5148c2ecf20Sopenharmony_ci		res.start += felix->switch_base;
5158c2ecf20Sopenharmony_ci		res.end += felix->switch_base;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		target = ocelot_regmap_init(ocelot, &res);
5188c2ecf20Sopenharmony_ci		if (IS_ERR(target)) {
5198c2ecf20Sopenharmony_ci			dev_err(ocelot->dev,
5208c2ecf20Sopenharmony_ci				"Failed to map memory space for port %d\n",
5218c2ecf20Sopenharmony_ci				port);
5228c2ecf20Sopenharmony_ci			kfree(port_phy_modes);
5238c2ecf20Sopenharmony_ci			return PTR_ERR(target);
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		template = devm_kzalloc(ocelot->dev, OCELOT_TOTAL_TAG_LEN,
5278c2ecf20Sopenharmony_ci					GFP_KERNEL);
5288c2ecf20Sopenharmony_ci		if (!template) {
5298c2ecf20Sopenharmony_ci			dev_err(ocelot->dev,
5308c2ecf20Sopenharmony_ci				"Failed to allocate memory for DSA tag\n");
5318c2ecf20Sopenharmony_ci			kfree(port_phy_modes);
5328c2ecf20Sopenharmony_ci			return -ENOMEM;
5338c2ecf20Sopenharmony_ci		}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci		ocelot_port->phy_mode = port_phy_modes[port];
5368c2ecf20Sopenharmony_ci		ocelot_port->ocelot = ocelot;
5378c2ecf20Sopenharmony_ci		ocelot_port->target = target;
5388c2ecf20Sopenharmony_ci		ocelot_port->xmit_template = template;
5398c2ecf20Sopenharmony_ci		ocelot->ports[port] = ocelot_port;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci		felix->info->xmit_template_populate(ocelot, port);
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	kfree(port_phy_modes);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	if (felix->info->mdio_bus_alloc) {
5478c2ecf20Sopenharmony_ci		err = felix->info->mdio_bus_alloc(ocelot);
5488c2ecf20Sopenharmony_ci		if (err < 0)
5498c2ecf20Sopenharmony_ci			return err;
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	return 0;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci/* The CPU port module is connected to the Node Processor Interface (NPI). This
5568c2ecf20Sopenharmony_ci * is the mode through which frames can be injected from and extracted to an
5578c2ecf20Sopenharmony_ci * external CPU, over Ethernet.
5588c2ecf20Sopenharmony_ci */
5598c2ecf20Sopenharmony_cistatic void felix_npi_port_init(struct ocelot *ocelot, int port)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	ocelot->npi = port;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M |
5648c2ecf20Sopenharmony_ci		     QSYS_EXT_CPU_CFG_EXT_CPU_PORT(port),
5658c2ecf20Sopenharmony_ci		     QSYS_EXT_CPU_CFG);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* NPI port Injection/Extraction configuration */
5688c2ecf20Sopenharmony_ci	ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR,
5698c2ecf20Sopenharmony_ci			    ocelot->xtr_prefix);
5708c2ecf20Sopenharmony_ci	ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR,
5718c2ecf20Sopenharmony_ci			    ocelot->inj_prefix);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	/* Disable transmission of pause frames */
5748c2ecf20Sopenharmony_ci	ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 0);
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci/* Hardware initialization done here so that we can allocate structures with
5788c2ecf20Sopenharmony_ci * devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing
5798c2ecf20Sopenharmony_ci * us to allocate structures twice (leak memory) and map PCI memory twice
5808c2ecf20Sopenharmony_ci * (which will not work).
5818c2ecf20Sopenharmony_ci */
5828c2ecf20Sopenharmony_cistatic int felix_setup(struct dsa_switch *ds)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
5858c2ecf20Sopenharmony_ci	struct felix *felix = ocelot_to_felix(ocelot);
5868c2ecf20Sopenharmony_ci	int port, err;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	err = felix_init_structs(felix, ds->num_ports);
5898c2ecf20Sopenharmony_ci	if (err)
5908c2ecf20Sopenharmony_ci		return err;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	err = ocelot_init(ocelot);
5938c2ecf20Sopenharmony_ci	if (err)
5948c2ecf20Sopenharmony_ci		return err;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	if (ocelot->ptp) {
5978c2ecf20Sopenharmony_ci		err = ocelot_init_timestamp(ocelot, felix->info->ptp_caps);
5988c2ecf20Sopenharmony_ci		if (err) {
5998c2ecf20Sopenharmony_ci			dev_err(ocelot->dev,
6008c2ecf20Sopenharmony_ci				"Timestamp initialization failed\n");
6018c2ecf20Sopenharmony_ci			ocelot->ptp = 0;
6028c2ecf20Sopenharmony_ci		}
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	for (port = 0; port < ds->num_ports; port++) {
6068c2ecf20Sopenharmony_ci		ocelot_init_port(ocelot, port);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci		if (dsa_is_cpu_port(ds, port))
6098c2ecf20Sopenharmony_ci			felix_npi_port_init(ocelot, port);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci		/* Set the default QoS Classification based on PCP and DEI
6128c2ecf20Sopenharmony_ci		 * bits of vlan tag.
6138c2ecf20Sopenharmony_ci		 */
6148c2ecf20Sopenharmony_ci		felix_port_qos_map_init(ocelot, port);
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	/* Include the CPU port module in the forwarding mask for unknown
6188c2ecf20Sopenharmony_ci	 * unicast - the hardware default value for ANA_FLOODING_FLD_UNICAST
6198c2ecf20Sopenharmony_ci	 * excludes BIT(ocelot->num_phys_ports), and so does ocelot_init, since
6208c2ecf20Sopenharmony_ci	 * Ocelot relies on whitelisting MAC addresses towards PGID_CPU.
6218c2ecf20Sopenharmony_ci	 */
6228c2ecf20Sopenharmony_ci	ocelot_write_rix(ocelot,
6238c2ecf20Sopenharmony_ci			 ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)),
6248c2ecf20Sopenharmony_ci			 ANA_PGID_PGID, PGID_UC);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	ds->mtu_enforcement_ingress = true;
6278c2ecf20Sopenharmony_ci	ds->configure_vlan_while_not_filtering = true;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	return 0;
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cistatic void felix_teardown(struct dsa_switch *ds)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
6358c2ecf20Sopenharmony_ci	struct felix *felix = ocelot_to_felix(ocelot);
6368c2ecf20Sopenharmony_ci	int port;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	ocelot_deinit_timestamp(ocelot);
6398c2ecf20Sopenharmony_ci	ocelot_deinit(ocelot);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	for (port = 0; port < ocelot->num_phys_ports; port++) {
6428c2ecf20Sopenharmony_ci		if (dsa_is_unused_port(ds, port))
6438c2ecf20Sopenharmony_ci			continue;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci		ocelot_deinit_port(ocelot, port);
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	if (felix->info->mdio_bus_free)
6498c2ecf20Sopenharmony_ci		felix->info->mdio_bus_free(ocelot);
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic int felix_hwtstamp_get(struct dsa_switch *ds, int port,
6538c2ecf20Sopenharmony_ci			      struct ifreq *ifr)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	return ocelot_hwstamp_get(ocelot, port, ifr);
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic int felix_hwtstamp_set(struct dsa_switch *ds, int port,
6618c2ecf20Sopenharmony_ci			      struct ifreq *ifr)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	return ocelot_hwstamp_set(ocelot, port, ifr);
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic bool felix_rxtstamp(struct dsa_switch *ds, int port,
6698c2ecf20Sopenharmony_ci			   struct sk_buff *skb, unsigned int type)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct skb_shared_hwtstamps *shhwtstamps;
6728c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
6738c2ecf20Sopenharmony_ci	u8 *extraction = skb->data - ETH_HLEN - OCELOT_TAG_LEN;
6748c2ecf20Sopenharmony_ci	u32 tstamp_lo, tstamp_hi;
6758c2ecf20Sopenharmony_ci	struct timespec64 ts;
6768c2ecf20Sopenharmony_ci	u64 tstamp, val;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
6798c2ecf20Sopenharmony_ci	tstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	packing(extraction, &val,  116, 85, OCELOT_TAG_LEN, UNPACK, 0);
6828c2ecf20Sopenharmony_ci	tstamp_lo = (u32)val;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	tstamp_hi = tstamp >> 32;
6858c2ecf20Sopenharmony_ci	if ((tstamp & 0xffffffff) < tstamp_lo)
6868c2ecf20Sopenharmony_ci		tstamp_hi--;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	tstamp = ((u64)tstamp_hi << 32) | tstamp_lo;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	shhwtstamps = skb_hwtstamps(skb);
6918c2ecf20Sopenharmony_ci	memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
6928c2ecf20Sopenharmony_ci	shhwtstamps->hwtstamp = tstamp;
6938c2ecf20Sopenharmony_ci	return false;
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_cistatic bool felix_txtstamp(struct dsa_switch *ds, int port,
6978c2ecf20Sopenharmony_ci			   struct sk_buff *clone, unsigned int type)
6988c2ecf20Sopenharmony_ci{
6998c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
7008c2ecf20Sopenharmony_ci	struct ocelot_port *ocelot_port = ocelot->ports[port];
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (ocelot->ptp && (skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP) &&
7038c2ecf20Sopenharmony_ci	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
7048c2ecf20Sopenharmony_ci		ocelot_port_add_txtstamp_skb(ocelot, port, clone);
7058c2ecf20Sopenharmony_ci		return true;
7068c2ecf20Sopenharmony_ci	}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	return false;
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic int felix_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	ocelot_port_set_maxlen(ocelot, port, new_mtu);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	return 0;
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic int felix_get_max_mtu(struct dsa_switch *ds, int port)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	return ocelot_get_max_mtu(ocelot, port);
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_cistatic int felix_cls_flower_add(struct dsa_switch *ds, int port,
7288c2ecf20Sopenharmony_ci				struct flow_cls_offload *cls, bool ingress)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	return ocelot_cls_flower_replace(ocelot, port, cls, ingress);
7338c2ecf20Sopenharmony_ci}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_cistatic int felix_cls_flower_del(struct dsa_switch *ds, int port,
7368c2ecf20Sopenharmony_ci				struct flow_cls_offload *cls, bool ingress)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	return ocelot_cls_flower_destroy(ocelot, port, cls, ingress);
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_cistatic int felix_cls_flower_stats(struct dsa_switch *ds, int port,
7448c2ecf20Sopenharmony_ci				  struct flow_cls_offload *cls, bool ingress)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	return ocelot_cls_flower_stats(ocelot, port, cls, ingress);
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic int felix_port_policer_add(struct dsa_switch *ds, int port,
7528c2ecf20Sopenharmony_ci				  struct dsa_mall_policer_tc_entry *policer)
7538c2ecf20Sopenharmony_ci{
7548c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
7558c2ecf20Sopenharmony_ci	struct ocelot_policer pol = {
7568c2ecf20Sopenharmony_ci		.rate = div_u64(policer->rate_bytes_per_sec, 1000) * 8,
7578c2ecf20Sopenharmony_ci		.burst = policer->burst,
7588c2ecf20Sopenharmony_ci	};
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	return ocelot_port_policer_add(ocelot, port, &pol);
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic void felix_port_policer_del(struct dsa_switch *ds, int port)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	ocelot_port_policer_del(ocelot, port);
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic int felix_port_setup_tc(struct dsa_switch *ds, int port,
7718c2ecf20Sopenharmony_ci			       enum tc_setup_type type,
7728c2ecf20Sopenharmony_ci			       void *type_data)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct ocelot *ocelot = ds->priv;
7758c2ecf20Sopenharmony_ci	struct felix *felix = ocelot_to_felix(ocelot);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	if (felix->info->port_setup_tc)
7788c2ecf20Sopenharmony_ci		return felix->info->port_setup_tc(ds, port, type, type_data);
7798c2ecf20Sopenharmony_ci	else
7808c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ciconst struct dsa_switch_ops felix_switch_ops = {
7848c2ecf20Sopenharmony_ci	.get_tag_protocol	= felix_get_tag_protocol,
7858c2ecf20Sopenharmony_ci	.setup			= felix_setup,
7868c2ecf20Sopenharmony_ci	.teardown		= felix_teardown,
7878c2ecf20Sopenharmony_ci	.set_ageing_time	= felix_set_ageing_time,
7888c2ecf20Sopenharmony_ci	.get_strings		= felix_get_strings,
7898c2ecf20Sopenharmony_ci	.get_ethtool_stats	= felix_get_ethtool_stats,
7908c2ecf20Sopenharmony_ci	.get_sset_count		= felix_get_sset_count,
7918c2ecf20Sopenharmony_ci	.get_ts_info		= felix_get_ts_info,
7928c2ecf20Sopenharmony_ci	.phylink_validate	= felix_phylink_validate,
7938c2ecf20Sopenharmony_ci	.phylink_mac_config	= felix_phylink_mac_config,
7948c2ecf20Sopenharmony_ci	.phylink_mac_link_down	= felix_phylink_mac_link_down,
7958c2ecf20Sopenharmony_ci	.phylink_mac_link_up	= felix_phylink_mac_link_up,
7968c2ecf20Sopenharmony_ci	.port_enable		= felix_port_enable,
7978c2ecf20Sopenharmony_ci	.port_disable		= felix_port_disable,
7988c2ecf20Sopenharmony_ci	.port_fdb_dump		= felix_fdb_dump,
7998c2ecf20Sopenharmony_ci	.port_fdb_add		= felix_fdb_add,
8008c2ecf20Sopenharmony_ci	.port_fdb_del		= felix_fdb_del,
8018c2ecf20Sopenharmony_ci	.port_mdb_prepare	= felix_mdb_prepare,
8028c2ecf20Sopenharmony_ci	.port_mdb_add		= felix_mdb_add,
8038c2ecf20Sopenharmony_ci	.port_mdb_del		= felix_mdb_del,
8048c2ecf20Sopenharmony_ci	.port_bridge_join	= felix_bridge_join,
8058c2ecf20Sopenharmony_ci	.port_bridge_leave	= felix_bridge_leave,
8068c2ecf20Sopenharmony_ci	.port_stp_state_set	= felix_bridge_stp_state_set,
8078c2ecf20Sopenharmony_ci	.port_vlan_prepare	= felix_vlan_prepare,
8088c2ecf20Sopenharmony_ci	.port_vlan_filtering	= felix_vlan_filtering,
8098c2ecf20Sopenharmony_ci	.port_vlan_add		= felix_vlan_add,
8108c2ecf20Sopenharmony_ci	.port_vlan_del		= felix_vlan_del,
8118c2ecf20Sopenharmony_ci	.port_hwtstamp_get	= felix_hwtstamp_get,
8128c2ecf20Sopenharmony_ci	.port_hwtstamp_set	= felix_hwtstamp_set,
8138c2ecf20Sopenharmony_ci	.port_rxtstamp		= felix_rxtstamp,
8148c2ecf20Sopenharmony_ci	.port_txtstamp		= felix_txtstamp,
8158c2ecf20Sopenharmony_ci	.port_change_mtu	= felix_change_mtu,
8168c2ecf20Sopenharmony_ci	.port_max_mtu		= felix_get_max_mtu,
8178c2ecf20Sopenharmony_ci	.port_policer_add	= felix_port_policer_add,
8188c2ecf20Sopenharmony_ci	.port_policer_del	= felix_port_policer_del,
8198c2ecf20Sopenharmony_ci	.cls_flower_add		= felix_cls_flower_add,
8208c2ecf20Sopenharmony_ci	.cls_flower_del		= felix_cls_flower_del,
8218c2ecf20Sopenharmony_ci	.cls_flower_stats	= felix_cls_flower_stats,
8228c2ecf20Sopenharmony_ci	.port_setup_tc		= felix_port_setup_tc,
8238c2ecf20Sopenharmony_ci};
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cistruct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	struct felix *felix = ocelot_to_felix(ocelot);
8288c2ecf20Sopenharmony_ci	struct dsa_switch *ds = felix->ds;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (!dsa_is_user_port(ds, port))
8318c2ecf20Sopenharmony_ci		return NULL;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	return dsa_to_port(ds, port)->slave;
8348c2ecf20Sopenharmony_ci}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ciint felix_netdev_to_port(struct net_device *dev)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct dsa_port *dp;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	dp = dsa_port_from_netdev(dev);
8418c2ecf20Sopenharmony_ci	if (IS_ERR(dp))
8428c2ecf20Sopenharmony_ci		return -EINVAL;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	return dp->index;
8458c2ecf20Sopenharmony_ci}
846