162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Texas Instruments Ethernet Switch Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2019 Texas Instruments
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/platform_device.h>
1162306a36Sopenharmony_ci#include <linux/timer.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/irqreturn.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/if_ether.h>
1662306a36Sopenharmony_ci#include <linux/etherdevice.h>
1762306a36Sopenharmony_ci#include <linux/net_tstamp.h>
1862306a36Sopenharmony_ci#include <linux/phy.h>
1962306a36Sopenharmony_ci#include <linux/phy/phy.h>
2062306a36Sopenharmony_ci#include <linux/delay.h>
2162306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
2262306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2362306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2462306a36Sopenharmony_ci#include <linux/of.h>
2562306a36Sopenharmony_ci#include <linux/of_mdio.h>
2662306a36Sopenharmony_ci#include <linux/of_net.h>
2762306a36Sopenharmony_ci#include <linux/of_platform.h>
2862306a36Sopenharmony_ci#include <linux/if_vlan.h>
2962306a36Sopenharmony_ci#include <linux/kmemleak.h>
3062306a36Sopenharmony_ci#include <linux/sys_soc.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <net/switchdev.h>
3362306a36Sopenharmony_ci#include <net/page_pool/helpers.h>
3462306a36Sopenharmony_ci#include <net/pkt_cls.h>
3562306a36Sopenharmony_ci#include <net/devlink.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "cpsw.h"
3862306a36Sopenharmony_ci#include "cpsw_ale.h"
3962306a36Sopenharmony_ci#include "cpsw_priv.h"
4062306a36Sopenharmony_ci#include "cpsw_sl.h"
4162306a36Sopenharmony_ci#include "cpsw_switchdev.h"
4262306a36Sopenharmony_ci#include "cpts.h"
4362306a36Sopenharmony_ci#include "davinci_cpdma.h"
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <net/pkt_sched.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int debug_level;
4862306a36Sopenharmony_cistatic int ale_ageout = CPSW_ALE_AGEOUT_DEFAULT;
4962306a36Sopenharmony_cistatic int rx_packet_max = CPSW_MAX_PACKET_SIZE;
5062306a36Sopenharmony_cistatic int descs_pool_size = CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistruct cpsw_devlink {
5362306a36Sopenharmony_ci	struct cpsw_common *cpsw;
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cienum cpsw_devlink_param_id {
5762306a36Sopenharmony_ci	CPSW_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
5862306a36Sopenharmony_ci	CPSW_DL_PARAM_SWITCH_MODE,
5962306a36Sopenharmony_ci	CPSW_DL_PARAM_ALE_BYPASS,
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* struct cpsw_common is not needed, kept here for compatibility
6362306a36Sopenharmony_ci * reasons witrh the old driver
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_cistatic int cpsw_slave_index_priv(struct cpsw_common *cpsw,
6662306a36Sopenharmony_ci				 struct cpsw_priv *priv)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	if (priv->emac_port == HOST_PORT_NUM)
6962306a36Sopenharmony_ci		return -1;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return priv->emac_port - 1;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic bool cpsw_is_switch_en(struct cpsw_common *cpsw)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	return !cpsw->data.dual_emac;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void cpsw_set_promiscious(struct net_device *ndev, bool enable)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
8262306a36Sopenharmony_ci	bool enable_uni = false;
8362306a36Sopenharmony_ci	int i;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (cpsw_is_switch_en(cpsw))
8662306a36Sopenharmony_ci		return;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Enabling promiscuous mode for one interface will be
8962306a36Sopenharmony_ci	 * common for both the interface as the interface shares
9062306a36Sopenharmony_ci	 * the same hardware resource.
9162306a36Sopenharmony_ci	 */
9262306a36Sopenharmony_ci	for (i = 0; i < cpsw->data.slaves; i++)
9362306a36Sopenharmony_ci		if (cpsw->slaves[i].ndev &&
9462306a36Sopenharmony_ci		    (cpsw->slaves[i].ndev->flags & IFF_PROMISC))
9562306a36Sopenharmony_ci			enable_uni = true;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (!enable && enable_uni) {
9862306a36Sopenharmony_ci		enable = enable_uni;
9962306a36Sopenharmony_ci		dev_dbg(cpsw->dev, "promiscuity not disabled as the other interface is still in promiscuity mode\n");
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (enable) {
10362306a36Sopenharmony_ci		/* Enable unknown unicast, reg/unreg mcast */
10462306a36Sopenharmony_ci		cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM,
10562306a36Sopenharmony_ci				     ALE_P0_UNI_FLOOD, 1);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		dev_dbg(cpsw->dev, "promiscuity enabled\n");
10862306a36Sopenharmony_ci	} else {
10962306a36Sopenharmony_ci		/* Disable unknown unicast */
11062306a36Sopenharmony_ci		cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM,
11162306a36Sopenharmony_ci				     ALE_P0_UNI_FLOOD, 0);
11262306a36Sopenharmony_ci		dev_dbg(cpsw->dev, "promiscuity disabled\n");
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/**
11762306a36Sopenharmony_ci * cpsw_set_mc - adds multicast entry to the table if it's not added or deletes
11862306a36Sopenharmony_ci * if it's not deleted
11962306a36Sopenharmony_ci * @ndev: device to sync
12062306a36Sopenharmony_ci * @addr: address to be added or deleted
12162306a36Sopenharmony_ci * @vid: vlan id, if vid < 0 set/unset address for real device
12262306a36Sopenharmony_ci * @add: add address if the flag is set or remove otherwise
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_cistatic int cpsw_set_mc(struct net_device *ndev, const u8 *addr,
12562306a36Sopenharmony_ci		       int vid, int add)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
12862306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
12962306a36Sopenharmony_ci	int mask, flags, ret, slave_no;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	slave_no = cpsw_slave_index(cpsw, priv);
13262306a36Sopenharmony_ci	if (vid < 0)
13362306a36Sopenharmony_ci		vid = cpsw->slaves[slave_no].port_vlan;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	mask =  ALE_PORT_HOST;
13662306a36Sopenharmony_ci	flags = vid ? ALE_VLAN : 0;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (add)
13962306a36Sopenharmony_ci		ret = cpsw_ale_add_mcast(cpsw->ale, addr, mask, flags, vid, 0);
14062306a36Sopenharmony_ci	else
14162306a36Sopenharmony_ci		ret = cpsw_ale_del_mcast(cpsw->ale, addr, 0, flags, vid);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return ret;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int cpsw_update_vlan_mc(struct net_device *vdev, int vid, void *ctx)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct addr_sync_ctx *sync_ctx = ctx;
14962306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
15062306a36Sopenharmony_ci	int found = 0, ret = 0;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (!vdev || !(vdev->flags & IFF_UP))
15362306a36Sopenharmony_ci		return 0;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* vlan address is relevant if its sync_cnt != 0 */
15662306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, vdev) {
15762306a36Sopenharmony_ci		if (ether_addr_equal(ha->addr, sync_ctx->addr)) {
15862306a36Sopenharmony_ci			found = ha->sync_cnt;
15962306a36Sopenharmony_ci			break;
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (found)
16462306a36Sopenharmony_ci		sync_ctx->consumed++;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (sync_ctx->flush) {
16762306a36Sopenharmony_ci		if (!found)
16862306a36Sopenharmony_ci			cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0);
16962306a36Sopenharmony_ci		return 0;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (found)
17362306a36Sopenharmony_ci		ret = cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 1);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return ret;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr, int num)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct addr_sync_ctx sync_ctx;
18162306a36Sopenharmony_ci	int ret;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	sync_ctx.consumed = 0;
18462306a36Sopenharmony_ci	sync_ctx.addr = addr;
18562306a36Sopenharmony_ci	sync_ctx.ndev = ndev;
18662306a36Sopenharmony_ci	sync_ctx.flush = 0;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	ret = vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx);
18962306a36Sopenharmony_ci	if (sync_ctx.consumed < num && !ret)
19062306a36Sopenharmony_ci		ret = cpsw_set_mc(ndev, addr, -1, 1);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return ret;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr, int num)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct addr_sync_ctx sync_ctx;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	sync_ctx.consumed = 0;
20062306a36Sopenharmony_ci	sync_ctx.addr = addr;
20162306a36Sopenharmony_ci	sync_ctx.ndev = ndev;
20262306a36Sopenharmony_ci	sync_ctx.flush = 1;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx);
20562306a36Sopenharmony_ci	if (sync_ctx.consumed == num)
20662306a36Sopenharmony_ci		cpsw_set_mc(ndev, addr, -1, 0);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return 0;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int cpsw_purge_vlan_mc(struct net_device *vdev, int vid, void *ctx)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct addr_sync_ctx *sync_ctx = ctx;
21462306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
21562306a36Sopenharmony_ci	int found = 0;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (!vdev || !(vdev->flags & IFF_UP))
21862306a36Sopenharmony_ci		return 0;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* vlan address is relevant if its sync_cnt != 0 */
22162306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, vdev) {
22262306a36Sopenharmony_ci		if (ether_addr_equal(ha->addr, sync_ctx->addr)) {
22362306a36Sopenharmony_ci			found = ha->sync_cnt;
22462306a36Sopenharmony_ci			break;
22562306a36Sopenharmony_ci		}
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (!found)
22962306a36Sopenharmony_ci		return 0;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	sync_ctx->consumed++;
23262306a36Sopenharmony_ci	cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0);
23362306a36Sopenharmony_ci	return 0;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct addr_sync_ctx sync_ctx;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	sync_ctx.addr = addr;
24162306a36Sopenharmony_ci	sync_ctx.ndev = ndev;
24262306a36Sopenharmony_ci	sync_ctx.consumed = 0;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	vlan_for_each(ndev, cpsw_purge_vlan_mc, &sync_ctx);
24562306a36Sopenharmony_ci	if (sync_ctx.consumed < num)
24662306a36Sopenharmony_ci		cpsw_set_mc(ndev, addr, -1, 0);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void cpsw_ndo_set_rx_mode(struct net_device *ndev)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
25462306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (ndev->flags & IFF_PROMISC) {
25762306a36Sopenharmony_ci		/* Enable promiscuous mode */
25862306a36Sopenharmony_ci		cpsw_set_promiscious(ndev, true);
25962306a36Sopenharmony_ci		cpsw_ale_set_allmulti(cpsw->ale, IFF_ALLMULTI, priv->emac_port);
26062306a36Sopenharmony_ci		return;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/* Disable promiscuous mode */
26462306a36Sopenharmony_ci	cpsw_set_promiscious(ndev, false);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/* Restore allmulti on vlans if necessary */
26762306a36Sopenharmony_ci	cpsw_ale_set_allmulti(cpsw->ale,
26862306a36Sopenharmony_ci			      ndev->flags & IFF_ALLMULTI, priv->emac_port);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/* add/remove mcast address either for real netdev or for vlan */
27162306a36Sopenharmony_ci	__hw_addr_ref_sync_dev(&ndev->mc, ndev, cpsw_add_mc_addr,
27262306a36Sopenharmony_ci			       cpsw_del_mc_addr);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic unsigned int cpsw_rxbuf_total_len(unsigned int len)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	len += CPSW_HEADROOM_NA;
27862306a36Sopenharmony_ci	len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return SKB_DATA_ALIGN(len);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic void cpsw_rx_handler(void *token, int len, int status)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct page *new_page, *page = token;
28662306a36Sopenharmony_ci	void *pa = page_address(page);
28762306a36Sopenharmony_ci	int headroom = CPSW_HEADROOM_NA;
28862306a36Sopenharmony_ci	struct cpsw_meta_xdp *xmeta;
28962306a36Sopenharmony_ci	struct cpsw_common *cpsw;
29062306a36Sopenharmony_ci	struct net_device *ndev;
29162306a36Sopenharmony_ci	int port, ch, pkt_size;
29262306a36Sopenharmony_ci	struct cpsw_priv *priv;
29362306a36Sopenharmony_ci	struct page_pool *pool;
29462306a36Sopenharmony_ci	struct sk_buff *skb;
29562306a36Sopenharmony_ci	struct xdp_buff xdp;
29662306a36Sopenharmony_ci	int ret = 0;
29762306a36Sopenharmony_ci	dma_addr_t dma;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	xmeta = pa + CPSW_XMETA_OFFSET;
30062306a36Sopenharmony_ci	cpsw = ndev_to_cpsw(xmeta->ndev);
30162306a36Sopenharmony_ci	ndev = xmeta->ndev;
30262306a36Sopenharmony_ci	pkt_size = cpsw->rx_packet_max;
30362306a36Sopenharmony_ci	ch = xmeta->ch;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (status >= 0) {
30662306a36Sopenharmony_ci		port = CPDMA_RX_SOURCE_PORT(status);
30762306a36Sopenharmony_ci		if (port)
30862306a36Sopenharmony_ci			ndev = cpsw->slaves[--port].ndev;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	priv = netdev_priv(ndev);
31262306a36Sopenharmony_ci	pool = cpsw->page_pool[ch];
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (unlikely(status < 0) || unlikely(!netif_running(ndev))) {
31562306a36Sopenharmony_ci		/* In dual emac mode check for all interfaces */
31662306a36Sopenharmony_ci		if (cpsw->usage_count && status >= 0) {
31762306a36Sopenharmony_ci			/* The packet received is for the interface which
31862306a36Sopenharmony_ci			 * is already down and the other interface is up
31962306a36Sopenharmony_ci			 * and running, instead of freeing which results
32062306a36Sopenharmony_ci			 * in reducing of the number of rx descriptor in
32162306a36Sopenharmony_ci			 * DMA engine, requeue page back to cpdma.
32262306a36Sopenharmony_ci			 */
32362306a36Sopenharmony_ci			new_page = page;
32462306a36Sopenharmony_ci			goto requeue;
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci		/* the interface is going down, pages are purged */
32862306a36Sopenharmony_ci		page_pool_recycle_direct(pool, page);
32962306a36Sopenharmony_ci		return;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	new_page = page_pool_dev_alloc_pages(pool);
33362306a36Sopenharmony_ci	if (unlikely(!new_page)) {
33462306a36Sopenharmony_ci		new_page = page;
33562306a36Sopenharmony_ci		ndev->stats.rx_dropped++;
33662306a36Sopenharmony_ci		goto requeue;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (priv->xdp_prog) {
34062306a36Sopenharmony_ci		int size = len;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci		xdp_init_buff(&xdp, PAGE_SIZE, &priv->xdp_rxq[ch]);
34362306a36Sopenharmony_ci		if (status & CPDMA_RX_VLAN_ENCAP) {
34462306a36Sopenharmony_ci			headroom += CPSW_RX_VLAN_ENCAP_HDR_SIZE;
34562306a36Sopenharmony_ci			size -= CPSW_RX_VLAN_ENCAP_HDR_SIZE;
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		xdp_prepare_buff(&xdp, pa, headroom, size, false);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		ret = cpsw_run_xdp(priv, ch, &xdp, page, priv->emac_port, &len);
35162306a36Sopenharmony_ci		if (ret != CPSW_XDP_PASS)
35262306a36Sopenharmony_ci			goto requeue;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		headroom = xdp.data - xdp.data_hard_start;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		/* XDP prog can modify vlan tag, so can't use encap header */
35762306a36Sopenharmony_ci		status &= ~CPDMA_RX_VLAN_ENCAP;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* pass skb to netstack if no XDP prog or returned XDP_PASS */
36162306a36Sopenharmony_ci	skb = build_skb(pa, cpsw_rxbuf_total_len(pkt_size));
36262306a36Sopenharmony_ci	if (!skb) {
36362306a36Sopenharmony_ci		ndev->stats.rx_dropped++;
36462306a36Sopenharmony_ci		page_pool_recycle_direct(pool, page);
36562306a36Sopenharmony_ci		goto requeue;
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	skb->offload_fwd_mark = priv->offload_fwd_mark;
36962306a36Sopenharmony_ci	skb_reserve(skb, headroom);
37062306a36Sopenharmony_ci	skb_put(skb, len);
37162306a36Sopenharmony_ci	skb->dev = ndev;
37262306a36Sopenharmony_ci	if (status & CPDMA_RX_VLAN_ENCAP)
37362306a36Sopenharmony_ci		cpsw_rx_vlan_encap(skb);
37462306a36Sopenharmony_ci	if (priv->rx_ts_enabled)
37562306a36Sopenharmony_ci		cpts_rx_timestamp(cpsw->cpts, skb);
37662306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, ndev);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* mark skb for recycling */
37962306a36Sopenharmony_ci	skb_mark_for_recycle(skb);
38062306a36Sopenharmony_ci	netif_receive_skb(skb);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	ndev->stats.rx_bytes += len;
38362306a36Sopenharmony_ci	ndev->stats.rx_packets++;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cirequeue:
38662306a36Sopenharmony_ci	xmeta = page_address(new_page) + CPSW_XMETA_OFFSET;
38762306a36Sopenharmony_ci	xmeta->ndev = ndev;
38862306a36Sopenharmony_ci	xmeta->ch = ch;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	dma = page_pool_get_dma_addr(new_page) + CPSW_HEADROOM_NA;
39162306a36Sopenharmony_ci	ret = cpdma_chan_submit_mapped(cpsw->rxv[ch].ch, new_page, dma,
39262306a36Sopenharmony_ci				       pkt_size, 0);
39362306a36Sopenharmony_ci	if (ret < 0) {
39462306a36Sopenharmony_ci		WARN_ON(ret == -ENOMEM);
39562306a36Sopenharmony_ci		page_pool_recycle_direct(pool, new_page);
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv,
40062306a36Sopenharmony_ci				   unsigned short vid)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
40362306a36Sopenharmony_ci	int unreg_mcast_mask = 0;
40462306a36Sopenharmony_ci	int mcast_mask;
40562306a36Sopenharmony_ci	u32 port_mask;
40662306a36Sopenharmony_ci	int ret;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	port_mask = (1 << priv->emac_port) | ALE_PORT_HOST;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	mcast_mask = ALE_PORT_HOST;
41162306a36Sopenharmony_ci	if (priv->ndev->flags & IFF_ALLMULTI)
41262306a36Sopenharmony_ci		unreg_mcast_mask = mcast_mask;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	ret = cpsw_ale_add_vlan(cpsw->ale, vid, port_mask, 0, port_mask,
41562306a36Sopenharmony_ci				unreg_mcast_mask);
41662306a36Sopenharmony_ci	if (ret != 0)
41762306a36Sopenharmony_ci		return ret;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	ret = cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr,
42062306a36Sopenharmony_ci				 HOST_PORT_NUM, ALE_VLAN, vid);
42162306a36Sopenharmony_ci	if (ret != 0)
42262306a36Sopenharmony_ci		goto clean_vid;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	ret = cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast,
42562306a36Sopenharmony_ci				 mcast_mask, ALE_VLAN, vid, 0);
42662306a36Sopenharmony_ci	if (ret != 0)
42762306a36Sopenharmony_ci		goto clean_vlan_ucast;
42862306a36Sopenharmony_ci	return 0;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ciclean_vlan_ucast:
43162306a36Sopenharmony_ci	cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr,
43262306a36Sopenharmony_ci			   HOST_PORT_NUM, ALE_VLAN, vid);
43362306a36Sopenharmony_ciclean_vid:
43462306a36Sopenharmony_ci	cpsw_ale_del_vlan(cpsw->ale, vid, 0);
43562306a36Sopenharmony_ci	return ret;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev,
43962306a36Sopenharmony_ci				    __be16 proto, u16 vid)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
44262306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
44362306a36Sopenharmony_ci	int ret, i;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (cpsw_is_switch_en(cpsw)) {
44662306a36Sopenharmony_ci		dev_dbg(cpsw->dev, ".ndo_vlan_rx_add_vid called in switch mode\n");
44762306a36Sopenharmony_ci		return 0;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (vid == cpsw->data.default_vlan)
45162306a36Sopenharmony_ci		return 0;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(cpsw->dev);
45462306a36Sopenharmony_ci	if (ret < 0)
45562306a36Sopenharmony_ci		return ret;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* In dual EMAC, reserved VLAN id should not be used for
45862306a36Sopenharmony_ci	 * creating VLAN interfaces as this can break the dual
45962306a36Sopenharmony_ci	 * EMAC port separation
46062306a36Sopenharmony_ci	 */
46162306a36Sopenharmony_ci	for (i = 0; i < cpsw->data.slaves; i++) {
46262306a36Sopenharmony_ci		if (cpsw->slaves[i].ndev &&
46362306a36Sopenharmony_ci		    vid == cpsw->slaves[i].port_vlan) {
46462306a36Sopenharmony_ci			ret = -EINVAL;
46562306a36Sopenharmony_ci			goto err;
46662306a36Sopenharmony_ci		}
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	dev_dbg(priv->dev, "Adding vlanid %d to vlan filter\n", vid);
47062306a36Sopenharmony_ci	ret = cpsw_add_vlan_ale_entry(priv, vid);
47162306a36Sopenharmony_cierr:
47262306a36Sopenharmony_ci	pm_runtime_put(cpsw->dev);
47362306a36Sopenharmony_ci	return ret;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int cpsw_restore_vlans(struct net_device *vdev, int vid, void *arg)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct cpsw_priv *priv = arg;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (!vdev || !vid)
48162306a36Sopenharmony_ci		return 0;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	cpsw_ndo_vlan_rx_add_vid(priv->ndev, 0, vid);
48462306a36Sopenharmony_ci	return 0;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci/* restore resources after port reset */
48862306a36Sopenharmony_cistatic void cpsw_restore(struct cpsw_priv *priv)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/* restore vlan configurations */
49362306a36Sopenharmony_ci	vlan_for_each(priv->ndev, cpsw_restore_vlans, priv);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* restore MQPRIO offload */
49662306a36Sopenharmony_ci	cpsw_mqprio_resume(&cpsw->slaves[priv->emac_port - 1], priv);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	/* restore CBS offload */
49962306a36Sopenharmony_ci	cpsw_cbs_resume(&cpsw->slaves[priv->emac_port - 1], priv);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	cpsw_qos_clsflower_resume(priv);
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic void cpsw_init_stp_ale_entry(struct cpsw_common *cpsw)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	static const char stpa[] = {0x01, 0x80, 0xc2, 0x0, 0x0, 0x0};
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	cpsw_ale_add_mcast(cpsw->ale, stpa,
50962306a36Sopenharmony_ci			   ALE_PORT_HOST, ALE_SUPER, 0,
51062306a36Sopenharmony_ci			   ALE_MCAST_BLOCK_LEARN_FWD);
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic void cpsw_init_host_port_switch(struct cpsw_common *cpsw)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	int vlan = cpsw->data.default_vlan;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	writel(CPSW_FIFO_NORMAL_MODE, &cpsw->host_port_regs->tx_in_ctl);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	writel(vlan, &cpsw->host_port_regs->port_vlan);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	cpsw_ale_add_vlan(cpsw->ale, vlan, ALE_ALL_PORTS,
52262306a36Sopenharmony_ci			  ALE_ALL_PORTS, ALE_ALL_PORTS,
52362306a36Sopenharmony_ci			  ALE_PORT_1 | ALE_PORT_2);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	cpsw_init_stp_ale_entry(cpsw);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_P0_UNI_FLOOD, 1);
52862306a36Sopenharmony_ci	dev_dbg(cpsw->dev, "Set P0_UNI_FLOOD\n");
52962306a36Sopenharmony_ci	cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_PORT_NOLEARN, 0);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic void cpsw_init_host_port_dual_mac(struct cpsw_common *cpsw)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	int vlan = cpsw->data.default_vlan;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	writel(CPSW_FIFO_DUAL_MAC_MODE, &cpsw->host_port_regs->tx_in_ctl);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_P0_UNI_FLOOD, 0);
53962306a36Sopenharmony_ci	dev_dbg(cpsw->dev, "unset P0_UNI_FLOOD\n");
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	writel(vlan, &cpsw->host_port_regs->port_vlan);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	cpsw_ale_add_vlan(cpsw->ale, vlan, ALE_ALL_PORTS, ALE_ALL_PORTS, 0, 0);
54462306a36Sopenharmony_ci	/* learning make no sense in dual_mac mode */
54562306a36Sopenharmony_ci	cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_PORT_NOLEARN, 1);
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic void cpsw_init_host_port(struct cpsw_priv *priv)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
55162306a36Sopenharmony_ci	u32 control_reg;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/* soft reset the controller and initialize ale */
55462306a36Sopenharmony_ci	soft_reset("cpsw", &cpsw->regs->soft_reset);
55562306a36Sopenharmony_ci	cpsw_ale_start(cpsw->ale);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* switch to vlan unaware mode */
55862306a36Sopenharmony_ci	cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_VLAN_AWARE,
55962306a36Sopenharmony_ci			     CPSW_ALE_VLAN_AWARE);
56062306a36Sopenharmony_ci	control_reg = readl(&cpsw->regs->control);
56162306a36Sopenharmony_ci	control_reg |= CPSW_VLAN_AWARE | CPSW_RX_VLAN_ENCAP;
56262306a36Sopenharmony_ci	writel(control_reg, &cpsw->regs->control);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* setup host port priority mapping */
56562306a36Sopenharmony_ci	writel_relaxed(CPDMA_TX_PRIORITY_MAP,
56662306a36Sopenharmony_ci		       &cpsw->host_port_regs->cpdma_tx_pri_map);
56762306a36Sopenharmony_ci	writel_relaxed(0, &cpsw->host_port_regs->cpdma_rx_chan_map);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/* disable priority elevation */
57062306a36Sopenharmony_ci	writel_relaxed(0, &cpsw->regs->ptype);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* enable statistics collection only on all ports */
57362306a36Sopenharmony_ci	writel_relaxed(0x7, &cpsw->regs->stat_port_en);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* Enable internal fifo flow control */
57662306a36Sopenharmony_ci	writel(0x7, &cpsw->regs->flow_control);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	if (cpsw_is_switch_en(cpsw))
57962306a36Sopenharmony_ci		cpsw_init_host_port_switch(cpsw);
58062306a36Sopenharmony_ci	else
58162306a36Sopenharmony_ci		cpsw_init_host_port_dual_mac(cpsw);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM,
58462306a36Sopenharmony_ci			     ALE_PORT_STATE, ALE_PORT_STATE_FORWARD);
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic void cpsw_port_add_dual_emac_def_ale_entries(struct cpsw_priv *priv,
58862306a36Sopenharmony_ci						    struct cpsw_slave *slave)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	u32 port_mask = 1 << priv->emac_port | ALE_PORT_HOST;
59162306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
59262306a36Sopenharmony_ci	u32 reg;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	reg = (cpsw->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN :
59562306a36Sopenharmony_ci	       CPSW2_PORT_VLAN;
59662306a36Sopenharmony_ci	slave_write(slave, slave->port_vlan, reg);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	cpsw_ale_add_vlan(cpsw->ale, slave->port_vlan, port_mask,
59962306a36Sopenharmony_ci			  port_mask, port_mask, 0);
60062306a36Sopenharmony_ci	cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast,
60162306a36Sopenharmony_ci			   ALE_PORT_HOST, ALE_VLAN, slave->port_vlan,
60262306a36Sopenharmony_ci			   ALE_MCAST_FWD);
60362306a36Sopenharmony_ci	cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr,
60462306a36Sopenharmony_ci			   HOST_PORT_NUM, ALE_VLAN |
60562306a36Sopenharmony_ci			   ALE_SECURE, slave->port_vlan);
60662306a36Sopenharmony_ci	cpsw_ale_control_set(cpsw->ale, priv->emac_port,
60762306a36Sopenharmony_ci			     ALE_PORT_DROP_UNKNOWN_VLAN, 1);
60862306a36Sopenharmony_ci	/* learning make no sense in dual_mac mode */
60962306a36Sopenharmony_ci	cpsw_ale_control_set(cpsw->ale, priv->emac_port,
61062306a36Sopenharmony_ci			     ALE_PORT_NOLEARN, 1);
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic void cpsw_port_add_switch_def_ale_entries(struct cpsw_priv *priv,
61462306a36Sopenharmony_ci						 struct cpsw_slave *slave)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	u32 port_mask = 1 << priv->emac_port | ALE_PORT_HOST;
61762306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
61862306a36Sopenharmony_ci	u32 reg;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	cpsw_ale_control_set(cpsw->ale, priv->emac_port,
62162306a36Sopenharmony_ci			     ALE_PORT_DROP_UNKNOWN_VLAN, 0);
62262306a36Sopenharmony_ci	cpsw_ale_control_set(cpsw->ale, priv->emac_port,
62362306a36Sopenharmony_ci			     ALE_PORT_NOLEARN, 0);
62462306a36Sopenharmony_ci	/* disabling SA_UPDATE required to make stp work, without this setting
62562306a36Sopenharmony_ci	 * Host MAC addresses will jump between ports.
62662306a36Sopenharmony_ci	 * As per TRM MAC address can be defined as unicast supervisory (super)
62762306a36Sopenharmony_ci	 * by setting both (ALE_BLOCKED | ALE_SECURE) which should prevent
62862306a36Sopenharmony_ci	 * SA_UPDATE, but HW seems works incorrectly and setting ALE_SECURE
62962306a36Sopenharmony_ci	 * causes STP packets to be dropped due to ingress filter
63062306a36Sopenharmony_ci	 *	if (source address found) and (secure) and
63162306a36Sopenharmony_ci	 *	   (receive port number != port_number))
63262306a36Sopenharmony_ci	 *	   then discard the packet
63362306a36Sopenharmony_ci	 */
63462306a36Sopenharmony_ci	cpsw_ale_control_set(cpsw->ale, priv->emac_port,
63562306a36Sopenharmony_ci			     ALE_PORT_NO_SA_UPDATE, 1);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast,
63862306a36Sopenharmony_ci			   port_mask, ALE_VLAN, slave->port_vlan,
63962306a36Sopenharmony_ci			   ALE_MCAST_FWD_2);
64062306a36Sopenharmony_ci	cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr,
64162306a36Sopenharmony_ci			   HOST_PORT_NUM, ALE_VLAN, slave->port_vlan);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	reg = (cpsw->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN :
64462306a36Sopenharmony_ci	       CPSW2_PORT_VLAN;
64562306a36Sopenharmony_ci	slave_write(slave, slave->port_vlan, reg);
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic void cpsw_adjust_link(struct net_device *ndev)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
65162306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
65262306a36Sopenharmony_ci	struct cpsw_slave *slave;
65362306a36Sopenharmony_ci	struct phy_device *phy;
65462306a36Sopenharmony_ci	u32 mac_control = 0;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	slave = &cpsw->slaves[priv->emac_port - 1];
65762306a36Sopenharmony_ci	phy = slave->phy;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	if (!phy)
66062306a36Sopenharmony_ci		return;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (phy->link) {
66362306a36Sopenharmony_ci		mac_control = CPSW_SL_CTL_GMII_EN;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		if (phy->speed == 1000)
66662306a36Sopenharmony_ci			mac_control |= CPSW_SL_CTL_GIG;
66762306a36Sopenharmony_ci		if (phy->duplex)
66862306a36Sopenharmony_ci			mac_control |= CPSW_SL_CTL_FULLDUPLEX;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci		/* set speed_in input in case RMII mode is used in 100Mbps */
67162306a36Sopenharmony_ci		if (phy->speed == 100)
67262306a36Sopenharmony_ci			mac_control |= CPSW_SL_CTL_IFCTL_A;
67362306a36Sopenharmony_ci		/* in band mode only works in 10Mbps RGMII mode */
67462306a36Sopenharmony_ci		else if ((phy->speed == 10) && phy_interface_is_rgmii(phy))
67562306a36Sopenharmony_ci			mac_control |= CPSW_SL_CTL_EXT_EN; /* In Band mode */
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		if (priv->rx_pause)
67862306a36Sopenharmony_ci			mac_control |= CPSW_SL_CTL_RX_FLOW_EN;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci		if (priv->tx_pause)
68162306a36Sopenharmony_ci			mac_control |= CPSW_SL_CTL_TX_FLOW_EN;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		if (mac_control != slave->mac_control)
68462306a36Sopenharmony_ci			cpsw_sl_ctl_set(slave->mac_sl, mac_control);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		/* enable forwarding */
68762306a36Sopenharmony_ci		cpsw_ale_control_set(cpsw->ale, priv->emac_port,
68862306a36Sopenharmony_ci				     ALE_PORT_STATE, ALE_PORT_STATE_FORWARD);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci		netif_tx_wake_all_queues(ndev);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		if (priv->shp_cfg_speed &&
69362306a36Sopenharmony_ci		    priv->shp_cfg_speed != slave->phy->speed &&
69462306a36Sopenharmony_ci		    !cpsw_shp_is_off(priv))
69562306a36Sopenharmony_ci			dev_warn(priv->dev, "Speed was changed, CBS shaper speeds are changed!");
69662306a36Sopenharmony_ci	} else {
69762306a36Sopenharmony_ci		netif_tx_stop_all_queues(ndev);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci		mac_control = 0;
70062306a36Sopenharmony_ci		/* disable forwarding */
70162306a36Sopenharmony_ci		cpsw_ale_control_set(cpsw->ale, priv->emac_port,
70262306a36Sopenharmony_ci				     ALE_PORT_STATE, ALE_PORT_STATE_DISABLE);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		cpsw_sl_wait_for_idle(slave->mac_sl, 100);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci		cpsw_sl_ctl_reset(slave->mac_sl);
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (mac_control != slave->mac_control)
71062306a36Sopenharmony_ci		phy_print_status(phy);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	slave->mac_control = mac_control;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (phy->link && cpsw_need_resplit(cpsw))
71562306a36Sopenharmony_ci		cpsw_split_res(cpsw);
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_cistatic void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
72162306a36Sopenharmony_ci	struct phy_device *phy;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	cpsw_sl_reset(slave->mac_sl, 100);
72462306a36Sopenharmony_ci	cpsw_sl_ctl_reset(slave->mac_sl);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* setup priority mapping */
72762306a36Sopenharmony_ci	cpsw_sl_reg_write(slave->mac_sl, CPSW_SL_RX_PRI_MAP,
72862306a36Sopenharmony_ci			  RX_PRIORITY_MAPPING);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	switch (cpsw->version) {
73162306a36Sopenharmony_ci	case CPSW_VERSION_1:
73262306a36Sopenharmony_ci		slave_write(slave, TX_PRIORITY_MAPPING, CPSW1_TX_PRI_MAP);
73362306a36Sopenharmony_ci		/* Increase RX FIFO size to 5 for supporting fullduplex
73462306a36Sopenharmony_ci		 * flow control mode
73562306a36Sopenharmony_ci		 */
73662306a36Sopenharmony_ci		slave_write(slave,
73762306a36Sopenharmony_ci			    (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) |
73862306a36Sopenharmony_ci			    CPSW_MAX_BLKS_RX, CPSW1_MAX_BLKS);
73962306a36Sopenharmony_ci		break;
74062306a36Sopenharmony_ci	case CPSW_VERSION_2:
74162306a36Sopenharmony_ci	case CPSW_VERSION_3:
74262306a36Sopenharmony_ci	case CPSW_VERSION_4:
74362306a36Sopenharmony_ci		slave_write(slave, TX_PRIORITY_MAPPING, CPSW2_TX_PRI_MAP);
74462306a36Sopenharmony_ci		/* Increase RX FIFO size to 5 for supporting fullduplex
74562306a36Sopenharmony_ci		 * flow control mode
74662306a36Sopenharmony_ci		 */
74762306a36Sopenharmony_ci		slave_write(slave,
74862306a36Sopenharmony_ci			    (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) |
74962306a36Sopenharmony_ci			    CPSW_MAX_BLKS_RX, CPSW2_MAX_BLKS);
75062306a36Sopenharmony_ci		break;
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	/* setup max packet size, and mac address */
75462306a36Sopenharmony_ci	cpsw_sl_reg_write(slave->mac_sl, CPSW_SL_RX_MAXLEN,
75562306a36Sopenharmony_ci			  cpsw->rx_packet_max);
75662306a36Sopenharmony_ci	cpsw_set_slave_mac(slave, priv);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	slave->mac_control = 0;	/* no link yet */
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	if (cpsw_is_switch_en(cpsw))
76162306a36Sopenharmony_ci		cpsw_port_add_switch_def_ale_entries(priv, slave);
76262306a36Sopenharmony_ci	else
76362306a36Sopenharmony_ci		cpsw_port_add_dual_emac_def_ale_entries(priv, slave);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (!slave->data->phy_node)
76662306a36Sopenharmony_ci		dev_err(priv->dev, "no phy found on slave %d\n",
76762306a36Sopenharmony_ci			slave->slave_num);
76862306a36Sopenharmony_ci	phy = of_phy_connect(priv->ndev, slave->data->phy_node,
76962306a36Sopenharmony_ci			     &cpsw_adjust_link, 0, slave->data->phy_if);
77062306a36Sopenharmony_ci	if (!phy) {
77162306a36Sopenharmony_ci		dev_err(priv->dev, "phy \"%pOF\" not found on slave %d\n",
77262306a36Sopenharmony_ci			slave->data->phy_node,
77362306a36Sopenharmony_ci			slave->slave_num);
77462306a36Sopenharmony_ci		return;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	phy->mac_managed_pm = true;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	slave->phy = phy;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	phy_attached_info(slave->phy);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	phy_start(slave->phy);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	/* Configure GMII_SEL register */
78662306a36Sopenharmony_ci	phy_set_mode_ext(slave->data->ifphy, PHY_MODE_ETHERNET,
78762306a36Sopenharmony_ci			 slave->data->phy_if);
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic int cpsw_ndo_stop(struct net_device *ndev)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
79362306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
79462306a36Sopenharmony_ci	struct cpsw_slave *slave;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	cpsw_info(priv, ifdown, "shutting down ndev\n");
79762306a36Sopenharmony_ci	slave = &cpsw->slaves[priv->emac_port - 1];
79862306a36Sopenharmony_ci	if (slave->phy)
79962306a36Sopenharmony_ci		phy_stop(slave->phy);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	netif_tx_stop_all_queues(priv->ndev);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	if (slave->phy) {
80462306a36Sopenharmony_ci		phy_disconnect(slave->phy);
80562306a36Sopenharmony_ci		slave->phy = NULL;
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	__hw_addr_ref_unsync_dev(&ndev->mc, ndev, cpsw_purge_all_mc);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	if (cpsw->usage_count <= 1) {
81162306a36Sopenharmony_ci		napi_disable(&cpsw->napi_rx);
81262306a36Sopenharmony_ci		napi_disable(&cpsw->napi_tx);
81362306a36Sopenharmony_ci		cpts_unregister(cpsw->cpts);
81462306a36Sopenharmony_ci		cpsw_intr_disable(cpsw);
81562306a36Sopenharmony_ci		cpdma_ctlr_stop(cpsw->dma);
81662306a36Sopenharmony_ci		cpsw_ale_stop(cpsw->ale);
81762306a36Sopenharmony_ci		cpsw_destroy_xdp_rxqs(cpsw);
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	if (cpsw_need_resplit(cpsw))
82162306a36Sopenharmony_ci		cpsw_split_res(cpsw);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	cpsw->usage_count--;
82462306a36Sopenharmony_ci	pm_runtime_put_sync(cpsw->dev);
82562306a36Sopenharmony_ci	return 0;
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic int cpsw_ndo_open(struct net_device *ndev)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
83162306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
83262306a36Sopenharmony_ci	int ret;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	dev_info(priv->dev, "starting ndev. mode: %s\n",
83562306a36Sopenharmony_ci		 cpsw_is_switch_en(cpsw) ? "switch" : "dual_mac");
83662306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(cpsw->dev);
83762306a36Sopenharmony_ci	if (ret < 0)
83862306a36Sopenharmony_ci		return ret;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	/* Notify the stack of the actual queue counts. */
84162306a36Sopenharmony_ci	ret = netif_set_real_num_tx_queues(ndev, cpsw->tx_ch_num);
84262306a36Sopenharmony_ci	if (ret) {
84362306a36Sopenharmony_ci		dev_err(priv->dev, "cannot set real number of tx queues\n");
84462306a36Sopenharmony_ci		goto pm_cleanup;
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	ret = netif_set_real_num_rx_queues(ndev, cpsw->rx_ch_num);
84862306a36Sopenharmony_ci	if (ret) {
84962306a36Sopenharmony_ci		dev_err(priv->dev, "cannot set real number of rx queues\n");
85062306a36Sopenharmony_ci		goto pm_cleanup;
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	/* Initialize host and slave ports */
85462306a36Sopenharmony_ci	if (!cpsw->usage_count)
85562306a36Sopenharmony_ci		cpsw_init_host_port(priv);
85662306a36Sopenharmony_ci	cpsw_slave_open(&cpsw->slaves[priv->emac_port - 1], priv);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	/* initialize shared resources for every ndev */
85962306a36Sopenharmony_ci	if (!cpsw->usage_count) {
86062306a36Sopenharmony_ci		/* create rxqs for both infs in dual mac as they use same pool
86162306a36Sopenharmony_ci		 * and must be destroyed together when no users.
86262306a36Sopenharmony_ci		 */
86362306a36Sopenharmony_ci		ret = cpsw_create_xdp_rxqs(cpsw);
86462306a36Sopenharmony_ci		if (ret < 0)
86562306a36Sopenharmony_ci			goto err_cleanup;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci		ret = cpsw_fill_rx_channels(priv);
86862306a36Sopenharmony_ci		if (ret < 0)
86962306a36Sopenharmony_ci			goto err_cleanup;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		if (cpsw->cpts) {
87262306a36Sopenharmony_ci			if (cpts_register(cpsw->cpts))
87362306a36Sopenharmony_ci				dev_err(priv->dev, "error registering cpts device\n");
87462306a36Sopenharmony_ci			else
87562306a36Sopenharmony_ci				writel(0x10, &cpsw->wr_regs->misc_en);
87662306a36Sopenharmony_ci		}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci		napi_enable(&cpsw->napi_rx);
87962306a36Sopenharmony_ci		napi_enable(&cpsw->napi_tx);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		if (cpsw->tx_irq_disabled) {
88262306a36Sopenharmony_ci			cpsw->tx_irq_disabled = false;
88362306a36Sopenharmony_ci			enable_irq(cpsw->irqs_table[1]);
88462306a36Sopenharmony_ci		}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci		if (cpsw->rx_irq_disabled) {
88762306a36Sopenharmony_ci			cpsw->rx_irq_disabled = false;
88862306a36Sopenharmony_ci			enable_irq(cpsw->irqs_table[0]);
88962306a36Sopenharmony_ci		}
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	cpsw_restore(priv);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* Enable Interrupt pacing if configured */
89562306a36Sopenharmony_ci	if (cpsw->coal_intvl != 0) {
89662306a36Sopenharmony_ci		struct ethtool_coalesce coal;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci		coal.rx_coalesce_usecs = cpsw->coal_intvl;
89962306a36Sopenharmony_ci		cpsw_set_coalesce(ndev, &coal, NULL, NULL);
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	cpdma_ctlr_start(cpsw->dma);
90362306a36Sopenharmony_ci	cpsw_intr_enable(cpsw);
90462306a36Sopenharmony_ci	cpsw->usage_count++;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	return 0;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cierr_cleanup:
90962306a36Sopenharmony_ci	cpsw_ndo_stop(ndev);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cipm_cleanup:
91262306a36Sopenharmony_ci	pm_runtime_put_sync(cpsw->dev);
91362306a36Sopenharmony_ci	return ret;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
91762306a36Sopenharmony_ci				       struct net_device *ndev)
91862306a36Sopenharmony_ci{
91962306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
92062306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
92162306a36Sopenharmony_ci	struct cpts *cpts = cpsw->cpts;
92262306a36Sopenharmony_ci	struct netdev_queue *txq;
92362306a36Sopenharmony_ci	struct cpdma_chan *txch;
92462306a36Sopenharmony_ci	int ret, q_idx;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	if (skb_put_padto(skb, READ_ONCE(priv->tx_packet_min))) {
92762306a36Sopenharmony_ci		cpsw_err(priv, tx_err, "packet pad failed\n");
92862306a36Sopenharmony_ci		ndev->stats.tx_dropped++;
92962306a36Sopenharmony_ci		return NET_XMIT_DROP;
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
93362306a36Sopenharmony_ci	    priv->tx_ts_enabled && cpts_can_timestamp(cpts, skb))
93462306a36Sopenharmony_ci		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	q_idx = skb_get_queue_mapping(skb);
93762306a36Sopenharmony_ci	if (q_idx >= cpsw->tx_ch_num)
93862306a36Sopenharmony_ci		q_idx = q_idx % cpsw->tx_ch_num;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	txch = cpsw->txv[q_idx].ch;
94162306a36Sopenharmony_ci	txq = netdev_get_tx_queue(ndev, q_idx);
94262306a36Sopenharmony_ci	skb_tx_timestamp(skb);
94362306a36Sopenharmony_ci	ret = cpdma_chan_submit(txch, skb, skb->data, skb->len,
94462306a36Sopenharmony_ci				priv->emac_port);
94562306a36Sopenharmony_ci	if (unlikely(ret != 0)) {
94662306a36Sopenharmony_ci		cpsw_err(priv, tx_err, "desc submit failed\n");
94762306a36Sopenharmony_ci		goto fail;
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/* If there is no more tx desc left free then we need to
95162306a36Sopenharmony_ci	 * tell the kernel to stop sending us tx frames.
95262306a36Sopenharmony_ci	 */
95362306a36Sopenharmony_ci	if (unlikely(!cpdma_check_free_tx_desc(txch))) {
95462306a36Sopenharmony_ci		netif_tx_stop_queue(txq);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci		/* Barrier, so that stop_queue visible to other cpus */
95762306a36Sopenharmony_ci		smp_mb__after_atomic();
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci		if (cpdma_check_free_tx_desc(txch))
96062306a36Sopenharmony_ci			netif_tx_wake_queue(txq);
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	return NETDEV_TX_OK;
96462306a36Sopenharmony_cifail:
96562306a36Sopenharmony_ci	ndev->stats.tx_dropped++;
96662306a36Sopenharmony_ci	netif_tx_stop_queue(txq);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	/* Barrier, so that stop_queue visible to other cpus */
96962306a36Sopenharmony_ci	smp_mb__after_atomic();
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	if (cpdma_check_free_tx_desc(txch))
97262306a36Sopenharmony_ci		netif_tx_wake_queue(txq);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	return NETDEV_TX_BUSY;
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_cistatic int cpsw_ndo_set_mac_address(struct net_device *ndev, void *p)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	struct sockaddr *addr = (struct sockaddr *)p;
98062306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
98162306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
98262306a36Sopenharmony_ci	int ret, slave_no;
98362306a36Sopenharmony_ci	int flags = 0;
98462306a36Sopenharmony_ci	u16 vid = 0;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	slave_no = cpsw_slave_index(cpsw, priv);
98762306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
98862306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(cpsw->dev);
99162306a36Sopenharmony_ci	if (ret < 0)
99262306a36Sopenharmony_ci		return ret;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	vid = cpsw->slaves[slave_no].port_vlan;
99562306a36Sopenharmony_ci	flags = ALE_VLAN | ALE_SECURE;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr, HOST_PORT_NUM,
99862306a36Sopenharmony_ci			   flags, vid);
99962306a36Sopenharmony_ci	cpsw_ale_add_ucast(cpsw->ale, addr->sa_data, HOST_PORT_NUM,
100062306a36Sopenharmony_ci			   flags, vid);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	ether_addr_copy(priv->mac_addr, addr->sa_data);
100362306a36Sopenharmony_ci	eth_hw_addr_set(ndev, priv->mac_addr);
100462306a36Sopenharmony_ci	cpsw_set_slave_mac(&cpsw->slaves[slave_no], priv);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	pm_runtime_put(cpsw->dev);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	return 0;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev,
101262306a36Sopenharmony_ci				     __be16 proto, u16 vid)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
101562306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
101662306a36Sopenharmony_ci	int ret;
101762306a36Sopenharmony_ci	int i;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (cpsw_is_switch_en(cpsw)) {
102062306a36Sopenharmony_ci		dev_dbg(cpsw->dev, "ndo del vlan is called in switch mode\n");
102162306a36Sopenharmony_ci		return 0;
102262306a36Sopenharmony_ci	}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	if (vid == cpsw->data.default_vlan)
102562306a36Sopenharmony_ci		return 0;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(cpsw->dev);
102862306a36Sopenharmony_ci	if (ret < 0)
102962306a36Sopenharmony_ci		return ret;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	/* reset the return code as pm_runtime_get_sync() can return
103262306a36Sopenharmony_ci	 * non zero values as well.
103362306a36Sopenharmony_ci	 */
103462306a36Sopenharmony_ci	ret = 0;
103562306a36Sopenharmony_ci	for (i = 0; i < cpsw->data.slaves; i++) {
103662306a36Sopenharmony_ci		if (cpsw->slaves[i].ndev &&
103762306a36Sopenharmony_ci		    vid == cpsw->slaves[i].port_vlan) {
103862306a36Sopenharmony_ci			ret = -EINVAL;
103962306a36Sopenharmony_ci			goto err;
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	dev_dbg(priv->dev, "removing vlanid %d from vlan filter\n", vid);
104462306a36Sopenharmony_ci	ret = cpsw_ale_del_vlan(cpsw->ale, vid, 0);
104562306a36Sopenharmony_ci	if (ret)
104662306a36Sopenharmony_ci		dev_err(priv->dev, "cpsw_ale_del_vlan() failed: ret %d\n", ret);
104762306a36Sopenharmony_ci	ret = cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr,
104862306a36Sopenharmony_ci				 HOST_PORT_NUM, ALE_VLAN, vid);
104962306a36Sopenharmony_ci	if (ret)
105062306a36Sopenharmony_ci		dev_err(priv->dev, "cpsw_ale_del_ucast() failed: ret %d\n",
105162306a36Sopenharmony_ci			ret);
105262306a36Sopenharmony_ci	ret = cpsw_ale_del_mcast(cpsw->ale, priv->ndev->broadcast,
105362306a36Sopenharmony_ci				 0, ALE_VLAN, vid);
105462306a36Sopenharmony_ci	if (ret)
105562306a36Sopenharmony_ci		dev_err(priv->dev, "cpsw_ale_del_mcast failed. ret %d\n",
105662306a36Sopenharmony_ci			ret);
105762306a36Sopenharmony_ci	cpsw_ale_flush_multicast(cpsw->ale, ALE_PORT_HOST, vid);
105862306a36Sopenharmony_ci	ret = 0;
105962306a36Sopenharmony_cierr:
106062306a36Sopenharmony_ci	pm_runtime_put(cpsw->dev);
106162306a36Sopenharmony_ci	return ret;
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_cistatic int cpsw_ndo_get_phys_port_name(struct net_device *ndev, char *name,
106562306a36Sopenharmony_ci				       size_t len)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
106862306a36Sopenharmony_ci	int err;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	err = snprintf(name, len, "p%d", priv->emac_port);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	if (err >= len)
107362306a36Sopenharmony_ci		return -EINVAL;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	return 0;
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
107962306a36Sopenharmony_cistatic void cpsw_ndo_poll_controller(struct net_device *ndev)
108062306a36Sopenharmony_ci{
108162306a36Sopenharmony_ci	struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	cpsw_intr_disable(cpsw);
108462306a36Sopenharmony_ci	cpsw_rx_interrupt(cpsw->irqs_table[0], cpsw);
108562306a36Sopenharmony_ci	cpsw_tx_interrupt(cpsw->irqs_table[1], cpsw);
108662306a36Sopenharmony_ci	cpsw_intr_enable(cpsw);
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci#endif
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_cistatic int cpsw_ndo_xdp_xmit(struct net_device *ndev, int n,
109162306a36Sopenharmony_ci			     struct xdp_frame **frames, u32 flags)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
109462306a36Sopenharmony_ci	struct xdp_frame *xdpf;
109562306a36Sopenharmony_ci	int i, nxmit = 0;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
109862306a36Sopenharmony_ci		return -EINVAL;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
110162306a36Sopenharmony_ci		xdpf = frames[i];
110262306a36Sopenharmony_ci		if (xdpf->len < READ_ONCE(priv->tx_packet_min))
110362306a36Sopenharmony_ci			break;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		if (cpsw_xdp_tx_frame(priv, xdpf, NULL, priv->emac_port))
110662306a36Sopenharmony_ci			break;
110762306a36Sopenharmony_ci		nxmit++;
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	return nxmit;
111162306a36Sopenharmony_ci}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_cistatic int cpsw_get_port_parent_id(struct net_device *ndev,
111462306a36Sopenharmony_ci				   struct netdev_phys_item_id *ppid)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	ppid->id_len = sizeof(cpsw->base_mac);
111962306a36Sopenharmony_ci	memcpy(&ppid->id, &cpsw->base_mac, ppid->id_len);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	return 0;
112262306a36Sopenharmony_ci}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cistatic const struct net_device_ops cpsw_netdev_ops = {
112562306a36Sopenharmony_ci	.ndo_open		= cpsw_ndo_open,
112662306a36Sopenharmony_ci	.ndo_stop		= cpsw_ndo_stop,
112762306a36Sopenharmony_ci	.ndo_start_xmit		= cpsw_ndo_start_xmit,
112862306a36Sopenharmony_ci	.ndo_set_mac_address	= cpsw_ndo_set_mac_address,
112962306a36Sopenharmony_ci	.ndo_eth_ioctl		= cpsw_ndo_ioctl,
113062306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
113162306a36Sopenharmony_ci	.ndo_tx_timeout		= cpsw_ndo_tx_timeout,
113262306a36Sopenharmony_ci	.ndo_set_rx_mode	= cpsw_ndo_set_rx_mode,
113362306a36Sopenharmony_ci	.ndo_set_tx_maxrate	= cpsw_ndo_set_tx_maxrate,
113462306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
113562306a36Sopenharmony_ci	.ndo_poll_controller	= cpsw_ndo_poll_controller,
113662306a36Sopenharmony_ci#endif
113762306a36Sopenharmony_ci	.ndo_vlan_rx_add_vid	= cpsw_ndo_vlan_rx_add_vid,
113862306a36Sopenharmony_ci	.ndo_vlan_rx_kill_vid	= cpsw_ndo_vlan_rx_kill_vid,
113962306a36Sopenharmony_ci	.ndo_setup_tc           = cpsw_ndo_setup_tc,
114062306a36Sopenharmony_ci	.ndo_get_phys_port_name = cpsw_ndo_get_phys_port_name,
114162306a36Sopenharmony_ci	.ndo_bpf		= cpsw_ndo_bpf,
114262306a36Sopenharmony_ci	.ndo_xdp_xmit		= cpsw_ndo_xdp_xmit,
114362306a36Sopenharmony_ci	.ndo_get_port_parent_id	= cpsw_get_port_parent_id,
114462306a36Sopenharmony_ci};
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_cistatic void cpsw_get_drvinfo(struct net_device *ndev,
114762306a36Sopenharmony_ci			     struct ethtool_drvinfo *info)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
115062306a36Sopenharmony_ci	struct platform_device *pdev;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	pdev = to_platform_device(cpsw->dev);
115362306a36Sopenharmony_ci	strscpy(info->driver, "cpsw-switch", sizeof(info->driver));
115462306a36Sopenharmony_ci	strscpy(info->version, "2.0", sizeof(info->version));
115562306a36Sopenharmony_ci	strscpy(info->bus_info, pdev->name, sizeof(info->bus_info));
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic int cpsw_set_pauseparam(struct net_device *ndev,
115962306a36Sopenharmony_ci			       struct ethtool_pauseparam *pause)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
116262306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
116362306a36Sopenharmony_ci	int slave_no;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	slave_no = cpsw_slave_index(cpsw, priv);
116662306a36Sopenharmony_ci	if (!cpsw->slaves[slave_no].phy)
116762306a36Sopenharmony_ci		return -EINVAL;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	if (!phy_validate_pause(cpsw->slaves[slave_no].phy, pause))
117062306a36Sopenharmony_ci		return -EINVAL;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	priv->rx_pause = pause->rx_pause ? true : false;
117362306a36Sopenharmony_ci	priv->tx_pause = pause->tx_pause ? true : false;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	phy_set_asym_pause(cpsw->slaves[slave_no].phy,
117662306a36Sopenharmony_ci			   priv->rx_pause, priv->tx_pause);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	return 0;
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_cistatic int cpsw_set_channels(struct net_device *ndev,
118262306a36Sopenharmony_ci			     struct ethtool_channels *chs)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	return cpsw_set_channels_common(ndev, chs, cpsw_rx_handler);
118562306a36Sopenharmony_ci}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_cistatic const struct ethtool_ops cpsw_ethtool_ops = {
118862306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS,
118962306a36Sopenharmony_ci	.get_drvinfo		= cpsw_get_drvinfo,
119062306a36Sopenharmony_ci	.get_msglevel		= cpsw_get_msglevel,
119162306a36Sopenharmony_ci	.set_msglevel		= cpsw_set_msglevel,
119262306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
119362306a36Sopenharmony_ci	.get_ts_info		= cpsw_get_ts_info,
119462306a36Sopenharmony_ci	.get_coalesce		= cpsw_get_coalesce,
119562306a36Sopenharmony_ci	.set_coalesce		= cpsw_set_coalesce,
119662306a36Sopenharmony_ci	.get_sset_count		= cpsw_get_sset_count,
119762306a36Sopenharmony_ci	.get_strings		= cpsw_get_strings,
119862306a36Sopenharmony_ci	.get_ethtool_stats	= cpsw_get_ethtool_stats,
119962306a36Sopenharmony_ci	.get_pauseparam		= cpsw_get_pauseparam,
120062306a36Sopenharmony_ci	.set_pauseparam		= cpsw_set_pauseparam,
120162306a36Sopenharmony_ci	.get_wol		= cpsw_get_wol,
120262306a36Sopenharmony_ci	.set_wol		= cpsw_set_wol,
120362306a36Sopenharmony_ci	.get_regs_len		= cpsw_get_regs_len,
120462306a36Sopenharmony_ci	.get_regs		= cpsw_get_regs,
120562306a36Sopenharmony_ci	.begin			= cpsw_ethtool_op_begin,
120662306a36Sopenharmony_ci	.complete		= cpsw_ethtool_op_complete,
120762306a36Sopenharmony_ci	.get_channels		= cpsw_get_channels,
120862306a36Sopenharmony_ci	.set_channels		= cpsw_set_channels,
120962306a36Sopenharmony_ci	.get_link_ksettings	= cpsw_get_link_ksettings,
121062306a36Sopenharmony_ci	.set_link_ksettings	= cpsw_set_link_ksettings,
121162306a36Sopenharmony_ci	.get_eee		= cpsw_get_eee,
121262306a36Sopenharmony_ci	.set_eee		= cpsw_set_eee,
121362306a36Sopenharmony_ci	.nway_reset		= cpsw_nway_reset,
121462306a36Sopenharmony_ci	.get_ringparam		= cpsw_get_ringparam,
121562306a36Sopenharmony_ci	.set_ringparam		= cpsw_set_ringparam,
121662306a36Sopenharmony_ci};
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_cistatic int cpsw_probe_dt(struct cpsw_common *cpsw)
121962306a36Sopenharmony_ci{
122062306a36Sopenharmony_ci	struct device_node *node = cpsw->dev->of_node, *tmp_node, *port_np;
122162306a36Sopenharmony_ci	struct cpsw_platform_data *data = &cpsw->data;
122262306a36Sopenharmony_ci	struct device *dev = cpsw->dev;
122362306a36Sopenharmony_ci	int ret;
122462306a36Sopenharmony_ci	u32 prop;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	if (!node)
122762306a36Sopenharmony_ci		return -EINVAL;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	tmp_node = of_get_child_by_name(node, "ethernet-ports");
123062306a36Sopenharmony_ci	if (!tmp_node)
123162306a36Sopenharmony_ci		return -ENOENT;
123262306a36Sopenharmony_ci	data->slaves = of_get_child_count(tmp_node);
123362306a36Sopenharmony_ci	if (data->slaves != CPSW_SLAVE_PORTS_NUM) {
123462306a36Sopenharmony_ci		of_node_put(tmp_node);
123562306a36Sopenharmony_ci		return -ENOENT;
123662306a36Sopenharmony_ci	}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	data->active_slave = 0;
123962306a36Sopenharmony_ci	data->channels = CPSW_MAX_QUEUES;
124062306a36Sopenharmony_ci	data->dual_emac = true;
124162306a36Sopenharmony_ci	data->bd_ram_size = CPSW_BD_RAM_SIZE;
124262306a36Sopenharmony_ci	data->mac_control = 0;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	data->slave_data = devm_kcalloc(dev, CPSW_SLAVE_PORTS_NUM,
124562306a36Sopenharmony_ci					sizeof(struct cpsw_slave_data),
124662306a36Sopenharmony_ci					GFP_KERNEL);
124762306a36Sopenharmony_ci	if (!data->slave_data) {
124862306a36Sopenharmony_ci		of_node_put(tmp_node);
124962306a36Sopenharmony_ci		return -ENOMEM;
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	/* Populate all the child nodes here...
125362306a36Sopenharmony_ci	 */
125462306a36Sopenharmony_ci	ret = devm_of_platform_populate(dev);
125562306a36Sopenharmony_ci	/* We do not want to force this, as in some cases may not have child */
125662306a36Sopenharmony_ci	if (ret)
125762306a36Sopenharmony_ci		dev_warn(dev, "Doesn't have any child node\n");
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	for_each_child_of_node(tmp_node, port_np) {
126062306a36Sopenharmony_ci		struct cpsw_slave_data *slave_data;
126162306a36Sopenharmony_ci		u32 port_id;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci		ret = of_property_read_u32(port_np, "reg", &port_id);
126462306a36Sopenharmony_ci		if (ret < 0) {
126562306a36Sopenharmony_ci			dev_err(dev, "%pOF error reading port_id %d\n",
126662306a36Sopenharmony_ci				port_np, ret);
126762306a36Sopenharmony_ci			goto err_node_put;
126862306a36Sopenharmony_ci		}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci		if (!port_id || port_id > CPSW_SLAVE_PORTS_NUM) {
127162306a36Sopenharmony_ci			dev_err(dev, "%pOF has invalid port_id %u\n",
127262306a36Sopenharmony_ci				port_np, port_id);
127362306a36Sopenharmony_ci			ret = -EINVAL;
127462306a36Sopenharmony_ci			goto err_node_put;
127562306a36Sopenharmony_ci		}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci		slave_data = &data->slave_data[port_id - 1];
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci		slave_data->disabled = !of_device_is_available(port_np);
128062306a36Sopenharmony_ci		if (slave_data->disabled)
128162306a36Sopenharmony_ci			continue;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci		slave_data->slave_node = port_np;
128462306a36Sopenharmony_ci		slave_data->ifphy = devm_of_phy_get(dev, port_np, NULL);
128562306a36Sopenharmony_ci		if (IS_ERR(slave_data->ifphy)) {
128662306a36Sopenharmony_ci			ret = PTR_ERR(slave_data->ifphy);
128762306a36Sopenharmony_ci			dev_err(dev, "%pOF: Error retrieving port phy: %d\n",
128862306a36Sopenharmony_ci				port_np, ret);
128962306a36Sopenharmony_ci			goto err_node_put;
129062306a36Sopenharmony_ci		}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci		if (of_phy_is_fixed_link(port_np)) {
129362306a36Sopenharmony_ci			ret = of_phy_register_fixed_link(port_np);
129462306a36Sopenharmony_ci			if (ret) {
129562306a36Sopenharmony_ci				dev_err_probe(dev, ret, "%pOF failed to register fixed-link phy\n",
129662306a36Sopenharmony_ci					      port_np);
129762306a36Sopenharmony_ci				goto err_node_put;
129862306a36Sopenharmony_ci			}
129962306a36Sopenharmony_ci			slave_data->phy_node = of_node_get(port_np);
130062306a36Sopenharmony_ci		} else {
130162306a36Sopenharmony_ci			slave_data->phy_node =
130262306a36Sopenharmony_ci				of_parse_phandle(port_np, "phy-handle", 0);
130362306a36Sopenharmony_ci		}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci		if (!slave_data->phy_node) {
130662306a36Sopenharmony_ci			dev_err(dev, "%pOF no phy found\n", port_np);
130762306a36Sopenharmony_ci			ret = -ENODEV;
130862306a36Sopenharmony_ci			goto err_node_put;
130962306a36Sopenharmony_ci		}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci		ret = of_get_phy_mode(port_np, &slave_data->phy_if);
131262306a36Sopenharmony_ci		if (ret) {
131362306a36Sopenharmony_ci			dev_err(dev, "%pOF read phy-mode err %d\n",
131462306a36Sopenharmony_ci				port_np, ret);
131562306a36Sopenharmony_ci			goto err_node_put;
131662306a36Sopenharmony_ci		}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci		ret = of_get_mac_address(port_np, slave_data->mac_addr);
131962306a36Sopenharmony_ci		if (ret) {
132062306a36Sopenharmony_ci			ret = ti_cm_get_macid(dev, port_id - 1,
132162306a36Sopenharmony_ci					      slave_data->mac_addr);
132262306a36Sopenharmony_ci			if (ret)
132362306a36Sopenharmony_ci				goto err_node_put;
132462306a36Sopenharmony_ci		}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci		if (of_property_read_u32(port_np, "ti,dual-emac-pvid",
132762306a36Sopenharmony_ci					 &prop)) {
132862306a36Sopenharmony_ci			dev_err(dev, "%pOF Missing dual_emac_res_vlan in DT.\n",
132962306a36Sopenharmony_ci				port_np);
133062306a36Sopenharmony_ci			slave_data->dual_emac_res_vlan = port_id;
133162306a36Sopenharmony_ci			dev_err(dev, "%pOF Using %d as Reserved VLAN\n",
133262306a36Sopenharmony_ci				port_np, slave_data->dual_emac_res_vlan);
133362306a36Sopenharmony_ci		} else {
133462306a36Sopenharmony_ci			slave_data->dual_emac_res_vlan = prop;
133562306a36Sopenharmony_ci		}
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	of_node_put(tmp_node);
133962306a36Sopenharmony_ci	return 0;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_cierr_node_put:
134262306a36Sopenharmony_ci	of_node_put(port_np);
134362306a36Sopenharmony_ci	of_node_put(tmp_node);
134462306a36Sopenharmony_ci	return ret;
134562306a36Sopenharmony_ci}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_cistatic void cpsw_remove_dt(struct cpsw_common *cpsw)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	struct cpsw_platform_data *data = &cpsw->data;
135062306a36Sopenharmony_ci	int i = 0;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	for (i = 0; i < cpsw->data.slaves; i++) {
135362306a36Sopenharmony_ci		struct cpsw_slave_data *slave_data = &data->slave_data[i];
135462306a36Sopenharmony_ci		struct device_node *port_np = slave_data->phy_node;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci		if (port_np) {
135762306a36Sopenharmony_ci			if (of_phy_is_fixed_link(port_np))
135862306a36Sopenharmony_ci				of_phy_deregister_fixed_link(port_np);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci			of_node_put(port_np);
136162306a36Sopenharmony_ci		}
136262306a36Sopenharmony_ci	}
136362306a36Sopenharmony_ci}
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_cistatic int cpsw_create_ports(struct cpsw_common *cpsw)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	struct cpsw_platform_data *data = &cpsw->data;
136862306a36Sopenharmony_ci	struct net_device *ndev, *napi_ndev = NULL;
136962306a36Sopenharmony_ci	struct device *dev = cpsw->dev;
137062306a36Sopenharmony_ci	struct cpsw_priv *priv;
137162306a36Sopenharmony_ci	int ret = 0, i = 0;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	for (i = 0; i < cpsw->data.slaves; i++) {
137462306a36Sopenharmony_ci		struct cpsw_slave_data *slave_data = &data->slave_data[i];
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci		if (slave_data->disabled)
137762306a36Sopenharmony_ci			continue;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci		ndev = devm_alloc_etherdev_mqs(dev, sizeof(struct cpsw_priv),
138062306a36Sopenharmony_ci					       CPSW_MAX_QUEUES,
138162306a36Sopenharmony_ci					       CPSW_MAX_QUEUES);
138262306a36Sopenharmony_ci		if (!ndev) {
138362306a36Sopenharmony_ci			dev_err(dev, "error allocating net_device\n");
138462306a36Sopenharmony_ci			return -ENOMEM;
138562306a36Sopenharmony_ci		}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci		priv = netdev_priv(ndev);
138862306a36Sopenharmony_ci		priv->cpsw = cpsw;
138962306a36Sopenharmony_ci		priv->ndev = ndev;
139062306a36Sopenharmony_ci		priv->dev  = dev;
139162306a36Sopenharmony_ci		priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
139262306a36Sopenharmony_ci		priv->emac_port = i + 1;
139362306a36Sopenharmony_ci		priv->tx_packet_min = CPSW_MIN_PACKET_SIZE;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci		if (is_valid_ether_addr(slave_data->mac_addr)) {
139662306a36Sopenharmony_ci			ether_addr_copy(priv->mac_addr, slave_data->mac_addr);
139762306a36Sopenharmony_ci			dev_info(cpsw->dev, "Detected MACID = %pM\n",
139862306a36Sopenharmony_ci				 priv->mac_addr);
139962306a36Sopenharmony_ci		} else {
140062306a36Sopenharmony_ci			eth_random_addr(slave_data->mac_addr);
140162306a36Sopenharmony_ci			dev_info(cpsw->dev, "Random MACID = %pM\n",
140262306a36Sopenharmony_ci				 priv->mac_addr);
140362306a36Sopenharmony_ci		}
140462306a36Sopenharmony_ci		eth_hw_addr_set(ndev, slave_data->mac_addr);
140562306a36Sopenharmony_ci		ether_addr_copy(priv->mac_addr, slave_data->mac_addr);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci		cpsw->slaves[i].ndev = ndev;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci		ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER |
141062306a36Sopenharmony_ci				  NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_NETNS_LOCAL | NETIF_F_HW_TC;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci		ndev->xdp_features = NETDEV_XDP_ACT_BASIC |
141362306a36Sopenharmony_ci				     NETDEV_XDP_ACT_REDIRECT |
141462306a36Sopenharmony_ci				     NETDEV_XDP_ACT_NDO_XMIT;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci		ndev->netdev_ops = &cpsw_netdev_ops;
141762306a36Sopenharmony_ci		ndev->ethtool_ops = &cpsw_ethtool_ops;
141862306a36Sopenharmony_ci		SET_NETDEV_DEV(ndev, dev);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci		if (!napi_ndev) {
142162306a36Sopenharmony_ci			/* CPSW Host port CPDMA interface is shared between
142262306a36Sopenharmony_ci			 * ports and there is only one TX and one RX IRQs
142362306a36Sopenharmony_ci			 * available for all possible TX and RX channels
142462306a36Sopenharmony_ci			 * accordingly.
142562306a36Sopenharmony_ci			 */
142662306a36Sopenharmony_ci			netif_napi_add(ndev, &cpsw->napi_rx,
142762306a36Sopenharmony_ci				       cpsw->quirk_irq ? cpsw_rx_poll : cpsw_rx_mq_poll);
142862306a36Sopenharmony_ci			netif_napi_add_tx(ndev, &cpsw->napi_tx,
142962306a36Sopenharmony_ci					  cpsw->quirk_irq ?
143062306a36Sopenharmony_ci					  cpsw_tx_poll : cpsw_tx_mq_poll);
143162306a36Sopenharmony_ci		}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci		napi_ndev = ndev;
143462306a36Sopenharmony_ci	}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	return ret;
143762306a36Sopenharmony_ci}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_cistatic void cpsw_unregister_ports(struct cpsw_common *cpsw)
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	int i = 0;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	for (i = 0; i < cpsw->data.slaves; i++) {
144462306a36Sopenharmony_ci		if (!cpsw->slaves[i].ndev)
144562306a36Sopenharmony_ci			continue;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci		unregister_netdev(cpsw->slaves[i].ndev);
144862306a36Sopenharmony_ci	}
144962306a36Sopenharmony_ci}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_cistatic int cpsw_register_ports(struct cpsw_common *cpsw)
145262306a36Sopenharmony_ci{
145362306a36Sopenharmony_ci	int ret = 0, i = 0;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	for (i = 0; i < cpsw->data.slaves; i++) {
145662306a36Sopenharmony_ci		if (!cpsw->slaves[i].ndev)
145762306a36Sopenharmony_ci			continue;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci		/* register the network device */
146062306a36Sopenharmony_ci		ret = register_netdev(cpsw->slaves[i].ndev);
146162306a36Sopenharmony_ci		if (ret) {
146262306a36Sopenharmony_ci			dev_err(cpsw->dev,
146362306a36Sopenharmony_ci				"cpsw: err registering net device%d\n", i);
146462306a36Sopenharmony_ci			cpsw->slaves[i].ndev = NULL;
146562306a36Sopenharmony_ci			break;
146662306a36Sopenharmony_ci		}
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	if (ret)
147062306a36Sopenharmony_ci		cpsw_unregister_ports(cpsw);
147162306a36Sopenharmony_ci	return ret;
147262306a36Sopenharmony_ci}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_cibool cpsw_port_dev_check(const struct net_device *ndev)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	if (ndev->netdev_ops == &cpsw_netdev_ops) {
147762306a36Sopenharmony_ci		struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci		return !cpsw->data.dual_emac;
148062306a36Sopenharmony_ci	}
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	return false;
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cistatic void cpsw_port_offload_fwd_mark_update(struct cpsw_common *cpsw)
148662306a36Sopenharmony_ci{
148762306a36Sopenharmony_ci	int set_val = 0;
148862306a36Sopenharmony_ci	int i;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	if (!cpsw->ale_bypass &&
149162306a36Sopenharmony_ci	    (cpsw->br_members == (ALE_PORT_1 | ALE_PORT_2)))
149262306a36Sopenharmony_ci		set_val = 1;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	dev_dbg(cpsw->dev, "set offload_fwd_mark %d\n", set_val);
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	for (i = 0; i < cpsw->data.slaves; i++) {
149762306a36Sopenharmony_ci		struct net_device *sl_ndev = cpsw->slaves[i].ndev;
149862306a36Sopenharmony_ci		struct cpsw_priv *priv = netdev_priv(sl_ndev);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci		priv->offload_fwd_mark = set_val;
150162306a36Sopenharmony_ci	}
150262306a36Sopenharmony_ci}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_cistatic int cpsw_netdevice_port_link(struct net_device *ndev,
150562306a36Sopenharmony_ci				    struct net_device *br_ndev,
150662306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
150762306a36Sopenharmony_ci{
150862306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
150962306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
151062306a36Sopenharmony_ci	int err;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	if (!cpsw->br_members) {
151362306a36Sopenharmony_ci		cpsw->hw_bridge_dev = br_ndev;
151462306a36Sopenharmony_ci	} else {
151562306a36Sopenharmony_ci		/* This is adding the port to a second bridge, this is
151662306a36Sopenharmony_ci		 * unsupported
151762306a36Sopenharmony_ci		 */
151862306a36Sopenharmony_ci		if (cpsw->hw_bridge_dev != br_ndev)
151962306a36Sopenharmony_ci			return -EOPNOTSUPP;
152062306a36Sopenharmony_ci	}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	err = switchdev_bridge_port_offload(ndev, ndev, NULL, NULL, NULL,
152362306a36Sopenharmony_ci					    false, extack);
152462306a36Sopenharmony_ci	if (err)
152562306a36Sopenharmony_ci		return err;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	cpsw->br_members |= BIT(priv->emac_port);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	cpsw_port_offload_fwd_mark_update(cpsw);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	return NOTIFY_DONE;
153262306a36Sopenharmony_ci}
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_cistatic void cpsw_netdevice_port_unlink(struct net_device *ndev)
153562306a36Sopenharmony_ci{
153662306a36Sopenharmony_ci	struct cpsw_priv *priv = netdev_priv(ndev);
153762306a36Sopenharmony_ci	struct cpsw_common *cpsw = priv->cpsw;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	switchdev_bridge_port_unoffload(ndev, NULL, NULL, NULL);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	cpsw->br_members &= ~BIT(priv->emac_port);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	cpsw_port_offload_fwd_mark_update(cpsw);
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	if (!cpsw->br_members)
154662306a36Sopenharmony_ci		cpsw->hw_bridge_dev = NULL;
154762306a36Sopenharmony_ci}
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci/* netdev notifier */
155062306a36Sopenharmony_cistatic int cpsw_netdevice_event(struct notifier_block *unused,
155162306a36Sopenharmony_ci				unsigned long event, void *ptr)
155262306a36Sopenharmony_ci{
155362306a36Sopenharmony_ci	struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
155462306a36Sopenharmony_ci	struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
155562306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *info;
155662306a36Sopenharmony_ci	int ret = NOTIFY_DONE;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	if (!cpsw_port_dev_check(ndev))
155962306a36Sopenharmony_ci		return NOTIFY_DONE;
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	switch (event) {
156262306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
156362306a36Sopenharmony_ci		info = ptr;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci		if (netif_is_bridge_master(info->upper_dev)) {
156662306a36Sopenharmony_ci			if (info->linking)
156762306a36Sopenharmony_ci				ret = cpsw_netdevice_port_link(ndev,
156862306a36Sopenharmony_ci							       info->upper_dev,
156962306a36Sopenharmony_ci							       extack);
157062306a36Sopenharmony_ci			else
157162306a36Sopenharmony_ci				cpsw_netdevice_port_unlink(ndev);
157262306a36Sopenharmony_ci		}
157362306a36Sopenharmony_ci		break;
157462306a36Sopenharmony_ci	default:
157562306a36Sopenharmony_ci		return NOTIFY_DONE;
157662306a36Sopenharmony_ci	}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	return notifier_from_errno(ret);
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_cistatic struct notifier_block cpsw_netdevice_nb __read_mostly = {
158262306a36Sopenharmony_ci	.notifier_call = cpsw_netdevice_event,
158362306a36Sopenharmony_ci};
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_cistatic int cpsw_register_notifiers(struct cpsw_common *cpsw)
158662306a36Sopenharmony_ci{
158762306a36Sopenharmony_ci	int ret = 0;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	ret = register_netdevice_notifier(&cpsw_netdevice_nb);
159062306a36Sopenharmony_ci	if (ret) {
159162306a36Sopenharmony_ci		dev_err(cpsw->dev, "can't register netdevice notifier\n");
159262306a36Sopenharmony_ci		return ret;
159362306a36Sopenharmony_ci	}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	ret = cpsw_switchdev_register_notifiers(cpsw);
159662306a36Sopenharmony_ci	if (ret)
159762306a36Sopenharmony_ci		unregister_netdevice_notifier(&cpsw_netdevice_nb);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	return ret;
160062306a36Sopenharmony_ci}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_cistatic void cpsw_unregister_notifiers(struct cpsw_common *cpsw)
160362306a36Sopenharmony_ci{
160462306a36Sopenharmony_ci	cpsw_switchdev_unregister_notifiers(cpsw);
160562306a36Sopenharmony_ci	unregister_netdevice_notifier(&cpsw_netdevice_nb);
160662306a36Sopenharmony_ci}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_cistatic const struct devlink_ops cpsw_devlink_ops = {
160962306a36Sopenharmony_ci};
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_cistatic int cpsw_dl_switch_mode_get(struct devlink *dl, u32 id,
161262306a36Sopenharmony_ci				   struct devlink_param_gset_ctx *ctx)
161362306a36Sopenharmony_ci{
161462306a36Sopenharmony_ci	struct cpsw_devlink *dl_priv = devlink_priv(dl);
161562306a36Sopenharmony_ci	struct cpsw_common *cpsw = dl_priv->cpsw;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	dev_dbg(cpsw->dev, "%s id:%u\n", __func__, id);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	if (id != CPSW_DL_PARAM_SWITCH_MODE)
162062306a36Sopenharmony_ci		return  -EOPNOTSUPP;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	ctx->val.vbool = !cpsw->data.dual_emac;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	return 0;
162562306a36Sopenharmony_ci}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_cistatic int cpsw_dl_switch_mode_set(struct devlink *dl, u32 id,
162862306a36Sopenharmony_ci				   struct devlink_param_gset_ctx *ctx)
162962306a36Sopenharmony_ci{
163062306a36Sopenharmony_ci	struct cpsw_devlink *dl_priv = devlink_priv(dl);
163162306a36Sopenharmony_ci	struct cpsw_common *cpsw = dl_priv->cpsw;
163262306a36Sopenharmony_ci	int vlan = cpsw->data.default_vlan;
163362306a36Sopenharmony_ci	bool switch_en = ctx->val.vbool;
163462306a36Sopenharmony_ci	bool if_running = false;
163562306a36Sopenharmony_ci	int i;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	dev_dbg(cpsw->dev, "%s id:%u\n", __func__, id);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	if (id != CPSW_DL_PARAM_SWITCH_MODE)
164062306a36Sopenharmony_ci		return  -EOPNOTSUPP;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	if (switch_en == !cpsw->data.dual_emac)
164362306a36Sopenharmony_ci		return 0;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	if (!switch_en && cpsw->br_members) {
164662306a36Sopenharmony_ci		dev_err(cpsw->dev, "Remove ports from BR before disabling switch mode\n");
164762306a36Sopenharmony_ci		return -EINVAL;
164862306a36Sopenharmony_ci	}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	rtnl_lock();
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	for (i = 0; i < cpsw->data.slaves; i++) {
165362306a36Sopenharmony_ci		struct cpsw_slave *slave = &cpsw->slaves[i];
165462306a36Sopenharmony_ci		struct net_device *sl_ndev = slave->ndev;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci		if (!sl_ndev || !netif_running(sl_ndev))
165762306a36Sopenharmony_ci			continue;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci		if_running = true;
166062306a36Sopenharmony_ci	}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	if (!if_running) {
166362306a36Sopenharmony_ci		/* all ndevs are down */
166462306a36Sopenharmony_ci		cpsw->data.dual_emac = !switch_en;
166562306a36Sopenharmony_ci		for (i = 0; i < cpsw->data.slaves; i++) {
166662306a36Sopenharmony_ci			struct cpsw_slave *slave = &cpsw->slaves[i];
166762306a36Sopenharmony_ci			struct net_device *sl_ndev = slave->ndev;
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci			if (!sl_ndev)
167062306a36Sopenharmony_ci				continue;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci			if (switch_en)
167362306a36Sopenharmony_ci				vlan = cpsw->data.default_vlan;
167462306a36Sopenharmony_ci			else
167562306a36Sopenharmony_ci				vlan = slave->data->dual_emac_res_vlan;
167662306a36Sopenharmony_ci			slave->port_vlan = vlan;
167762306a36Sopenharmony_ci		}
167862306a36Sopenharmony_ci		goto exit;
167962306a36Sopenharmony_ci	}
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	if (switch_en) {
168262306a36Sopenharmony_ci		dev_info(cpsw->dev, "Enable switch mode\n");
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci		/* enable bypass - no forwarding; all traffic goes to Host */
168562306a36Sopenharmony_ci		cpsw_ale_control_set(cpsw->ale, 0, ALE_BYPASS, 1);
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci		/* clean up ALE table */
168862306a36Sopenharmony_ci		cpsw_ale_control_set(cpsw->ale, 0, ALE_CLEAR, 1);
168962306a36Sopenharmony_ci		cpsw_ale_control_get(cpsw->ale, 0, ALE_AGEOUT);
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci		cpsw_init_host_port_switch(cpsw);
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci		for (i = 0; i < cpsw->data.slaves; i++) {
169462306a36Sopenharmony_ci			struct cpsw_slave *slave = &cpsw->slaves[i];
169562306a36Sopenharmony_ci			struct net_device *sl_ndev = slave->ndev;
169662306a36Sopenharmony_ci			struct cpsw_priv *priv;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci			if (!sl_ndev)
169962306a36Sopenharmony_ci				continue;
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci			priv = netdev_priv(sl_ndev);
170262306a36Sopenharmony_ci			slave->port_vlan = vlan;
170362306a36Sopenharmony_ci			WRITE_ONCE(priv->tx_packet_min, CPSW_MIN_PACKET_SIZE_VLAN);
170462306a36Sopenharmony_ci			if (netif_running(sl_ndev))
170562306a36Sopenharmony_ci				cpsw_port_add_switch_def_ale_entries(priv,
170662306a36Sopenharmony_ci								     slave);
170762306a36Sopenharmony_ci		}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci		cpsw_ale_control_set(cpsw->ale, 0, ALE_BYPASS, 0);
171062306a36Sopenharmony_ci		cpsw->data.dual_emac = false;
171162306a36Sopenharmony_ci	} else {
171262306a36Sopenharmony_ci		dev_info(cpsw->dev, "Disable switch mode\n");
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci		/* enable bypass - no forwarding; all traffic goes to Host */
171562306a36Sopenharmony_ci		cpsw_ale_control_set(cpsw->ale, 0, ALE_BYPASS, 1);
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci		cpsw_ale_control_set(cpsw->ale, 0, ALE_CLEAR, 1);
171862306a36Sopenharmony_ci		cpsw_ale_control_get(cpsw->ale, 0, ALE_AGEOUT);
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci		cpsw_init_host_port_dual_mac(cpsw);
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci		for (i = 0; i < cpsw->data.slaves; i++) {
172362306a36Sopenharmony_ci			struct cpsw_slave *slave = &cpsw->slaves[i];
172462306a36Sopenharmony_ci			struct net_device *sl_ndev = slave->ndev;
172562306a36Sopenharmony_ci			struct cpsw_priv *priv;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci			if (!sl_ndev)
172862306a36Sopenharmony_ci				continue;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci			priv = netdev_priv(slave->ndev);
173162306a36Sopenharmony_ci			slave->port_vlan = slave->data->dual_emac_res_vlan;
173262306a36Sopenharmony_ci			WRITE_ONCE(priv->tx_packet_min, CPSW_MIN_PACKET_SIZE);
173362306a36Sopenharmony_ci			cpsw_port_add_dual_emac_def_ale_entries(priv, slave);
173462306a36Sopenharmony_ci		}
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci		cpsw_ale_control_set(cpsw->ale, 0, ALE_BYPASS, 0);
173762306a36Sopenharmony_ci		cpsw->data.dual_emac = true;
173862306a36Sopenharmony_ci	}
173962306a36Sopenharmony_ciexit:
174062306a36Sopenharmony_ci	rtnl_unlock();
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	return 0;
174362306a36Sopenharmony_ci}
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_cistatic int cpsw_dl_ale_ctrl_get(struct devlink *dl, u32 id,
174662306a36Sopenharmony_ci				struct devlink_param_gset_ctx *ctx)
174762306a36Sopenharmony_ci{
174862306a36Sopenharmony_ci	struct cpsw_devlink *dl_priv = devlink_priv(dl);
174962306a36Sopenharmony_ci	struct cpsw_common *cpsw = dl_priv->cpsw;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	dev_dbg(cpsw->dev, "%s id:%u\n", __func__, id);
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	switch (id) {
175462306a36Sopenharmony_ci	case CPSW_DL_PARAM_ALE_BYPASS:
175562306a36Sopenharmony_ci		ctx->val.vbool = cpsw_ale_control_get(cpsw->ale, 0, ALE_BYPASS);
175662306a36Sopenharmony_ci		break;
175762306a36Sopenharmony_ci	default:
175862306a36Sopenharmony_ci		return -EOPNOTSUPP;
175962306a36Sopenharmony_ci	}
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	return 0;
176262306a36Sopenharmony_ci}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_cistatic int cpsw_dl_ale_ctrl_set(struct devlink *dl, u32 id,
176562306a36Sopenharmony_ci				struct devlink_param_gset_ctx *ctx)
176662306a36Sopenharmony_ci{
176762306a36Sopenharmony_ci	struct cpsw_devlink *dl_priv = devlink_priv(dl);
176862306a36Sopenharmony_ci	struct cpsw_common *cpsw = dl_priv->cpsw;
176962306a36Sopenharmony_ci	int ret = -EOPNOTSUPP;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	dev_dbg(cpsw->dev, "%s id:%u\n", __func__, id);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	switch (id) {
177462306a36Sopenharmony_ci	case CPSW_DL_PARAM_ALE_BYPASS:
177562306a36Sopenharmony_ci		ret = cpsw_ale_control_set(cpsw->ale, 0, ALE_BYPASS,
177662306a36Sopenharmony_ci					   ctx->val.vbool);
177762306a36Sopenharmony_ci		if (!ret) {
177862306a36Sopenharmony_ci			cpsw->ale_bypass = ctx->val.vbool;
177962306a36Sopenharmony_ci			cpsw_port_offload_fwd_mark_update(cpsw);
178062306a36Sopenharmony_ci		}
178162306a36Sopenharmony_ci		break;
178262306a36Sopenharmony_ci	default:
178362306a36Sopenharmony_ci		return -EOPNOTSUPP;
178462306a36Sopenharmony_ci	}
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	return 0;
178762306a36Sopenharmony_ci}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_cistatic const struct devlink_param cpsw_devlink_params[] = {
179062306a36Sopenharmony_ci	DEVLINK_PARAM_DRIVER(CPSW_DL_PARAM_SWITCH_MODE,
179162306a36Sopenharmony_ci			     "switch_mode", DEVLINK_PARAM_TYPE_BOOL,
179262306a36Sopenharmony_ci			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
179362306a36Sopenharmony_ci			     cpsw_dl_switch_mode_get, cpsw_dl_switch_mode_set,
179462306a36Sopenharmony_ci			     NULL),
179562306a36Sopenharmony_ci	DEVLINK_PARAM_DRIVER(CPSW_DL_PARAM_ALE_BYPASS,
179662306a36Sopenharmony_ci			     "ale_bypass", DEVLINK_PARAM_TYPE_BOOL,
179762306a36Sopenharmony_ci			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
179862306a36Sopenharmony_ci			     cpsw_dl_ale_ctrl_get, cpsw_dl_ale_ctrl_set, NULL),
179962306a36Sopenharmony_ci};
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_cistatic int cpsw_register_devlink(struct cpsw_common *cpsw)
180262306a36Sopenharmony_ci{
180362306a36Sopenharmony_ci	struct device *dev = cpsw->dev;
180462306a36Sopenharmony_ci	struct cpsw_devlink *dl_priv;
180562306a36Sopenharmony_ci	int ret = 0;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	cpsw->devlink = devlink_alloc(&cpsw_devlink_ops, sizeof(*dl_priv), dev);
180862306a36Sopenharmony_ci	if (!cpsw->devlink)
180962306a36Sopenharmony_ci		return -ENOMEM;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	dl_priv = devlink_priv(cpsw->devlink);
181262306a36Sopenharmony_ci	dl_priv->cpsw = cpsw;
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	ret = devlink_params_register(cpsw->devlink, cpsw_devlink_params,
181562306a36Sopenharmony_ci				      ARRAY_SIZE(cpsw_devlink_params));
181662306a36Sopenharmony_ci	if (ret) {
181762306a36Sopenharmony_ci		dev_err(dev, "DL params reg fail ret:%d\n", ret);
181862306a36Sopenharmony_ci		goto dl_unreg;
181962306a36Sopenharmony_ci	}
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	devlink_register(cpsw->devlink);
182262306a36Sopenharmony_ci	return ret;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_cidl_unreg:
182562306a36Sopenharmony_ci	devlink_free(cpsw->devlink);
182662306a36Sopenharmony_ci	return ret;
182762306a36Sopenharmony_ci}
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_cistatic void cpsw_unregister_devlink(struct cpsw_common *cpsw)
183062306a36Sopenharmony_ci{
183162306a36Sopenharmony_ci	devlink_unregister(cpsw->devlink);
183262306a36Sopenharmony_ci	devlink_params_unregister(cpsw->devlink, cpsw_devlink_params,
183362306a36Sopenharmony_ci				  ARRAY_SIZE(cpsw_devlink_params));
183462306a36Sopenharmony_ci	devlink_free(cpsw->devlink);
183562306a36Sopenharmony_ci}
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_cistatic const struct of_device_id cpsw_of_mtable[] = {
183862306a36Sopenharmony_ci	{ .compatible = "ti,cpsw-switch"},
183962306a36Sopenharmony_ci	{ .compatible = "ti,am335x-cpsw-switch"},
184062306a36Sopenharmony_ci	{ .compatible = "ti,am4372-cpsw-switch"},
184162306a36Sopenharmony_ci	{ .compatible = "ti,dra7-cpsw-switch"},
184262306a36Sopenharmony_ci	{ /* sentinel */ },
184362306a36Sopenharmony_ci};
184462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cpsw_of_mtable);
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cistatic const struct soc_device_attribute cpsw_soc_devices[] = {
184762306a36Sopenharmony_ci	{ .family = "AM33xx", .revision = "ES1.0"},
184862306a36Sopenharmony_ci	{ /* sentinel */ }
184962306a36Sopenharmony_ci};
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_cistatic int cpsw_probe(struct platform_device *pdev)
185262306a36Sopenharmony_ci{
185362306a36Sopenharmony_ci	const struct soc_device_attribute *soc;
185462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
185562306a36Sopenharmony_ci	struct cpsw_common *cpsw;
185662306a36Sopenharmony_ci	struct resource *ss_res;
185762306a36Sopenharmony_ci	struct gpio_descs *mode;
185862306a36Sopenharmony_ci	void __iomem *ss_regs;
185962306a36Sopenharmony_ci	int ret = 0, ch;
186062306a36Sopenharmony_ci	struct clk *clk;
186162306a36Sopenharmony_ci	int irq;
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	cpsw = devm_kzalloc(dev, sizeof(struct cpsw_common), GFP_KERNEL);
186462306a36Sopenharmony_ci	if (!cpsw)
186562306a36Sopenharmony_ci		return -ENOMEM;
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	cpsw_slave_index = cpsw_slave_index_priv;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	cpsw->dev = dev;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	cpsw->slaves = devm_kcalloc(dev,
187262306a36Sopenharmony_ci				    CPSW_SLAVE_PORTS_NUM,
187362306a36Sopenharmony_ci				    sizeof(struct cpsw_slave),
187462306a36Sopenharmony_ci				    GFP_KERNEL);
187562306a36Sopenharmony_ci	if (!cpsw->slaves)
187662306a36Sopenharmony_ci		return -ENOMEM;
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	mode = devm_gpiod_get_array_optional(dev, "mode", GPIOD_OUT_LOW);
187962306a36Sopenharmony_ci	if (IS_ERR(mode)) {
188062306a36Sopenharmony_ci		ret = PTR_ERR(mode);
188162306a36Sopenharmony_ci		dev_err(dev, "gpio request failed, ret %d\n", ret);
188262306a36Sopenharmony_ci		return ret;
188362306a36Sopenharmony_ci	}
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	clk = devm_clk_get(dev, "fck");
188662306a36Sopenharmony_ci	if (IS_ERR(clk)) {
188762306a36Sopenharmony_ci		ret = PTR_ERR(clk);
188862306a36Sopenharmony_ci		dev_err(dev, "fck is not found %d\n", ret);
188962306a36Sopenharmony_ci		return ret;
189062306a36Sopenharmony_ci	}
189162306a36Sopenharmony_ci	cpsw->bus_freq_mhz = clk_get_rate(clk) / 1000000;
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	ss_regs = devm_platform_get_and_ioremap_resource(pdev, 0, &ss_res);
189462306a36Sopenharmony_ci	if (IS_ERR(ss_regs)) {
189562306a36Sopenharmony_ci		ret = PTR_ERR(ss_regs);
189662306a36Sopenharmony_ci		return ret;
189762306a36Sopenharmony_ci	}
189862306a36Sopenharmony_ci	cpsw->regs = ss_regs;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	irq = platform_get_irq_byname(pdev, "rx");
190162306a36Sopenharmony_ci	if (irq < 0)
190262306a36Sopenharmony_ci		return irq;
190362306a36Sopenharmony_ci	cpsw->irqs_table[0] = irq;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	irq = platform_get_irq_byname(pdev, "tx");
190662306a36Sopenharmony_ci	if (irq < 0)
190762306a36Sopenharmony_ci		return irq;
190862306a36Sopenharmony_ci	cpsw->irqs_table[1] = irq;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	irq = platform_get_irq_byname(pdev, "misc");
191162306a36Sopenharmony_ci	if (irq <= 0)
191262306a36Sopenharmony_ci		return irq;
191362306a36Sopenharmony_ci	cpsw->misc_irq = irq;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	platform_set_drvdata(pdev, cpsw);
191662306a36Sopenharmony_ci	/* This may be required here for child devices. */
191762306a36Sopenharmony_ci	pm_runtime_enable(dev);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	/* Need to enable clocks with runtime PM api to access module
192062306a36Sopenharmony_ci	 * registers
192162306a36Sopenharmony_ci	 */
192262306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
192362306a36Sopenharmony_ci	if (ret < 0) {
192462306a36Sopenharmony_ci		pm_runtime_disable(dev);
192562306a36Sopenharmony_ci		return ret;
192662306a36Sopenharmony_ci	}
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	ret = cpsw_probe_dt(cpsw);
192962306a36Sopenharmony_ci	if (ret)
193062306a36Sopenharmony_ci		goto clean_dt_ret;
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	soc = soc_device_match(cpsw_soc_devices);
193362306a36Sopenharmony_ci	if (soc)
193462306a36Sopenharmony_ci		cpsw->quirk_irq = true;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	cpsw->rx_packet_max = rx_packet_max;
193762306a36Sopenharmony_ci	cpsw->descs_pool_size = descs_pool_size;
193862306a36Sopenharmony_ci	eth_random_addr(cpsw->base_mac);
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	ret = cpsw_init_common(cpsw, ss_regs, ale_ageout,
194162306a36Sopenharmony_ci			       (u32 __force)ss_res->start + CPSW2_BD_OFFSET,
194262306a36Sopenharmony_ci			       descs_pool_size);
194362306a36Sopenharmony_ci	if (ret)
194462306a36Sopenharmony_ci		goto clean_dt_ret;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	cpsw->wr_regs = cpsw->version == CPSW_VERSION_1 ?
194762306a36Sopenharmony_ci			ss_regs + CPSW1_WR_OFFSET :
194862306a36Sopenharmony_ci			ss_regs + CPSW2_WR_OFFSET;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	ch = cpsw->quirk_irq ? 0 : 7;
195162306a36Sopenharmony_ci	cpsw->txv[0].ch = cpdma_chan_create(cpsw->dma, ch, cpsw_tx_handler, 0);
195262306a36Sopenharmony_ci	if (IS_ERR(cpsw->txv[0].ch)) {
195362306a36Sopenharmony_ci		dev_err(dev, "error initializing tx dma channel\n");
195462306a36Sopenharmony_ci		ret = PTR_ERR(cpsw->txv[0].ch);
195562306a36Sopenharmony_ci		goto clean_cpts;
195662306a36Sopenharmony_ci	}
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	cpsw->rxv[0].ch = cpdma_chan_create(cpsw->dma, 0, cpsw_rx_handler, 1);
195962306a36Sopenharmony_ci	if (IS_ERR(cpsw->rxv[0].ch)) {
196062306a36Sopenharmony_ci		dev_err(dev, "error initializing rx dma channel\n");
196162306a36Sopenharmony_ci		ret = PTR_ERR(cpsw->rxv[0].ch);
196262306a36Sopenharmony_ci		goto clean_cpts;
196362306a36Sopenharmony_ci	}
196462306a36Sopenharmony_ci	cpsw_split_res(cpsw);
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	/* setup netdevs */
196762306a36Sopenharmony_ci	ret = cpsw_create_ports(cpsw);
196862306a36Sopenharmony_ci	if (ret)
196962306a36Sopenharmony_ci		goto clean_unregister_netdev;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	/* Grab RX and TX IRQs. Note that we also have RX_THRESHOLD and
197262306a36Sopenharmony_ci	 * MISC IRQs which are always kept disabled with this driver so
197362306a36Sopenharmony_ci	 * we will not request them.
197462306a36Sopenharmony_ci	 *
197562306a36Sopenharmony_ci	 * If anyone wants to implement support for those, make sure to
197662306a36Sopenharmony_ci	 * first request and append them to irqs_table array.
197762306a36Sopenharmony_ci	 */
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	ret = devm_request_irq(dev, cpsw->irqs_table[0], cpsw_rx_interrupt,
198062306a36Sopenharmony_ci			       0, dev_name(dev), cpsw);
198162306a36Sopenharmony_ci	if (ret < 0) {
198262306a36Sopenharmony_ci		dev_err(dev, "error attaching irq (%d)\n", ret);
198362306a36Sopenharmony_ci		goto clean_unregister_netdev;
198462306a36Sopenharmony_ci	}
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	ret = devm_request_irq(dev, cpsw->irqs_table[1], cpsw_tx_interrupt,
198762306a36Sopenharmony_ci			       0, dev_name(dev), cpsw);
198862306a36Sopenharmony_ci	if (ret < 0) {
198962306a36Sopenharmony_ci		dev_err(dev, "error attaching irq (%d)\n", ret);
199062306a36Sopenharmony_ci		goto clean_unregister_netdev;
199162306a36Sopenharmony_ci	}
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	if (!cpsw->cpts)
199462306a36Sopenharmony_ci		goto skip_cpts;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	ret = devm_request_irq(dev, cpsw->misc_irq, cpsw_misc_interrupt,
199762306a36Sopenharmony_ci			       0, dev_name(&pdev->dev), cpsw);
199862306a36Sopenharmony_ci	if (ret < 0) {
199962306a36Sopenharmony_ci		dev_err(dev, "error attaching misc irq (%d)\n", ret);
200062306a36Sopenharmony_ci		goto clean_unregister_netdev;
200162306a36Sopenharmony_ci	}
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	/* Enable misc CPTS evnt_pend IRQ */
200462306a36Sopenharmony_ci	cpts_set_irqpoll(cpsw->cpts, false);
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ciskip_cpts:
200762306a36Sopenharmony_ci	ret = cpsw_register_notifiers(cpsw);
200862306a36Sopenharmony_ci	if (ret)
200962306a36Sopenharmony_ci		goto clean_unregister_netdev;
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	ret = cpsw_register_devlink(cpsw);
201262306a36Sopenharmony_ci	if (ret)
201362306a36Sopenharmony_ci		goto clean_unregister_notifiers;
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	ret = cpsw_register_ports(cpsw);
201662306a36Sopenharmony_ci	if (ret)
201762306a36Sopenharmony_ci		goto clean_unregister_notifiers;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	dev_notice(dev, "initialized (regs %pa, pool size %d) hw_ver:%08X %d.%d (%d)\n",
202062306a36Sopenharmony_ci		   &ss_res->start, descs_pool_size,
202162306a36Sopenharmony_ci		   cpsw->version, CPSW_MAJOR_VERSION(cpsw->version),
202262306a36Sopenharmony_ci		   CPSW_MINOR_VERSION(cpsw->version),
202362306a36Sopenharmony_ci		   CPSW_RTL_VERSION(cpsw->version));
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	pm_runtime_put(dev);
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	return 0;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ciclean_unregister_notifiers:
203062306a36Sopenharmony_ci	cpsw_unregister_notifiers(cpsw);
203162306a36Sopenharmony_ciclean_unregister_netdev:
203262306a36Sopenharmony_ci	cpsw_unregister_ports(cpsw);
203362306a36Sopenharmony_ciclean_cpts:
203462306a36Sopenharmony_ci	cpts_release(cpsw->cpts);
203562306a36Sopenharmony_ci	cpdma_ctlr_destroy(cpsw->dma);
203662306a36Sopenharmony_ciclean_dt_ret:
203762306a36Sopenharmony_ci	cpsw_remove_dt(cpsw);
203862306a36Sopenharmony_ci	pm_runtime_put_sync(dev);
203962306a36Sopenharmony_ci	pm_runtime_disable(dev);
204062306a36Sopenharmony_ci	return ret;
204162306a36Sopenharmony_ci}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_cistatic int cpsw_remove(struct platform_device *pdev)
204462306a36Sopenharmony_ci{
204562306a36Sopenharmony_ci	struct cpsw_common *cpsw = platform_get_drvdata(pdev);
204662306a36Sopenharmony_ci	int ret;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(&pdev->dev);
204962306a36Sopenharmony_ci	if (ret < 0)
205062306a36Sopenharmony_ci		return ret;
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci	cpsw_unregister_notifiers(cpsw);
205362306a36Sopenharmony_ci	cpsw_unregister_devlink(cpsw);
205462306a36Sopenharmony_ci	cpsw_unregister_ports(cpsw);
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	cpts_release(cpsw->cpts);
205762306a36Sopenharmony_ci	cpdma_ctlr_destroy(cpsw->dma);
205862306a36Sopenharmony_ci	cpsw_remove_dt(cpsw);
205962306a36Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
206062306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
206162306a36Sopenharmony_ci	return 0;
206262306a36Sopenharmony_ci}
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_cistatic int __maybe_unused cpsw_suspend(struct device *dev)
206562306a36Sopenharmony_ci{
206662306a36Sopenharmony_ci	struct cpsw_common *cpsw = dev_get_drvdata(dev);
206762306a36Sopenharmony_ci	int i;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	rtnl_lock();
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	for (i = 0; i < cpsw->data.slaves; i++) {
207262306a36Sopenharmony_ci		struct net_device *ndev = cpsw->slaves[i].ndev;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci		if (!(ndev && netif_running(ndev)))
207562306a36Sopenharmony_ci			continue;
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci		cpsw_ndo_stop(ndev);
207862306a36Sopenharmony_ci	}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	rtnl_unlock();
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	/* Select sleep pin state */
208362306a36Sopenharmony_ci	pinctrl_pm_select_sleep_state(dev);
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	return 0;
208662306a36Sopenharmony_ci}
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_cistatic int __maybe_unused cpsw_resume(struct device *dev)
208962306a36Sopenharmony_ci{
209062306a36Sopenharmony_ci	struct cpsw_common *cpsw = dev_get_drvdata(dev);
209162306a36Sopenharmony_ci	int i;
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	/* Select default pin state */
209462306a36Sopenharmony_ci	pinctrl_pm_select_default_state(dev);
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	/* shut up ASSERT_RTNL() warning in netif_set_real_num_tx/rx_queues */
209762306a36Sopenharmony_ci	rtnl_lock();
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	for (i = 0; i < cpsw->data.slaves; i++) {
210062306a36Sopenharmony_ci		struct net_device *ndev = cpsw->slaves[i].ndev;
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci		if (!(ndev && netif_running(ndev)))
210362306a36Sopenharmony_ci			continue;
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci		cpsw_ndo_open(ndev);
210662306a36Sopenharmony_ci	}
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	rtnl_unlock();
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	return 0;
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cpsw_pm_ops, cpsw_suspend, cpsw_resume);
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_cistatic struct platform_driver cpsw_driver = {
211662306a36Sopenharmony_ci	.driver = {
211762306a36Sopenharmony_ci		.name	 = "cpsw-switch",
211862306a36Sopenharmony_ci		.pm	 = &cpsw_pm_ops,
211962306a36Sopenharmony_ci		.of_match_table = cpsw_of_mtable,
212062306a36Sopenharmony_ci	},
212162306a36Sopenharmony_ci	.probe = cpsw_probe,
212262306a36Sopenharmony_ci	.remove = cpsw_remove,
212362306a36Sopenharmony_ci};
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_cimodule_platform_driver(cpsw_driver);
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
212862306a36Sopenharmony_ciMODULE_DESCRIPTION("TI CPSW switchdev Ethernet driver");
2129