xref: /openbmc/linux/drivers/net/ethernet/ti/cpsw.c (revision 51a95337)
168cf027fSGrygorii Strashko // SPDX-License-Identifier: GPL-2.0
2df828598SMugunthan V N /*
3df828598SMugunthan V N  * Texas Instruments Ethernet Switch Driver
4df828598SMugunthan V N  *
5df828598SMugunthan V N  * Copyright (C) 2012 Texas Instruments
6df828598SMugunthan V N  *
7df828598SMugunthan V N  */
8df828598SMugunthan V N 
9df828598SMugunthan V N #include <linux/kernel.h>
10df828598SMugunthan V N #include <linux/io.h>
11df828598SMugunthan V N #include <linux/clk.h>
12df828598SMugunthan V N #include <linux/timer.h>
13df828598SMugunthan V N #include <linux/module.h>
14df828598SMugunthan V N #include <linux/platform_device.h>
15df828598SMugunthan V N #include <linux/irqreturn.h>
16df828598SMugunthan V N #include <linux/interrupt.h>
17df828598SMugunthan V N #include <linux/if_ether.h>
18df828598SMugunthan V N #include <linux/etherdevice.h>
19df828598SMugunthan V N #include <linux/netdevice.h>
202e5b38abSRichard Cochran #include <linux/net_tstamp.h>
21df828598SMugunthan V N #include <linux/phy.h>
223ff18849SGrygorii Strashko #include <linux/phy/phy.h>
23df828598SMugunthan V N #include <linux/workqueue.h>
24df828598SMugunthan V N #include <linux/delay.h>
25f150bd7fSMugunthan V N #include <linux/pm_runtime.h>
26e2b3e493SArnd Bergmann #include <linux/gpio/consumer.h>
272eb32b0aSMugunthan V N #include <linux/of.h>
289e42f715SHeiko Schocher #include <linux/of_mdio.h>
292eb32b0aSMugunthan V N #include <linux/of_net.h>
302eb32b0aSMugunthan V N #include <linux/of_device.h>
313b72c2feSMugunthan V N #include <linux/if_vlan.h>
32514c6032SRandy Dunlap #include <linux/kmemleak.h>
339611d6d6SIvan Khoronzhuk #include <linux/sys_soc.h>
349ed4050cSIvan Khoronzhuk #include <net/page_pool.h>
359ed4050cSIvan Khoronzhuk #include <linux/bpf.h>
369ed4050cSIvan Khoronzhuk #include <linux/bpf_trace.h>
379ed4050cSIvan Khoronzhuk #include <linux/filter.h>
38df828598SMugunthan V N 
39739683b4SMugunthan V N #include <linux/pinctrl/consumer.h>
407929a668SIvan Khoronzhuk #include <net/pkt_cls.h>
41df828598SMugunthan V N 
42dbe34724SMugunthan V N #include "cpsw.h"
43df828598SMugunthan V N #include "cpsw_ale.h"
44814b4a67SGrygorii Strashko #include "cpsw_priv.h"
45cfc08345SGrygorii Strashko #include "cpsw_sl.h"
462e5b38abSRichard Cochran #include "cpts.h"
47df828598SMugunthan V N #include "davinci_cpdma.h"
48df828598SMugunthan V N 
4957d90148SIvan Khoronzhuk #include <net/pkt_sched.h>
5057d90148SIvan Khoronzhuk 
51df828598SMugunthan V N static int debug_level;
52df828598SMugunthan V N module_param(debug_level, int, 0);
53df828598SMugunthan V N MODULE_PARM_DESC(debug_level, "cpsw debug level (NETIF_MSG bits)");
54df828598SMugunthan V N 
55df828598SMugunthan V N static int ale_ageout = 10;
56df828598SMugunthan V N module_param(ale_ageout, int, 0);
57df828598SMugunthan V N MODULE_PARM_DESC(ale_ageout, "cpsw ale ageout interval (seconds)");
58df828598SMugunthan V N 
59df828598SMugunthan V N static int rx_packet_max = CPSW_MAX_PACKET_SIZE;
60df828598SMugunthan V N module_param(rx_packet_max, int, 0);
61df828598SMugunthan V N MODULE_PARM_DESC(rx_packet_max, "maximum receive packet size (bytes)");
62df828598SMugunthan V N 
6390225bf0SGrygorii Strashko static int descs_pool_size = CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT;
6490225bf0SGrygorii Strashko module_param(descs_pool_size, int, 0444);
6590225bf0SGrygorii Strashko MODULE_PARM_DESC(descs_pool_size, "Number of CPDMA CPPI descriptors in pool");
6690225bf0SGrygorii Strashko 
679ed4050cSIvan Khoronzhuk /* The buf includes headroom compatible with both skb and xdpf */
689ed4050cSIvan Khoronzhuk #define CPSW_HEADROOM_NA (max(XDP_PACKET_HEADROOM, NET_SKB_PAD) + NET_IP_ALIGN)
699ed4050cSIvan Khoronzhuk #define CPSW_HEADROOM  ALIGN(CPSW_HEADROOM_NA, sizeof(long))
709ed4050cSIvan Khoronzhuk 
71df828598SMugunthan V N #define for_each_slave(priv, func, arg...)				\
72df828598SMugunthan V N 	do {								\
736e6ceaedSSebastian Siewior 		struct cpsw_slave *slave;				\
74606f3993SIvan Khoronzhuk 		struct cpsw_common *cpsw = (priv)->cpsw;		\
756e6ceaedSSebastian Siewior 		int n;							\
76606f3993SIvan Khoronzhuk 		if (cpsw->data.dual_emac)				\
77606f3993SIvan Khoronzhuk 			(func)((cpsw)->slaves + priv->emac_port, ##arg);\
78d9ba8f9eSMugunthan V N 		else							\
79606f3993SIvan Khoronzhuk 			for (n = cpsw->data.slaves,			\
80606f3993SIvan Khoronzhuk 					slave = cpsw->slaves;		\
816e6ceaedSSebastian Siewior 					n; n--)				\
826e6ceaedSSebastian Siewior 				(func)(slave++, ##arg);			\
83df828598SMugunthan V N 	} while (0)
84d9ba8f9eSMugunthan V N 
859ed4050cSIvan Khoronzhuk #define CPSW_XMETA_OFFSET	ALIGN(sizeof(struct xdp_frame), sizeof(long))
869ed4050cSIvan Khoronzhuk 
879ed4050cSIvan Khoronzhuk #define CPSW_XDP_CONSUMED		1
889ed4050cSIvan Khoronzhuk #define CPSW_XDP_PASS			0
899ed4050cSIvan Khoronzhuk 
9051a95337SGrygorii Strashko static int cpsw_slave_index_priv(struct cpsw_common *cpsw,
9151a95337SGrygorii Strashko 				 struct cpsw_priv *priv)
9251a95337SGrygorii Strashko {
9351a95337SGrygorii Strashko 	return cpsw->data.dual_emac ? priv->emac_port : cpsw->data.active_slave;
9451a95337SGrygorii Strashko }
9551a95337SGrygorii Strashko 
9651a95337SGrygorii Strashko static int cpsw_get_slave_port(u32 slave_num)
9751a95337SGrygorii Strashko {
9851a95337SGrygorii Strashko 	return slave_num + 1;
9951a95337SGrygorii Strashko }
10051a95337SGrygorii Strashko 
10100fe4712SIvan Khoronzhuk static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev,
10200fe4712SIvan Khoronzhuk 				    __be16 proto, u16 vid);
10300fe4712SIvan Khoronzhuk 
1040cd8f9ccSMugunthan V N static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
1050cd8f9ccSMugunthan V N {
1062a05a622SIvan Khoronzhuk 	struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
1072a05a622SIvan Khoronzhuk 	struct cpsw_ale *ale = cpsw->ale;
1080cd8f9ccSMugunthan V N 	int i;
1090cd8f9ccSMugunthan V N 
110606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
1110cd8f9ccSMugunthan V N 		bool flag = false;
1120cd8f9ccSMugunthan V N 
1130cd8f9ccSMugunthan V N 		/* Enabling promiscuous mode for one interface will be
1140cd8f9ccSMugunthan V N 		 * common for both the interface as the interface shares
1150cd8f9ccSMugunthan V N 		 * the same hardware resource.
1160cd8f9ccSMugunthan V N 		 */
117606f3993SIvan Khoronzhuk 		for (i = 0; i < cpsw->data.slaves; i++)
118606f3993SIvan Khoronzhuk 			if (cpsw->slaves[i].ndev->flags & IFF_PROMISC)
1190cd8f9ccSMugunthan V N 				flag = true;
1200cd8f9ccSMugunthan V N 
1210cd8f9ccSMugunthan V N 		if (!enable && flag) {
1220cd8f9ccSMugunthan V N 			enable = true;
1230cd8f9ccSMugunthan V N 			dev_err(&ndev->dev, "promiscuity not disabled as the other interface is still in promiscuity mode\n");
1240cd8f9ccSMugunthan V N 		}
1250cd8f9ccSMugunthan V N 
1260cd8f9ccSMugunthan V N 		if (enable) {
1270cd8f9ccSMugunthan V N 			/* Enable Bypass */
1280cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_BYPASS, 1);
1290cd8f9ccSMugunthan V N 
1300cd8f9ccSMugunthan V N 			dev_dbg(&ndev->dev, "promiscuity enabled\n");
1310cd8f9ccSMugunthan V N 		} else {
1320cd8f9ccSMugunthan V N 			/* Disable Bypass */
1330cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_BYPASS, 0);
1340cd8f9ccSMugunthan V N 			dev_dbg(&ndev->dev, "promiscuity disabled\n");
1350cd8f9ccSMugunthan V N 		}
1360cd8f9ccSMugunthan V N 	} else {
1370cd8f9ccSMugunthan V N 		if (enable) {
1380cd8f9ccSMugunthan V N 			unsigned long timeout = jiffies + HZ;
1390cd8f9ccSMugunthan V N 
1406f979eb3SLennart Sorensen 			/* Disable Learn for all ports (host is port 0 and slaves are port 1 and up */
141606f3993SIvan Khoronzhuk 			for (i = 0; i <= cpsw->data.slaves; i++) {
1420cd8f9ccSMugunthan V N 				cpsw_ale_control_set(ale, i,
1430cd8f9ccSMugunthan V N 						     ALE_PORT_NOLEARN, 1);
1440cd8f9ccSMugunthan V N 				cpsw_ale_control_set(ale, i,
1450cd8f9ccSMugunthan V N 						     ALE_PORT_NO_SA_UPDATE, 1);
1460cd8f9ccSMugunthan V N 			}
1470cd8f9ccSMugunthan V N 
1480cd8f9ccSMugunthan V N 			/* Clear All Untouched entries */
1490cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
1500cd8f9ccSMugunthan V N 			do {
1510cd8f9ccSMugunthan V N 				cpu_relax();
1520cd8f9ccSMugunthan V N 				if (cpsw_ale_control_get(ale, 0, ALE_AGEOUT))
1530cd8f9ccSMugunthan V N 					break;
1540cd8f9ccSMugunthan V N 			} while (time_after(timeout, jiffies));
1550cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
1560cd8f9ccSMugunthan V N 
1570cd8f9ccSMugunthan V N 			/* Clear all mcast from ALE */
15861f1cef9SGrygorii Strashko 			cpsw_ale_flush_multicast(ale, ALE_ALL_PORTS, -1);
15915180ecaSIvan Khoronzhuk 			__hw_addr_ref_unsync_dev(&ndev->mc, ndev, NULL);
1600cd8f9ccSMugunthan V N 
1610cd8f9ccSMugunthan V N 			/* Flood All Unicast Packets to Host port */
1620cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 1);
1630cd8f9ccSMugunthan V N 			dev_dbg(&ndev->dev, "promiscuity enabled\n");
1640cd8f9ccSMugunthan V N 		} else {
1656f979eb3SLennart Sorensen 			/* Don't Flood All Unicast Packets to Host port */
1660cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 0);
1670cd8f9ccSMugunthan V N 
1686f979eb3SLennart Sorensen 			/* Enable Learn for all ports (host is port 0 and slaves are port 1 and up */
169606f3993SIvan Khoronzhuk 			for (i = 0; i <= cpsw->data.slaves; i++) {
1700cd8f9ccSMugunthan V N 				cpsw_ale_control_set(ale, i,
1710cd8f9ccSMugunthan V N 						     ALE_PORT_NOLEARN, 0);
1720cd8f9ccSMugunthan V N 				cpsw_ale_control_set(ale, i,
1730cd8f9ccSMugunthan V N 						     ALE_PORT_NO_SA_UPDATE, 0);
1740cd8f9ccSMugunthan V N 			}
1750cd8f9ccSMugunthan V N 			dev_dbg(&ndev->dev, "promiscuity disabled\n");
1760cd8f9ccSMugunthan V N 		}
1770cd8f9ccSMugunthan V N 	}
1780cd8f9ccSMugunthan V N }
1790cd8f9ccSMugunthan V N 
18015180ecaSIvan Khoronzhuk /**
18115180ecaSIvan Khoronzhuk  * cpsw_set_mc - adds multicast entry to the table if it's not added or deletes
18215180ecaSIvan Khoronzhuk  * if it's not deleted
18315180ecaSIvan Khoronzhuk  * @ndev: device to sync
18415180ecaSIvan Khoronzhuk  * @addr: address to be added or deleted
18515180ecaSIvan Khoronzhuk  * @vid: vlan id, if vid < 0 set/unset address for real device
18615180ecaSIvan Khoronzhuk  * @add: add address if the flag is set or remove otherwise
18715180ecaSIvan Khoronzhuk  */
18815180ecaSIvan Khoronzhuk static int cpsw_set_mc(struct net_device *ndev, const u8 *addr,
18915180ecaSIvan Khoronzhuk 		       int vid, int add)
1905c50a856SMugunthan V N {
1915c50a856SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
192606f3993SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
19315180ecaSIvan Khoronzhuk 	int mask, flags, ret;
19425906052SMugunthan V N 
19515180ecaSIvan Khoronzhuk 	if (vid < 0) {
19615180ecaSIvan Khoronzhuk 		if (cpsw->data.dual_emac)
197606f3993SIvan Khoronzhuk 			vid = cpsw->slaves[priv->emac_port].port_vlan;
19815180ecaSIvan Khoronzhuk 		else
1995da19489SIvan Khoronzhuk 			vid = 0;
2005da19489SIvan Khoronzhuk 	}
2015da19489SIvan Khoronzhuk 
20215180ecaSIvan Khoronzhuk 	mask = cpsw->data.dual_emac ? ALE_PORT_HOST : ALE_ALL_PORTS;
20315180ecaSIvan Khoronzhuk 	flags = vid ? ALE_VLAN : 0;
20415180ecaSIvan Khoronzhuk 
20515180ecaSIvan Khoronzhuk 	if (add)
20615180ecaSIvan Khoronzhuk 		ret = cpsw_ale_add_mcast(cpsw->ale, addr, mask, flags, vid, 0);
20715180ecaSIvan Khoronzhuk 	else
20815180ecaSIvan Khoronzhuk 		ret = cpsw_ale_del_mcast(cpsw->ale, addr, 0, flags, vid);
20915180ecaSIvan Khoronzhuk 
21015180ecaSIvan Khoronzhuk 	return ret;
21115180ecaSIvan Khoronzhuk }
21215180ecaSIvan Khoronzhuk 
21315180ecaSIvan Khoronzhuk static int cpsw_update_vlan_mc(struct net_device *vdev, int vid, void *ctx)
21415180ecaSIvan Khoronzhuk {
21515180ecaSIvan Khoronzhuk 	struct addr_sync_ctx *sync_ctx = ctx;
21615180ecaSIvan Khoronzhuk 	struct netdev_hw_addr *ha;
21715180ecaSIvan Khoronzhuk 	int found = 0, ret = 0;
21815180ecaSIvan Khoronzhuk 
21915180ecaSIvan Khoronzhuk 	if (!vdev || !(vdev->flags & IFF_UP))
22015180ecaSIvan Khoronzhuk 		return 0;
22115180ecaSIvan Khoronzhuk 
22215180ecaSIvan Khoronzhuk 	/* vlan address is relevant if its sync_cnt != 0 */
22315180ecaSIvan Khoronzhuk 	netdev_for_each_mc_addr(ha, vdev) {
22415180ecaSIvan Khoronzhuk 		if (ether_addr_equal(ha->addr, sync_ctx->addr)) {
22515180ecaSIvan Khoronzhuk 			found = ha->sync_cnt;
22615180ecaSIvan Khoronzhuk 			break;
22715180ecaSIvan Khoronzhuk 		}
22815180ecaSIvan Khoronzhuk 	}
22915180ecaSIvan Khoronzhuk 
23015180ecaSIvan Khoronzhuk 	if (found)
23115180ecaSIvan Khoronzhuk 		sync_ctx->consumed++;
23215180ecaSIvan Khoronzhuk 
23315180ecaSIvan Khoronzhuk 	if (sync_ctx->flush) {
23415180ecaSIvan Khoronzhuk 		if (!found)
23515180ecaSIvan Khoronzhuk 			cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0);
23615180ecaSIvan Khoronzhuk 		return 0;
23715180ecaSIvan Khoronzhuk 	}
23815180ecaSIvan Khoronzhuk 
23915180ecaSIvan Khoronzhuk 	if (found)
24015180ecaSIvan Khoronzhuk 		ret = cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 1);
24115180ecaSIvan Khoronzhuk 
24215180ecaSIvan Khoronzhuk 	return ret;
24315180ecaSIvan Khoronzhuk }
24415180ecaSIvan Khoronzhuk 
24515180ecaSIvan Khoronzhuk static int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr, int num)
24615180ecaSIvan Khoronzhuk {
24715180ecaSIvan Khoronzhuk 	struct addr_sync_ctx sync_ctx;
24815180ecaSIvan Khoronzhuk 	int ret;
24915180ecaSIvan Khoronzhuk 
25015180ecaSIvan Khoronzhuk 	sync_ctx.consumed = 0;
25115180ecaSIvan Khoronzhuk 	sync_ctx.addr = addr;
25215180ecaSIvan Khoronzhuk 	sync_ctx.ndev = ndev;
25315180ecaSIvan Khoronzhuk 	sync_ctx.flush = 0;
25415180ecaSIvan Khoronzhuk 
25515180ecaSIvan Khoronzhuk 	ret = vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx);
25615180ecaSIvan Khoronzhuk 	if (sync_ctx.consumed < num && !ret)
25715180ecaSIvan Khoronzhuk 		ret = cpsw_set_mc(ndev, addr, -1, 1);
25815180ecaSIvan Khoronzhuk 
25915180ecaSIvan Khoronzhuk 	return ret;
26015180ecaSIvan Khoronzhuk }
26115180ecaSIvan Khoronzhuk 
26215180ecaSIvan Khoronzhuk static int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr, int num)
26315180ecaSIvan Khoronzhuk {
26415180ecaSIvan Khoronzhuk 	struct addr_sync_ctx sync_ctx;
26515180ecaSIvan Khoronzhuk 
26615180ecaSIvan Khoronzhuk 	sync_ctx.consumed = 0;
26715180ecaSIvan Khoronzhuk 	sync_ctx.addr = addr;
26815180ecaSIvan Khoronzhuk 	sync_ctx.ndev = ndev;
26915180ecaSIvan Khoronzhuk 	sync_ctx.flush = 1;
27015180ecaSIvan Khoronzhuk 
27115180ecaSIvan Khoronzhuk 	vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx);
27215180ecaSIvan Khoronzhuk 	if (sync_ctx.consumed == num)
27315180ecaSIvan Khoronzhuk 		cpsw_set_mc(ndev, addr, -1, 0);
27415180ecaSIvan Khoronzhuk 
27515180ecaSIvan Khoronzhuk 	return 0;
27615180ecaSIvan Khoronzhuk }
27715180ecaSIvan Khoronzhuk 
27815180ecaSIvan Khoronzhuk static int cpsw_purge_vlan_mc(struct net_device *vdev, int vid, void *ctx)
27915180ecaSIvan Khoronzhuk {
28015180ecaSIvan Khoronzhuk 	struct addr_sync_ctx *sync_ctx = ctx;
28115180ecaSIvan Khoronzhuk 	struct netdev_hw_addr *ha;
28215180ecaSIvan Khoronzhuk 	int found = 0;
28315180ecaSIvan Khoronzhuk 
28415180ecaSIvan Khoronzhuk 	if (!vdev || !(vdev->flags & IFF_UP))
28515180ecaSIvan Khoronzhuk 		return 0;
28615180ecaSIvan Khoronzhuk 
28715180ecaSIvan Khoronzhuk 	/* vlan address is relevant if its sync_cnt != 0 */
28815180ecaSIvan Khoronzhuk 	netdev_for_each_mc_addr(ha, vdev) {
28915180ecaSIvan Khoronzhuk 		if (ether_addr_equal(ha->addr, sync_ctx->addr)) {
29015180ecaSIvan Khoronzhuk 			found = ha->sync_cnt;
29115180ecaSIvan Khoronzhuk 			break;
29215180ecaSIvan Khoronzhuk 		}
29315180ecaSIvan Khoronzhuk 	}
29415180ecaSIvan Khoronzhuk 
29515180ecaSIvan Khoronzhuk 	if (!found)
29615180ecaSIvan Khoronzhuk 		return 0;
29715180ecaSIvan Khoronzhuk 
29815180ecaSIvan Khoronzhuk 	sync_ctx->consumed++;
29915180ecaSIvan Khoronzhuk 	cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0);
30015180ecaSIvan Khoronzhuk 	return 0;
30115180ecaSIvan Khoronzhuk }
30215180ecaSIvan Khoronzhuk 
30315180ecaSIvan Khoronzhuk static int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num)
30415180ecaSIvan Khoronzhuk {
30515180ecaSIvan Khoronzhuk 	struct addr_sync_ctx sync_ctx;
30615180ecaSIvan Khoronzhuk 
30715180ecaSIvan Khoronzhuk 	sync_ctx.addr = addr;
30815180ecaSIvan Khoronzhuk 	sync_ctx.ndev = ndev;
30915180ecaSIvan Khoronzhuk 	sync_ctx.consumed = 0;
31015180ecaSIvan Khoronzhuk 
31115180ecaSIvan Khoronzhuk 	vlan_for_each(ndev, cpsw_purge_vlan_mc, &sync_ctx);
31215180ecaSIvan Khoronzhuk 	if (sync_ctx.consumed < num)
31315180ecaSIvan Khoronzhuk 		cpsw_set_mc(ndev, addr, -1, 0);
31415180ecaSIvan Khoronzhuk 
3155da19489SIvan Khoronzhuk 	return 0;
3165da19489SIvan Khoronzhuk }
3175da19489SIvan Khoronzhuk 
3185da19489SIvan Khoronzhuk static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
3195da19489SIvan Khoronzhuk {
32006095f34SGrygorii Strashko 	struct cpsw_priv *priv = netdev_priv(ndev);
32106095f34SGrygorii Strashko 	struct cpsw_common *cpsw = priv->cpsw;
32206095f34SGrygorii Strashko 	int slave_port = -1;
32306095f34SGrygorii Strashko 
32406095f34SGrygorii Strashko 	if (cpsw->data.dual_emac)
32506095f34SGrygorii Strashko 		slave_port = priv->emac_port + 1;
3265c50a856SMugunthan V N 
3275c50a856SMugunthan V N 	if (ndev->flags & IFF_PROMISC) {
3285c50a856SMugunthan V N 		/* Enable promiscuous mode */
3290cd8f9ccSMugunthan V N 		cpsw_set_promiscious(ndev, true);
33006095f34SGrygorii Strashko 		cpsw_ale_set_allmulti(cpsw->ale, IFF_ALLMULTI, slave_port);
3315c50a856SMugunthan V N 		return;
3320cd8f9ccSMugunthan V N 	} else {
3330cd8f9ccSMugunthan V N 		/* Disable promiscuous mode */
3340cd8f9ccSMugunthan V N 		cpsw_set_promiscious(ndev, false);
3355c50a856SMugunthan V N 	}
3365c50a856SMugunthan V N 
3371e5c4bc4SLennart Sorensen 	/* Restore allmulti on vlans if necessary */
33806095f34SGrygorii Strashko 	cpsw_ale_set_allmulti(cpsw->ale,
33906095f34SGrygorii Strashko 			      ndev->flags & IFF_ALLMULTI, slave_port);
3401e5c4bc4SLennart Sorensen 
34115180ecaSIvan Khoronzhuk 	/* add/remove mcast address either for real netdev or for vlan */
34215180ecaSIvan Khoronzhuk 	__hw_addr_ref_sync_dev(&ndev->mc, ndev, cpsw_add_mc_addr,
34315180ecaSIvan Khoronzhuk 			       cpsw_del_mc_addr);
3445c50a856SMugunthan V N }
3455c50a856SMugunthan V N 
346c24eef28SGrygorii Strashko void cpsw_intr_enable(struct cpsw_common *cpsw)
347df828598SMugunthan V N {
348dda5f5feSGrygorii Strashko 	writel_relaxed(0xFF, &cpsw->wr_regs->tx_en);
349dda5f5feSGrygorii Strashko 	writel_relaxed(0xFF, &cpsw->wr_regs->rx_en);
350df828598SMugunthan V N 
3512c836bd9SIvan Khoronzhuk 	cpdma_ctlr_int_ctrl(cpsw->dma, true);
352df828598SMugunthan V N 	return;
353df828598SMugunthan V N }
354df828598SMugunthan V N 
355c24eef28SGrygorii Strashko void cpsw_intr_disable(struct cpsw_common *cpsw)
356df828598SMugunthan V N {
357dda5f5feSGrygorii Strashko 	writel_relaxed(0, &cpsw->wr_regs->tx_en);
358dda5f5feSGrygorii Strashko 	writel_relaxed(0, &cpsw->wr_regs->rx_en);
359df828598SMugunthan V N 
3602c836bd9SIvan Khoronzhuk 	cpdma_ctlr_int_ctrl(cpsw->dma, false);
361df828598SMugunthan V N 	return;
362df828598SMugunthan V N }
363df828598SMugunthan V N 
3649ed4050cSIvan Khoronzhuk static int cpsw_is_xdpf_handle(void *handle)
3659ed4050cSIvan Khoronzhuk {
3669ed4050cSIvan Khoronzhuk 	return (unsigned long)handle & BIT(0);
3679ed4050cSIvan Khoronzhuk }
3689ed4050cSIvan Khoronzhuk 
3699ed4050cSIvan Khoronzhuk static void *cpsw_xdpf_to_handle(struct xdp_frame *xdpf)
3709ed4050cSIvan Khoronzhuk {
3719ed4050cSIvan Khoronzhuk 	return (void *)((unsigned long)xdpf | BIT(0));
3729ed4050cSIvan Khoronzhuk }
3739ed4050cSIvan Khoronzhuk 
3749ed4050cSIvan Khoronzhuk static struct xdp_frame *cpsw_handle_to_xdpf(void *handle)
3759ed4050cSIvan Khoronzhuk {
3769ed4050cSIvan Khoronzhuk 	return (struct xdp_frame *)((unsigned long)handle & ~BIT(0));
3779ed4050cSIvan Khoronzhuk }
3789ed4050cSIvan Khoronzhuk 
3799ed4050cSIvan Khoronzhuk struct __aligned(sizeof(long)) cpsw_meta_xdp {
3809ed4050cSIvan Khoronzhuk 	struct net_device *ndev;
3819ed4050cSIvan Khoronzhuk 	int ch;
3829ed4050cSIvan Khoronzhuk };
3839ed4050cSIvan Khoronzhuk 
384c24eef28SGrygorii Strashko void cpsw_tx_handler(void *token, int len, int status)
385df828598SMugunthan V N {
3869ed4050cSIvan Khoronzhuk 	struct cpsw_meta_xdp	*xmeta;
3879ed4050cSIvan Khoronzhuk 	struct xdp_frame	*xdpf;
3889ed4050cSIvan Khoronzhuk 	struct net_device	*ndev;
389e05107e6SIvan Khoronzhuk 	struct netdev_queue	*txq;
3909ed4050cSIvan Khoronzhuk 	struct sk_buff		*skb;
3919ed4050cSIvan Khoronzhuk 	int			ch;
3929ed4050cSIvan Khoronzhuk 
3939ed4050cSIvan Khoronzhuk 	if (cpsw_is_xdpf_handle(token)) {
3949ed4050cSIvan Khoronzhuk 		xdpf = cpsw_handle_to_xdpf(token);
3959ed4050cSIvan Khoronzhuk 		xmeta = (void *)xdpf + CPSW_XMETA_OFFSET;
3969ed4050cSIvan Khoronzhuk 		ndev = xmeta->ndev;
3979ed4050cSIvan Khoronzhuk 		ch = xmeta->ch;
3989ed4050cSIvan Khoronzhuk 		xdp_return_frame(xdpf);
3999ed4050cSIvan Khoronzhuk 	} else {
4009ed4050cSIvan Khoronzhuk 		skb = token;
4019ed4050cSIvan Khoronzhuk 		ndev = skb->dev;
4029ed4050cSIvan Khoronzhuk 		ch = skb_get_queue_mapping(skb);
4039ed4050cSIvan Khoronzhuk 		cpts_tx_timestamp(ndev_to_cpsw(ndev)->cpts, skb);
4049ed4050cSIvan Khoronzhuk 		dev_kfree_skb_any(skb);
4059ed4050cSIvan Khoronzhuk 	}
406df828598SMugunthan V N 
407fae50823SMugunthan V N 	/* Check whether the queue is stopped due to stalled tx dma, if the
408fae50823SMugunthan V N 	 * queue is stopped then start the queue as we have free desc for tx
409fae50823SMugunthan V N 	 */
4109ed4050cSIvan Khoronzhuk 	txq = netdev_get_tx_queue(ndev, ch);
411e05107e6SIvan Khoronzhuk 	if (unlikely(netif_tx_queue_stopped(txq)))
412e05107e6SIvan Khoronzhuk 		netif_tx_wake_queue(txq);
413e05107e6SIvan Khoronzhuk 
4148dc43ddcSTobias Klauser 	ndev->stats.tx_packets++;
4158dc43ddcSTobias Klauser 	ndev->stats.tx_bytes += len;
416df828598SMugunthan V N }
417df828598SMugunthan V N 
418a3a41d2fSGrygorii Strashko static void cpsw_rx_vlan_encap(struct sk_buff *skb)
419a3a41d2fSGrygorii Strashko {
420a3a41d2fSGrygorii Strashko 	struct cpsw_priv *priv = netdev_priv(skb->dev);
421a3a41d2fSGrygorii Strashko 	struct cpsw_common *cpsw = priv->cpsw;
422a3a41d2fSGrygorii Strashko 	u32 rx_vlan_encap_hdr = *((u32 *)skb->data);
423a3a41d2fSGrygorii Strashko 	u16 vtag, vid, prio, pkt_type;
424a3a41d2fSGrygorii Strashko 
425a3a41d2fSGrygorii Strashko 	/* Remove VLAN header encapsulation word */
426a3a41d2fSGrygorii Strashko 	skb_pull(skb, CPSW_RX_VLAN_ENCAP_HDR_SIZE);
427a3a41d2fSGrygorii Strashko 
428a3a41d2fSGrygorii Strashko 	pkt_type = (rx_vlan_encap_hdr >>
429a3a41d2fSGrygorii Strashko 		    CPSW_RX_VLAN_ENCAP_HDR_PKT_TYPE_SHIFT) &
430a3a41d2fSGrygorii Strashko 		    CPSW_RX_VLAN_ENCAP_HDR_PKT_TYPE_MSK;
431a3a41d2fSGrygorii Strashko 	/* Ignore unknown & Priority-tagged packets*/
432a3a41d2fSGrygorii Strashko 	if (pkt_type == CPSW_RX_VLAN_ENCAP_HDR_PKT_RESERV ||
433a3a41d2fSGrygorii Strashko 	    pkt_type == CPSW_RX_VLAN_ENCAP_HDR_PKT_PRIO_TAG)
434a3a41d2fSGrygorii Strashko 		return;
435a3a41d2fSGrygorii Strashko 
436a3a41d2fSGrygorii Strashko 	vid = (rx_vlan_encap_hdr >>
437a3a41d2fSGrygorii Strashko 	       CPSW_RX_VLAN_ENCAP_HDR_VID_SHIFT) &
438a3a41d2fSGrygorii Strashko 	       VLAN_VID_MASK;
439a3a41d2fSGrygorii Strashko 	/* Ignore vid 0 and pass packet as is */
440a3a41d2fSGrygorii Strashko 	if (!vid)
441a3a41d2fSGrygorii Strashko 		return;
442a3a41d2fSGrygorii Strashko 
4434b41d343SGrygorii Strashko 	/* Untag P0 packets if set for vlan */
4444b41d343SGrygorii Strashko 	if (!cpsw_ale_get_vlan_p0_untag(cpsw->ale, vid)) {
445a3a41d2fSGrygorii Strashko 		prio = (rx_vlan_encap_hdr >>
446a3a41d2fSGrygorii Strashko 			CPSW_RX_VLAN_ENCAP_HDR_PRIO_SHIFT) &
447a3a41d2fSGrygorii Strashko 			CPSW_RX_VLAN_ENCAP_HDR_PRIO_MSK;
448a3a41d2fSGrygorii Strashko 
449a3a41d2fSGrygorii Strashko 		vtag = (prio << VLAN_PRIO_SHIFT) | vid;
450a3a41d2fSGrygorii Strashko 		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag);
4514b41d343SGrygorii Strashko 	}
452a3a41d2fSGrygorii Strashko 
453a3a41d2fSGrygorii Strashko 	/* strip vlan tag for VLAN-tagged packet */
454a3a41d2fSGrygorii Strashko 	if (pkt_type == CPSW_RX_VLAN_ENCAP_HDR_PKT_VLAN_TAG) {
455a3a41d2fSGrygorii Strashko 		memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
456a3a41d2fSGrygorii Strashko 		skb_pull(skb, VLAN_HLEN);
457a3a41d2fSGrygorii Strashko 	}
458a3a41d2fSGrygorii Strashko }
459a3a41d2fSGrygorii Strashko 
4609ed4050cSIvan Khoronzhuk static int cpsw_xdp_tx_frame(struct cpsw_priv *priv, struct xdp_frame *xdpf,
4619ed4050cSIvan Khoronzhuk 			     struct page *page)
4629ed4050cSIvan Khoronzhuk {
4639ed4050cSIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
4649ed4050cSIvan Khoronzhuk 	struct cpsw_meta_xdp *xmeta;
4659ed4050cSIvan Khoronzhuk 	struct cpdma_chan *txch;
4669ed4050cSIvan Khoronzhuk 	dma_addr_t dma;
4679ed4050cSIvan Khoronzhuk 	int ret, port;
4689ed4050cSIvan Khoronzhuk 
4699ed4050cSIvan Khoronzhuk 	xmeta = (void *)xdpf + CPSW_XMETA_OFFSET;
4709ed4050cSIvan Khoronzhuk 	xmeta->ndev = priv->ndev;
4719ed4050cSIvan Khoronzhuk 	xmeta->ch = 0;
4729ed4050cSIvan Khoronzhuk 	txch = cpsw->txv[0].ch;
4739ed4050cSIvan Khoronzhuk 
4749ed4050cSIvan Khoronzhuk 	port = priv->emac_port + cpsw->data.dual_emac;
4759ed4050cSIvan Khoronzhuk 	if (page) {
4769ed4050cSIvan Khoronzhuk 		dma = page_pool_get_dma_addr(page);
4779ed4050cSIvan Khoronzhuk 		dma += xdpf->headroom + sizeof(struct xdp_frame);
4789ed4050cSIvan Khoronzhuk 		ret = cpdma_chan_submit_mapped(txch, cpsw_xdpf_to_handle(xdpf),
4799ed4050cSIvan Khoronzhuk 					       dma, xdpf->len, port);
4809ed4050cSIvan Khoronzhuk 	} else {
4819ed4050cSIvan Khoronzhuk 		if (sizeof(*xmeta) > xdpf->headroom) {
4829ed4050cSIvan Khoronzhuk 			xdp_return_frame_rx_napi(xdpf);
4839ed4050cSIvan Khoronzhuk 			return -EINVAL;
4849ed4050cSIvan Khoronzhuk 		}
4859ed4050cSIvan Khoronzhuk 
4869ed4050cSIvan Khoronzhuk 		ret = cpdma_chan_submit(txch, cpsw_xdpf_to_handle(xdpf),
4879ed4050cSIvan Khoronzhuk 					xdpf->data, xdpf->len, port);
4889ed4050cSIvan Khoronzhuk 	}
4899ed4050cSIvan Khoronzhuk 
4909ed4050cSIvan Khoronzhuk 	if (ret) {
4919ed4050cSIvan Khoronzhuk 		priv->ndev->stats.tx_dropped++;
4929ed4050cSIvan Khoronzhuk 		xdp_return_frame_rx_napi(xdpf);
4939ed4050cSIvan Khoronzhuk 	}
4949ed4050cSIvan Khoronzhuk 
4959ed4050cSIvan Khoronzhuk 	return ret;
4969ed4050cSIvan Khoronzhuk }
4979ed4050cSIvan Khoronzhuk 
4989ed4050cSIvan Khoronzhuk static int cpsw_run_xdp(struct cpsw_priv *priv, int ch, struct xdp_buff *xdp,
4999ed4050cSIvan Khoronzhuk 			struct page *page)
5009ed4050cSIvan Khoronzhuk {
5019ed4050cSIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
5029ed4050cSIvan Khoronzhuk 	struct net_device *ndev = priv->ndev;
5039ed4050cSIvan Khoronzhuk 	int ret = CPSW_XDP_CONSUMED;
5049ed4050cSIvan Khoronzhuk 	struct xdp_frame *xdpf;
5059ed4050cSIvan Khoronzhuk 	struct bpf_prog *prog;
5069ed4050cSIvan Khoronzhuk 	u32 act;
5079ed4050cSIvan Khoronzhuk 
5089ed4050cSIvan Khoronzhuk 	rcu_read_lock();
5099ed4050cSIvan Khoronzhuk 
5109ed4050cSIvan Khoronzhuk 	prog = READ_ONCE(priv->xdp_prog);
5119ed4050cSIvan Khoronzhuk 	if (!prog) {
5129ed4050cSIvan Khoronzhuk 		ret = CPSW_XDP_PASS;
5139ed4050cSIvan Khoronzhuk 		goto out;
5149ed4050cSIvan Khoronzhuk 	}
5159ed4050cSIvan Khoronzhuk 
5169ed4050cSIvan Khoronzhuk 	act = bpf_prog_run_xdp(prog, xdp);
5179ed4050cSIvan Khoronzhuk 	switch (act) {
5189ed4050cSIvan Khoronzhuk 	case XDP_PASS:
5199ed4050cSIvan Khoronzhuk 		ret = CPSW_XDP_PASS;
5209ed4050cSIvan Khoronzhuk 		break;
5219ed4050cSIvan Khoronzhuk 	case XDP_TX:
5229ed4050cSIvan Khoronzhuk 		xdpf = convert_to_xdp_frame(xdp);
5239ed4050cSIvan Khoronzhuk 		if (unlikely(!xdpf))
5249ed4050cSIvan Khoronzhuk 			goto drop;
5259ed4050cSIvan Khoronzhuk 
5269ed4050cSIvan Khoronzhuk 		cpsw_xdp_tx_frame(priv, xdpf, page);
5279ed4050cSIvan Khoronzhuk 		break;
5289ed4050cSIvan Khoronzhuk 	case XDP_REDIRECT:
5299ed4050cSIvan Khoronzhuk 		if (xdp_do_redirect(ndev, xdp, prog))
5309ed4050cSIvan Khoronzhuk 			goto drop;
5319ed4050cSIvan Khoronzhuk 
5329ed4050cSIvan Khoronzhuk 		/*  Have to flush here, per packet, instead of doing it in bulk
5339ed4050cSIvan Khoronzhuk 		 *  at the end of the napi handler. The RX devices on this
5349ed4050cSIvan Khoronzhuk 		 *  particular hardware is sharing a common queue, so the
5359ed4050cSIvan Khoronzhuk 		 *  incoming device might change per packet.
5369ed4050cSIvan Khoronzhuk 		 */
5379ed4050cSIvan Khoronzhuk 		xdp_do_flush_map();
5389ed4050cSIvan Khoronzhuk 		break;
5399ed4050cSIvan Khoronzhuk 	default:
5409ed4050cSIvan Khoronzhuk 		bpf_warn_invalid_xdp_action(act);
5419ed4050cSIvan Khoronzhuk 		/* fall through */
5429ed4050cSIvan Khoronzhuk 	case XDP_ABORTED:
5439ed4050cSIvan Khoronzhuk 		trace_xdp_exception(ndev, prog, act);
5449ed4050cSIvan Khoronzhuk 		/* fall through -- handle aborts by dropping packet */
5459ed4050cSIvan Khoronzhuk 	case XDP_DROP:
5469ed4050cSIvan Khoronzhuk 		goto drop;
5479ed4050cSIvan Khoronzhuk 	}
5489ed4050cSIvan Khoronzhuk out:
5499ed4050cSIvan Khoronzhuk 	rcu_read_unlock();
5509ed4050cSIvan Khoronzhuk 	return ret;
5519ed4050cSIvan Khoronzhuk drop:
5529ed4050cSIvan Khoronzhuk 	rcu_read_unlock();
5539ed4050cSIvan Khoronzhuk 	page_pool_recycle_direct(cpsw->page_pool[ch], page);
5549ed4050cSIvan Khoronzhuk 	return ret;
5559ed4050cSIvan Khoronzhuk }
5569ed4050cSIvan Khoronzhuk 
5579ed4050cSIvan Khoronzhuk static unsigned int cpsw_rxbuf_total_len(unsigned int len)
5589ed4050cSIvan Khoronzhuk {
5599ed4050cSIvan Khoronzhuk 	len += CPSW_HEADROOM;
5609ed4050cSIvan Khoronzhuk 	len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
5619ed4050cSIvan Khoronzhuk 
5629ed4050cSIvan Khoronzhuk 	return SKB_DATA_ALIGN(len);
5639ed4050cSIvan Khoronzhuk }
5649ed4050cSIvan Khoronzhuk 
5659ed4050cSIvan Khoronzhuk static struct page_pool *cpsw_create_page_pool(struct cpsw_common *cpsw,
5669ed4050cSIvan Khoronzhuk 					       int size)
5679ed4050cSIvan Khoronzhuk {
5689ed4050cSIvan Khoronzhuk 	struct page_pool_params pp_params;
5699ed4050cSIvan Khoronzhuk 	struct page_pool *pool;
5709ed4050cSIvan Khoronzhuk 
5719ed4050cSIvan Khoronzhuk 	pp_params.order = 0;
5729ed4050cSIvan Khoronzhuk 	pp_params.flags = PP_FLAG_DMA_MAP;
5739ed4050cSIvan Khoronzhuk 	pp_params.pool_size = size;
5749ed4050cSIvan Khoronzhuk 	pp_params.nid = NUMA_NO_NODE;
5759ed4050cSIvan Khoronzhuk 	pp_params.dma_dir = DMA_BIDIRECTIONAL;
5769ed4050cSIvan Khoronzhuk 	pp_params.dev = cpsw->dev;
5779ed4050cSIvan Khoronzhuk 
5789ed4050cSIvan Khoronzhuk 	pool = page_pool_create(&pp_params);
5799ed4050cSIvan Khoronzhuk 	if (IS_ERR(pool))
5809ed4050cSIvan Khoronzhuk 		dev_err(cpsw->dev, "cannot create rx page pool\n");
5819ed4050cSIvan Khoronzhuk 
5829ed4050cSIvan Khoronzhuk 	return pool;
5839ed4050cSIvan Khoronzhuk }
5849ed4050cSIvan Khoronzhuk 
5859ed4050cSIvan Khoronzhuk static int cpsw_ndev_create_xdp_rxq(struct cpsw_priv *priv, int ch)
5869ed4050cSIvan Khoronzhuk {
5879ed4050cSIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
5889ed4050cSIvan Khoronzhuk 	struct xdp_rxq_info *rxq;
5899ed4050cSIvan Khoronzhuk 	struct page_pool *pool;
5909ed4050cSIvan Khoronzhuk 	int ret;
5919ed4050cSIvan Khoronzhuk 
5929ed4050cSIvan Khoronzhuk 	pool = cpsw->page_pool[ch];
5939ed4050cSIvan Khoronzhuk 	rxq = &priv->xdp_rxq[ch];
5949ed4050cSIvan Khoronzhuk 
5959ed4050cSIvan Khoronzhuk 	ret = xdp_rxq_info_reg(rxq, priv->ndev, ch);
5969ed4050cSIvan Khoronzhuk 	if (ret)
5979ed4050cSIvan Khoronzhuk 		return ret;
5989ed4050cSIvan Khoronzhuk 
5999ed4050cSIvan Khoronzhuk 	ret = xdp_rxq_info_reg_mem_model(rxq, MEM_TYPE_PAGE_POOL, pool);
6009ed4050cSIvan Khoronzhuk 	if (ret)
6019ed4050cSIvan Khoronzhuk 		xdp_rxq_info_unreg(rxq);
6029ed4050cSIvan Khoronzhuk 
6039ed4050cSIvan Khoronzhuk 	return ret;
6049ed4050cSIvan Khoronzhuk }
6059ed4050cSIvan Khoronzhuk 
6069ed4050cSIvan Khoronzhuk static void cpsw_ndev_destroy_xdp_rxq(struct cpsw_priv *priv, int ch)
6079ed4050cSIvan Khoronzhuk {
6089ed4050cSIvan Khoronzhuk 	struct xdp_rxq_info *rxq = &priv->xdp_rxq[ch];
6099ed4050cSIvan Khoronzhuk 
6109ed4050cSIvan Khoronzhuk 	if (!xdp_rxq_info_is_reg(rxq))
6119ed4050cSIvan Khoronzhuk 		return;
6129ed4050cSIvan Khoronzhuk 
6139ed4050cSIvan Khoronzhuk 	xdp_rxq_info_unreg(rxq);
6149ed4050cSIvan Khoronzhuk }
6159ed4050cSIvan Khoronzhuk 
6169ed4050cSIvan Khoronzhuk static int cpsw_create_rx_pool(struct cpsw_common *cpsw, int ch)
6179ed4050cSIvan Khoronzhuk {
6189ed4050cSIvan Khoronzhuk 	struct page_pool *pool;
6199ed4050cSIvan Khoronzhuk 	int ret = 0, pool_size;
6209ed4050cSIvan Khoronzhuk 
6219ed4050cSIvan Khoronzhuk 	pool_size = cpdma_chan_get_rx_buf_num(cpsw->rxv[ch].ch);
6229ed4050cSIvan Khoronzhuk 	pool = cpsw_create_page_pool(cpsw, pool_size);
6239ed4050cSIvan Khoronzhuk 	if (IS_ERR(pool))
6249ed4050cSIvan Khoronzhuk 		ret = PTR_ERR(pool);
6259ed4050cSIvan Khoronzhuk 	else
6269ed4050cSIvan Khoronzhuk 		cpsw->page_pool[ch] = pool;
6279ed4050cSIvan Khoronzhuk 
6289ed4050cSIvan Khoronzhuk 	return ret;
6299ed4050cSIvan Khoronzhuk }
6309ed4050cSIvan Khoronzhuk 
6319ed4050cSIvan Khoronzhuk void cpsw_destroy_xdp_rxqs(struct cpsw_common *cpsw)
6329ed4050cSIvan Khoronzhuk {
6339ed4050cSIvan Khoronzhuk 	struct net_device *ndev;
6349ed4050cSIvan Khoronzhuk 	int i, ch;
6359ed4050cSIvan Khoronzhuk 
6369ed4050cSIvan Khoronzhuk 	for (ch = 0; ch < cpsw->rx_ch_num; ch++) {
6379ed4050cSIvan Khoronzhuk 		for (i = 0; i < cpsw->data.slaves; i++) {
6389ed4050cSIvan Khoronzhuk 			ndev = cpsw->slaves[i].ndev;
6399ed4050cSIvan Khoronzhuk 			if (!ndev)
6409ed4050cSIvan Khoronzhuk 				continue;
6419ed4050cSIvan Khoronzhuk 
6429ed4050cSIvan Khoronzhuk 			cpsw_ndev_destroy_xdp_rxq(netdev_priv(ndev), ch);
6439ed4050cSIvan Khoronzhuk 		}
6449ed4050cSIvan Khoronzhuk 
6459ed4050cSIvan Khoronzhuk 		page_pool_destroy(cpsw->page_pool[ch]);
6469ed4050cSIvan Khoronzhuk 		cpsw->page_pool[ch] = NULL;
6479ed4050cSIvan Khoronzhuk 	}
6489ed4050cSIvan Khoronzhuk }
6499ed4050cSIvan Khoronzhuk 
6509ed4050cSIvan Khoronzhuk int cpsw_create_xdp_rxqs(struct cpsw_common *cpsw)
6519ed4050cSIvan Khoronzhuk {
6529ed4050cSIvan Khoronzhuk 	struct net_device *ndev;
6539ed4050cSIvan Khoronzhuk 	int i, ch, ret;
6549ed4050cSIvan Khoronzhuk 
6559ed4050cSIvan Khoronzhuk 	for (ch = 0; ch < cpsw->rx_ch_num; ch++) {
6569ed4050cSIvan Khoronzhuk 		ret = cpsw_create_rx_pool(cpsw, ch);
6579ed4050cSIvan Khoronzhuk 		if (ret)
6589ed4050cSIvan Khoronzhuk 			goto err_cleanup;
6599ed4050cSIvan Khoronzhuk 
6609ed4050cSIvan Khoronzhuk 		/* using same page pool is allowed as no running rx handlers
6619ed4050cSIvan Khoronzhuk 		 * simultaneously for both ndevs
6629ed4050cSIvan Khoronzhuk 		 */
6639ed4050cSIvan Khoronzhuk 		for (i = 0; i < cpsw->data.slaves; i++) {
6649ed4050cSIvan Khoronzhuk 			ndev = cpsw->slaves[i].ndev;
6659ed4050cSIvan Khoronzhuk 			if (!ndev)
6669ed4050cSIvan Khoronzhuk 				continue;
6679ed4050cSIvan Khoronzhuk 
6689ed4050cSIvan Khoronzhuk 			ret = cpsw_ndev_create_xdp_rxq(netdev_priv(ndev), ch);
6699ed4050cSIvan Khoronzhuk 			if (ret)
6709ed4050cSIvan Khoronzhuk 				goto err_cleanup;
6719ed4050cSIvan Khoronzhuk 		}
6729ed4050cSIvan Khoronzhuk 	}
6739ed4050cSIvan Khoronzhuk 
6749ed4050cSIvan Khoronzhuk 	return 0;
6759ed4050cSIvan Khoronzhuk 
6769ed4050cSIvan Khoronzhuk err_cleanup:
6779ed4050cSIvan Khoronzhuk 	cpsw_destroy_xdp_rxqs(cpsw);
6789ed4050cSIvan Khoronzhuk 
6799ed4050cSIvan Khoronzhuk 	return ret;
6809ed4050cSIvan Khoronzhuk }
6819ed4050cSIvan Khoronzhuk 
6821a3b5056SOlof Johansson static void cpsw_rx_handler(void *token, int len, int status)
683df828598SMugunthan V N {
6849ed4050cSIvan Khoronzhuk 	struct page		*new_page, *page = token;
6859ed4050cSIvan Khoronzhuk 	void			*pa = page_address(page);
6869ed4050cSIvan Khoronzhuk 	struct cpsw_meta_xdp	*xmeta = pa + CPSW_XMETA_OFFSET;
6879ed4050cSIvan Khoronzhuk 	struct cpsw_common	*cpsw = ndev_to_cpsw(xmeta->ndev);
6889ed4050cSIvan Khoronzhuk 	int			pkt_size = cpsw->rx_packet_max;
6899ed4050cSIvan Khoronzhuk 	int			ret = 0, port, ch = xmeta->ch;
6909ed4050cSIvan Khoronzhuk 	int			headroom = CPSW_HEADROOM;
6919ed4050cSIvan Khoronzhuk 	struct net_device	*ndev = xmeta->ndev;
692a9423120SIvan Khoronzhuk 	struct cpsw_priv	*priv;
6939ed4050cSIvan Khoronzhuk 	struct page_pool	*pool;
6949ed4050cSIvan Khoronzhuk 	struct sk_buff		*skb;
6959ed4050cSIvan Khoronzhuk 	struct xdp_buff		xdp;
6969ed4050cSIvan Khoronzhuk 	dma_addr_t		dma;
697df828598SMugunthan V N 
6989ed4050cSIvan Khoronzhuk 	if (cpsw->data.dual_emac && status >= 0) {
699fea49f60SIvan Khoronzhuk 		port = CPDMA_RX_SOURCE_PORT(status);
7009ed4050cSIvan Khoronzhuk 		if (port)
701fea49f60SIvan Khoronzhuk 			ndev = cpsw->slaves[--port].ndev;
702fea49f60SIvan Khoronzhuk 	}
703d9ba8f9eSMugunthan V N 
7049ed4050cSIvan Khoronzhuk 	priv = netdev_priv(ndev);
7059ed4050cSIvan Khoronzhuk 	pool = cpsw->page_pool[ch];
70616e5c57dSMugunthan V N 	if (unlikely(status < 0) || unlikely(!netif_running(ndev))) {
707a0e2c822SMugunthan V N 		/* In dual emac mode check for all interfaces */
708d5bc1613SIvan Khoronzhuk 		if (cpsw->data.dual_emac && cpsw->usage_count &&
709fe734d0aSIvan Khoronzhuk 		    (status >= 0)) {
710a0e2c822SMugunthan V N 			/* The packet received is for the interface which
711a0e2c822SMugunthan V N 			 * is already down and the other interface is up
712dbedd44eSJoe Perches 			 * and running, instead of freeing which results
713a0e2c822SMugunthan V N 			 * in reducing of the number of rx descriptor in
7149ed4050cSIvan Khoronzhuk 			 * DMA engine, requeue page back to cpdma.
715a0e2c822SMugunthan V N 			 */
7169ed4050cSIvan Khoronzhuk 			new_page = page;
717a0e2c822SMugunthan V N 			goto requeue;
718a0e2c822SMugunthan V N 		}
719a0e2c822SMugunthan V N 
7209ed4050cSIvan Khoronzhuk 		/* the interface is going down, pages are purged */
7219ed4050cSIvan Khoronzhuk 		page_pool_recycle_direct(pool, page);
722df828598SMugunthan V N 		return;
723df828598SMugunthan V N 	}
724b4727e69SSebastian Siewior 
7259ed4050cSIvan Khoronzhuk 	new_page = page_pool_dev_alloc_pages(pool);
7269ed4050cSIvan Khoronzhuk 	if (unlikely(!new_page)) {
7279ed4050cSIvan Khoronzhuk 		new_page = page;
7289ed4050cSIvan Khoronzhuk 		ndev->stats.rx_dropped++;
7299ed4050cSIvan Khoronzhuk 		goto requeue;
7309ed4050cSIvan Khoronzhuk 	}
7319ed4050cSIvan Khoronzhuk 
7329ed4050cSIvan Khoronzhuk 	if (priv->xdp_prog) {
7339ed4050cSIvan Khoronzhuk 		if (status & CPDMA_RX_VLAN_ENCAP) {
7349ed4050cSIvan Khoronzhuk 			xdp.data = pa + CPSW_HEADROOM +
7359ed4050cSIvan Khoronzhuk 				   CPSW_RX_VLAN_ENCAP_HDR_SIZE;
7369ed4050cSIvan Khoronzhuk 			xdp.data_end = xdp.data + len -
7379ed4050cSIvan Khoronzhuk 				       CPSW_RX_VLAN_ENCAP_HDR_SIZE;
7389ed4050cSIvan Khoronzhuk 		} else {
7399ed4050cSIvan Khoronzhuk 			xdp.data = pa + CPSW_HEADROOM;
7409ed4050cSIvan Khoronzhuk 			xdp.data_end = xdp.data + len;
7419ed4050cSIvan Khoronzhuk 		}
7429ed4050cSIvan Khoronzhuk 
7439ed4050cSIvan Khoronzhuk 		xdp_set_data_meta_invalid(&xdp);
7449ed4050cSIvan Khoronzhuk 
7459ed4050cSIvan Khoronzhuk 		xdp.data_hard_start = pa;
7469ed4050cSIvan Khoronzhuk 		xdp.rxq = &priv->xdp_rxq[ch];
7479ed4050cSIvan Khoronzhuk 
7489ed4050cSIvan Khoronzhuk 		ret = cpsw_run_xdp(priv, ch, &xdp, page);
7499ed4050cSIvan Khoronzhuk 		if (ret != CPSW_XDP_PASS)
7509ed4050cSIvan Khoronzhuk 			goto requeue;
7519ed4050cSIvan Khoronzhuk 
7529ed4050cSIvan Khoronzhuk 		/* XDP prog might have changed packet data and boundaries */
7539ed4050cSIvan Khoronzhuk 		len = xdp.data_end - xdp.data;
7549ed4050cSIvan Khoronzhuk 		headroom = xdp.data - xdp.data_hard_start;
7559ed4050cSIvan Khoronzhuk 
7569ed4050cSIvan Khoronzhuk 		/* XDP prog can modify vlan tag, so can't use encap header */
7579ed4050cSIvan Khoronzhuk 		status &= ~CPDMA_RX_VLAN_ENCAP;
7589ed4050cSIvan Khoronzhuk 	}
7599ed4050cSIvan Khoronzhuk 
7609ed4050cSIvan Khoronzhuk 	/* pass skb to netstack if no XDP prog or returned XDP_PASS */
7619ed4050cSIvan Khoronzhuk 	skb = build_skb(pa, cpsw_rxbuf_total_len(pkt_size));
7629ed4050cSIvan Khoronzhuk 	if (!skb) {
7639ed4050cSIvan Khoronzhuk 		ndev->stats.rx_dropped++;
7649ed4050cSIvan Khoronzhuk 		page_pool_recycle_direct(pool, page);
7659ed4050cSIvan Khoronzhuk 		goto requeue;
7669ed4050cSIvan Khoronzhuk 	}
7679ed4050cSIvan Khoronzhuk 
7689ed4050cSIvan Khoronzhuk 	skb_reserve(skb, headroom);
769df828598SMugunthan V N 	skb_put(skb, len);
7709ed4050cSIvan Khoronzhuk 	skb->dev = ndev;
771a3a41d2fSGrygorii Strashko 	if (status & CPDMA_RX_VLAN_ENCAP)
772a3a41d2fSGrygorii Strashko 		cpsw_rx_vlan_encap(skb);
773a9423120SIvan Khoronzhuk 	if (priv->rx_ts_enabled)
7742a05a622SIvan Khoronzhuk 		cpts_rx_timestamp(cpsw->cpts, skb);
775df828598SMugunthan V N 	skb->protocol = eth_type_trans(skb, ndev);
7769ed4050cSIvan Khoronzhuk 
7779ed4050cSIvan Khoronzhuk 	/* unmap page as no netstack skb page recycling */
7789ed4050cSIvan Khoronzhuk 	page_pool_release_page(pool, page);
779df828598SMugunthan V N 	netif_receive_skb(skb);
7809ed4050cSIvan Khoronzhuk 
7818dc43ddcSTobias Klauser 	ndev->stats.rx_bytes += len;
7828dc43ddcSTobias Klauser 	ndev->stats.rx_packets++;
783df828598SMugunthan V N 
784a0e2c822SMugunthan V N requeue:
7859ed4050cSIvan Khoronzhuk 	xmeta = page_address(new_page) + CPSW_XMETA_OFFSET;
7869ed4050cSIvan Khoronzhuk 	xmeta->ndev = ndev;
7879ed4050cSIvan Khoronzhuk 	xmeta->ch = ch;
7889ed4050cSIvan Khoronzhuk 
7899ed4050cSIvan Khoronzhuk 	dma = page_pool_get_dma_addr(new_page) + CPSW_HEADROOM;
7909ed4050cSIvan Khoronzhuk 	ret = cpdma_chan_submit_mapped(cpsw->rxv[ch].ch, new_page, dma,
7919ed4050cSIvan Khoronzhuk 				       pkt_size, 0);
792871e8465SIvan Khoronzhuk 	if (ret < 0) {
793871e8465SIvan Khoronzhuk 		WARN_ON(ret == -ENOMEM);
7949ed4050cSIvan Khoronzhuk 		page_pool_recycle_direct(pool, new_page);
795df828598SMugunthan V N 	}
796871e8465SIvan Khoronzhuk }
797df828598SMugunthan V N 
798c24eef28SGrygorii Strashko void cpsw_split_res(struct cpsw_common *cpsw)
79948e0a83eSIvan Khoronzhuk {
80032b78d85SIvan Khoronzhuk 	u32 consumed_rate = 0, bigest_rate = 0;
80148e0a83eSIvan Khoronzhuk 	struct cpsw_vector *txv = cpsw->txv;
80232b78d85SIvan Khoronzhuk 	int i, ch_weight, rlim_ch_num = 0;
80348e0a83eSIvan Khoronzhuk 	int budget, bigest_rate_ch = 0;
80448e0a83eSIvan Khoronzhuk 	u32 ch_rate, max_rate;
80548e0a83eSIvan Khoronzhuk 	int ch_budget = 0;
80648e0a83eSIvan Khoronzhuk 
80748e0a83eSIvan Khoronzhuk 	for (i = 0; i < cpsw->tx_ch_num; i++) {
80848e0a83eSIvan Khoronzhuk 		ch_rate = cpdma_chan_get_rate(txv[i].ch);
80948e0a83eSIvan Khoronzhuk 		if (!ch_rate)
81048e0a83eSIvan Khoronzhuk 			continue;
81148e0a83eSIvan Khoronzhuk 
81248e0a83eSIvan Khoronzhuk 		rlim_ch_num++;
81348e0a83eSIvan Khoronzhuk 		consumed_rate += ch_rate;
81448e0a83eSIvan Khoronzhuk 	}
81548e0a83eSIvan Khoronzhuk 
81648e0a83eSIvan Khoronzhuk 	if (cpsw->tx_ch_num == rlim_ch_num) {
81748e0a83eSIvan Khoronzhuk 		max_rate = consumed_rate;
81832b78d85SIvan Khoronzhuk 	} else if (!rlim_ch_num) {
81932b78d85SIvan Khoronzhuk 		ch_budget = CPSW_POLL_WEIGHT / cpsw->tx_ch_num;
82032b78d85SIvan Khoronzhuk 		bigest_rate = 0;
82132b78d85SIvan Khoronzhuk 		max_rate = consumed_rate;
82248e0a83eSIvan Khoronzhuk 	} else {
8230be01b8eSIvan Khoronzhuk 		max_rate = cpsw->speed * 1000;
8240be01b8eSIvan Khoronzhuk 
8250be01b8eSIvan Khoronzhuk 		/* if max_rate is less then expected due to reduced link speed,
8260be01b8eSIvan Khoronzhuk 		 * split proportionally according next potential max speed
8270be01b8eSIvan Khoronzhuk 		 */
8280be01b8eSIvan Khoronzhuk 		if (max_rate < consumed_rate)
8290be01b8eSIvan Khoronzhuk 			max_rate *= 10;
8300be01b8eSIvan Khoronzhuk 
8310be01b8eSIvan Khoronzhuk 		if (max_rate < consumed_rate)
8320be01b8eSIvan Khoronzhuk 			max_rate *= 10;
83332b78d85SIvan Khoronzhuk 
83448e0a83eSIvan Khoronzhuk 		ch_budget = (consumed_rate * CPSW_POLL_WEIGHT) / max_rate;
83548e0a83eSIvan Khoronzhuk 		ch_budget = (CPSW_POLL_WEIGHT - ch_budget) /
83648e0a83eSIvan Khoronzhuk 			    (cpsw->tx_ch_num - rlim_ch_num);
83748e0a83eSIvan Khoronzhuk 		bigest_rate = (max_rate - consumed_rate) /
83848e0a83eSIvan Khoronzhuk 			      (cpsw->tx_ch_num - rlim_ch_num);
83948e0a83eSIvan Khoronzhuk 	}
84048e0a83eSIvan Khoronzhuk 
84132b78d85SIvan Khoronzhuk 	/* split tx weight/budget */
84248e0a83eSIvan Khoronzhuk 	budget = CPSW_POLL_WEIGHT;
84348e0a83eSIvan Khoronzhuk 	for (i = 0; i < cpsw->tx_ch_num; i++) {
84448e0a83eSIvan Khoronzhuk 		ch_rate = cpdma_chan_get_rate(txv[i].ch);
84548e0a83eSIvan Khoronzhuk 		if (ch_rate) {
84648e0a83eSIvan Khoronzhuk 			txv[i].budget = (ch_rate * CPSW_POLL_WEIGHT) / max_rate;
84748e0a83eSIvan Khoronzhuk 			if (!txv[i].budget)
84832b78d85SIvan Khoronzhuk 				txv[i].budget++;
84948e0a83eSIvan Khoronzhuk 			if (ch_rate > bigest_rate) {
85048e0a83eSIvan Khoronzhuk 				bigest_rate_ch = i;
85148e0a83eSIvan Khoronzhuk 				bigest_rate = ch_rate;
85248e0a83eSIvan Khoronzhuk 			}
85332b78d85SIvan Khoronzhuk 
85432b78d85SIvan Khoronzhuk 			ch_weight = (ch_rate * 100) / max_rate;
85532b78d85SIvan Khoronzhuk 			if (!ch_weight)
85632b78d85SIvan Khoronzhuk 				ch_weight++;
85732b78d85SIvan Khoronzhuk 			cpdma_chan_set_weight(cpsw->txv[i].ch, ch_weight);
85848e0a83eSIvan Khoronzhuk 		} else {
85948e0a83eSIvan Khoronzhuk 			txv[i].budget = ch_budget;
86048e0a83eSIvan Khoronzhuk 			if (!bigest_rate_ch)
86148e0a83eSIvan Khoronzhuk 				bigest_rate_ch = i;
86232b78d85SIvan Khoronzhuk 			cpdma_chan_set_weight(cpsw->txv[i].ch, 0);
86348e0a83eSIvan Khoronzhuk 		}
86448e0a83eSIvan Khoronzhuk 
86548e0a83eSIvan Khoronzhuk 		budget -= txv[i].budget;
86648e0a83eSIvan Khoronzhuk 	}
86748e0a83eSIvan Khoronzhuk 
86848e0a83eSIvan Khoronzhuk 	if (budget)
86948e0a83eSIvan Khoronzhuk 		txv[bigest_rate_ch].budget += budget;
87048e0a83eSIvan Khoronzhuk 
87148e0a83eSIvan Khoronzhuk 	/* split rx budget */
87248e0a83eSIvan Khoronzhuk 	budget = CPSW_POLL_WEIGHT;
87348e0a83eSIvan Khoronzhuk 	ch_budget = budget / cpsw->rx_ch_num;
87448e0a83eSIvan Khoronzhuk 	for (i = 0; i < cpsw->rx_ch_num; i++) {
87548e0a83eSIvan Khoronzhuk 		cpsw->rxv[i].budget = ch_budget;
87648e0a83eSIvan Khoronzhuk 		budget -= ch_budget;
87748e0a83eSIvan Khoronzhuk 	}
87848e0a83eSIvan Khoronzhuk 
87948e0a83eSIvan Khoronzhuk 	if (budget)
88048e0a83eSIvan Khoronzhuk 		cpsw->rxv[0].budget += budget;
88148e0a83eSIvan Khoronzhuk }
88248e0a83eSIvan Khoronzhuk 
883c03abd84SFelipe Balbi static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id)
884df828598SMugunthan V N {
885dbc4ec52SIvan Khoronzhuk 	struct cpsw_common *cpsw = dev_id;
8867ce67a38SFelipe Balbi 
8875d8d0d4dSIvan Khoronzhuk 	writel(0, &cpsw->wr_regs->tx_en);
8882c836bd9SIvan Khoronzhuk 	cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_TX);
889c03abd84SFelipe Balbi 
890e38b5a3dSIvan Khoronzhuk 	if (cpsw->quirk_irq) {
891e38b5a3dSIvan Khoronzhuk 		disable_irq_nosync(cpsw->irqs_table[1]);
892e38b5a3dSIvan Khoronzhuk 		cpsw->tx_irq_disabled = true;
8937da11600SMugunthan V N 	}
8947da11600SMugunthan V N 
895dbc4ec52SIvan Khoronzhuk 	napi_schedule(&cpsw->napi_tx);
896c03abd84SFelipe Balbi 	return IRQ_HANDLED;
897c03abd84SFelipe Balbi }
898c03abd84SFelipe Balbi 
899c03abd84SFelipe Balbi static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id)
900c03abd84SFelipe Balbi {
901dbc4ec52SIvan Khoronzhuk 	struct cpsw_common *cpsw = dev_id;
902c03abd84SFelipe Balbi 
9032c836bd9SIvan Khoronzhuk 	cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_RX);
9045d8d0d4dSIvan Khoronzhuk 	writel(0, &cpsw->wr_regs->rx_en);
905fd51cf19SSebastian Siewior 
906e38b5a3dSIvan Khoronzhuk 	if (cpsw->quirk_irq) {
907e38b5a3dSIvan Khoronzhuk 		disable_irq_nosync(cpsw->irqs_table[0]);
908e38b5a3dSIvan Khoronzhuk 		cpsw->rx_irq_disabled = true;
9097da11600SMugunthan V N 	}
9107da11600SMugunthan V N 
911dbc4ec52SIvan Khoronzhuk 	napi_schedule(&cpsw->napi_rx);
912df828598SMugunthan V N 	return IRQ_HANDLED;
913df828598SMugunthan V N }
914df828598SMugunthan V N 
9159611d6d6SIvan Khoronzhuk static int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget)
916df828598SMugunthan V N {
917e05107e6SIvan Khoronzhuk 	u32			ch_map;
9188feb0a19SIvan Khoronzhuk 	int			num_tx, cur_budget, ch;
919dbc4ec52SIvan Khoronzhuk 	struct cpsw_common	*cpsw = napi_to_cpsw(napi_tx);
9208feb0a19SIvan Khoronzhuk 	struct cpsw_vector	*txv;
92132a7432cSMugunthan V N 
922e05107e6SIvan Khoronzhuk 	/* process every unprocessed channel */
923e05107e6SIvan Khoronzhuk 	ch_map = cpdma_ctrl_txchs_state(cpsw->dma);
92479b3325dSIvan Khoronzhuk 	for (ch = 0, num_tx = 0; ch_map & 0xff; ch_map <<= 1, ch++) {
92579b3325dSIvan Khoronzhuk 		if (!(ch_map & 0x80))
926e05107e6SIvan Khoronzhuk 			continue;
927e05107e6SIvan Khoronzhuk 
9288feb0a19SIvan Khoronzhuk 		txv = &cpsw->txv[ch];
9298feb0a19SIvan Khoronzhuk 		if (unlikely(txv->budget > budget - num_tx))
9308feb0a19SIvan Khoronzhuk 			cur_budget = budget - num_tx;
9318feb0a19SIvan Khoronzhuk 		else
9328feb0a19SIvan Khoronzhuk 			cur_budget = txv->budget;
9338feb0a19SIvan Khoronzhuk 
9348feb0a19SIvan Khoronzhuk 		num_tx += cpdma_chan_process(txv->ch, cur_budget);
935342934a5SIvan Khoronzhuk 		if (num_tx >= budget)
936342934a5SIvan Khoronzhuk 			break;
937e05107e6SIvan Khoronzhuk 	}
938e05107e6SIvan Khoronzhuk 
93932a7432cSMugunthan V N 	if (num_tx < budget) {
94032a7432cSMugunthan V N 		napi_complete(napi_tx);
9415d8d0d4dSIvan Khoronzhuk 		writel(0xff, &cpsw->wr_regs->tx_en);
9429611d6d6SIvan Khoronzhuk 	}
9439611d6d6SIvan Khoronzhuk 
9449611d6d6SIvan Khoronzhuk 	return num_tx;
9459611d6d6SIvan Khoronzhuk }
9469611d6d6SIvan Khoronzhuk 
9479611d6d6SIvan Khoronzhuk static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget)
9489611d6d6SIvan Khoronzhuk {
9499611d6d6SIvan Khoronzhuk 	struct cpsw_common *cpsw = napi_to_cpsw(napi_tx);
9509611d6d6SIvan Khoronzhuk 	int num_tx;
9519611d6d6SIvan Khoronzhuk 
9529611d6d6SIvan Khoronzhuk 	num_tx = cpdma_chan_process(cpsw->txv[0].ch, budget);
9539611d6d6SIvan Khoronzhuk 	if (num_tx < budget) {
9549611d6d6SIvan Khoronzhuk 		napi_complete(napi_tx);
9559611d6d6SIvan Khoronzhuk 		writel(0xff, &cpsw->wr_regs->tx_en);
9569611d6d6SIvan Khoronzhuk 		if (cpsw->tx_irq_disabled) {
957e38b5a3dSIvan Khoronzhuk 			cpsw->tx_irq_disabled = false;
958e38b5a3dSIvan Khoronzhuk 			enable_irq(cpsw->irqs_table[1]);
9597da11600SMugunthan V N 		}
96032a7432cSMugunthan V N 	}
96132a7432cSMugunthan V N 
96232a7432cSMugunthan V N 	return num_tx;
96332a7432cSMugunthan V N }
96432a7432cSMugunthan V N 
9659611d6d6SIvan Khoronzhuk static int cpsw_rx_mq_poll(struct napi_struct *napi_rx, int budget)
96632a7432cSMugunthan V N {
967e05107e6SIvan Khoronzhuk 	u32			ch_map;
9688feb0a19SIvan Khoronzhuk 	int			num_rx, cur_budget, ch;
969dbc4ec52SIvan Khoronzhuk 	struct cpsw_common	*cpsw = napi_to_cpsw(napi_rx);
9708feb0a19SIvan Khoronzhuk 	struct cpsw_vector	*rxv;
971510a1e72SMugunthan V N 
972e05107e6SIvan Khoronzhuk 	/* process every unprocessed channel */
973e05107e6SIvan Khoronzhuk 	ch_map = cpdma_ctrl_rxchs_state(cpsw->dma);
974342934a5SIvan Khoronzhuk 	for (ch = 0, num_rx = 0; ch_map; ch_map >>= 1, ch++) {
975e05107e6SIvan Khoronzhuk 		if (!(ch_map & 0x01))
976e05107e6SIvan Khoronzhuk 			continue;
977e05107e6SIvan Khoronzhuk 
9788feb0a19SIvan Khoronzhuk 		rxv = &cpsw->rxv[ch];
9798feb0a19SIvan Khoronzhuk 		if (unlikely(rxv->budget > budget - num_rx))
9808feb0a19SIvan Khoronzhuk 			cur_budget = budget - num_rx;
9818feb0a19SIvan Khoronzhuk 		else
9828feb0a19SIvan Khoronzhuk 			cur_budget = rxv->budget;
9838feb0a19SIvan Khoronzhuk 
9848feb0a19SIvan Khoronzhuk 		num_rx += cpdma_chan_process(rxv->ch, cur_budget);
985342934a5SIvan Khoronzhuk 		if (num_rx >= budget)
986342934a5SIvan Khoronzhuk 			break;
987e05107e6SIvan Khoronzhuk 	}
988e05107e6SIvan Khoronzhuk 
989510a1e72SMugunthan V N 	if (num_rx < budget) {
9906ad20165SEric Dumazet 		napi_complete_done(napi_rx, num_rx);
9915d8d0d4dSIvan Khoronzhuk 		writel(0xff, &cpsw->wr_regs->rx_en);
9929611d6d6SIvan Khoronzhuk 	}
9939611d6d6SIvan Khoronzhuk 
9949611d6d6SIvan Khoronzhuk 	return num_rx;
9959611d6d6SIvan Khoronzhuk }
9969611d6d6SIvan Khoronzhuk 
9979611d6d6SIvan Khoronzhuk static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget)
9989611d6d6SIvan Khoronzhuk {
9999611d6d6SIvan Khoronzhuk 	struct cpsw_common *cpsw = napi_to_cpsw(napi_rx);
10009611d6d6SIvan Khoronzhuk 	int num_rx;
10019611d6d6SIvan Khoronzhuk 
10029611d6d6SIvan Khoronzhuk 	num_rx = cpdma_chan_process(cpsw->rxv[0].ch, budget);
10039611d6d6SIvan Khoronzhuk 	if (num_rx < budget) {
10049611d6d6SIvan Khoronzhuk 		napi_complete_done(napi_rx, num_rx);
10059611d6d6SIvan Khoronzhuk 		writel(0xff, &cpsw->wr_regs->rx_en);
10069611d6d6SIvan Khoronzhuk 		if (cpsw->rx_irq_disabled) {
1007e38b5a3dSIvan Khoronzhuk 			cpsw->rx_irq_disabled = false;
1008e38b5a3dSIvan Khoronzhuk 			enable_irq(cpsw->irqs_table[0]);
10097da11600SMugunthan V N 		}
1010510a1e72SMugunthan V N 	}
1011df828598SMugunthan V N 
1012df828598SMugunthan V N 	return num_rx;
1013df828598SMugunthan V N }
1014df828598SMugunthan V N 
1015df828598SMugunthan V N static inline void soft_reset(const char *module, void __iomem *reg)
1016df828598SMugunthan V N {
1017df828598SMugunthan V N 	unsigned long timeout = jiffies + HZ;
1018df828598SMugunthan V N 
1019dda5f5feSGrygorii Strashko 	writel_relaxed(1, reg);
1020df828598SMugunthan V N 	do {
1021df828598SMugunthan V N 		cpu_relax();
1022dda5f5feSGrygorii Strashko 	} while ((readl_relaxed(reg) & 1) && time_after(timeout, jiffies));
1023df828598SMugunthan V N 
1024dda5f5feSGrygorii Strashko 	WARN(readl_relaxed(reg) & 1, "failed to soft-reset %s\n", module);
1025df828598SMugunthan V N }
1026df828598SMugunthan V N 
1027df828598SMugunthan V N static void cpsw_set_slave_mac(struct cpsw_slave *slave,
1028df828598SMugunthan V N 			       struct cpsw_priv *priv)
1029df828598SMugunthan V N {
10309750a3adSRichard Cochran 	slave_write(slave, mac_hi(priv->mac_addr), SA_HI);
10319750a3adSRichard Cochran 	slave_write(slave, mac_lo(priv->mac_addr), SA_LO);
1032df828598SMugunthan V N }
1033df828598SMugunthan V N 
103457d90148SIvan Khoronzhuk static bool cpsw_shp_is_off(struct cpsw_priv *priv)
103557d90148SIvan Khoronzhuk {
103657d90148SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
103757d90148SIvan Khoronzhuk 	struct cpsw_slave *slave;
103857d90148SIvan Khoronzhuk 	u32 shift, mask, val;
103957d90148SIvan Khoronzhuk 
104057d90148SIvan Khoronzhuk 	val = readl_relaxed(&cpsw->regs->ptype);
104157d90148SIvan Khoronzhuk 
104257d90148SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
104357d90148SIvan Khoronzhuk 	shift = CPSW_FIFO_SHAPE_EN_SHIFT + 3 * slave->slave_num;
104457d90148SIvan Khoronzhuk 	mask = 7 << shift;
104557d90148SIvan Khoronzhuk 	val = val & mask;
104657d90148SIvan Khoronzhuk 
104757d90148SIvan Khoronzhuk 	return !val;
104857d90148SIvan Khoronzhuk }
104957d90148SIvan Khoronzhuk 
105057d90148SIvan Khoronzhuk static void cpsw_fifo_shp_on(struct cpsw_priv *priv, int fifo, int on)
105157d90148SIvan Khoronzhuk {
105257d90148SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
105357d90148SIvan Khoronzhuk 	struct cpsw_slave *slave;
105457d90148SIvan Khoronzhuk 	u32 shift, mask, val;
105557d90148SIvan Khoronzhuk 
105657d90148SIvan Khoronzhuk 	val = readl_relaxed(&cpsw->regs->ptype);
105757d90148SIvan Khoronzhuk 
105857d90148SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
105957d90148SIvan Khoronzhuk 	shift = CPSW_FIFO_SHAPE_EN_SHIFT + 3 * slave->slave_num;
106057d90148SIvan Khoronzhuk 	mask = (1 << --fifo) << shift;
106157d90148SIvan Khoronzhuk 	val = on ? val | mask : val & ~mask;
106257d90148SIvan Khoronzhuk 
106357d90148SIvan Khoronzhuk 	writel_relaxed(val, &cpsw->regs->ptype);
106457d90148SIvan Khoronzhuk }
106557d90148SIvan Khoronzhuk 
1066df828598SMugunthan V N static void _cpsw_adjust_link(struct cpsw_slave *slave,
1067df828598SMugunthan V N 			      struct cpsw_priv *priv, bool *link)
1068df828598SMugunthan V N {
1069df828598SMugunthan V N 	struct phy_device	*phy = slave->phy;
1070df828598SMugunthan V N 	u32			mac_control = 0;
1071df828598SMugunthan V N 	u32			slave_port;
1072606f3993SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1073df828598SMugunthan V N 
1074df828598SMugunthan V N 	if (!phy)
1075df828598SMugunthan V N 		return;
1076df828598SMugunthan V N 
10776f1f5836SIvan Khoronzhuk 	slave_port = cpsw_get_slave_port(slave->slave_num);
1078df828598SMugunthan V N 
1079df828598SMugunthan V N 	if (phy->link) {
1080cfc08345SGrygorii Strashko 		mac_control = CPSW_SL_CTL_GMII_EN;
1081cfc08345SGrygorii Strashko 
1082cfc08345SGrygorii Strashko 		if (phy->speed == 1000)
1083cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_GIG;
1084cfc08345SGrygorii Strashko 		if (phy->duplex)
1085cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_FULLDUPLEX;
1086cfc08345SGrygorii Strashko 
1087cfc08345SGrygorii Strashko 		/* set speed_in input in case RMII mode is used in 100Mbps */
1088cfc08345SGrygorii Strashko 		if (phy->speed == 100)
1089cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_IFCTL_A;
1090cfc08345SGrygorii Strashko 		/* in band mode only works in 10Mbps RGMII mode */
1091cfc08345SGrygorii Strashko 		else if ((phy->speed == 10) && phy_interface_is_rgmii(phy))
1092cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_EXT_EN; /* In Band mode */
1093cfc08345SGrygorii Strashko 
1094cfc08345SGrygorii Strashko 		if (priv->rx_pause)
1095cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_RX_FLOW_EN;
1096cfc08345SGrygorii Strashko 
1097cfc08345SGrygorii Strashko 		if (priv->tx_pause)
1098cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_TX_FLOW_EN;
1099cfc08345SGrygorii Strashko 
1100cfc08345SGrygorii Strashko 		if (mac_control != slave->mac_control)
1101cfc08345SGrygorii Strashko 			cpsw_sl_ctl_set(slave->mac_sl, mac_control);
1102df828598SMugunthan V N 
1103df828598SMugunthan V N 		/* enable forwarding */
11042a05a622SIvan Khoronzhuk 		cpsw_ale_control_set(cpsw->ale, slave_port,
1105df828598SMugunthan V N 				     ALE_PORT_STATE, ALE_PORT_STATE_FORWARD);
1106df828598SMugunthan V N 
1107df828598SMugunthan V N 		*link = true;
110857d90148SIvan Khoronzhuk 
110957d90148SIvan Khoronzhuk 		if (priv->shp_cfg_speed &&
111057d90148SIvan Khoronzhuk 		    priv->shp_cfg_speed != slave->phy->speed &&
111157d90148SIvan Khoronzhuk 		    !cpsw_shp_is_off(priv))
111257d90148SIvan Khoronzhuk 			dev_warn(priv->dev,
111357d90148SIvan Khoronzhuk 				 "Speed was changed, CBS shaper speeds are changed!");
1114df828598SMugunthan V N 	} else {
1115df828598SMugunthan V N 		mac_control = 0;
1116df828598SMugunthan V N 		/* disable forwarding */
11172a05a622SIvan Khoronzhuk 		cpsw_ale_control_set(cpsw->ale, slave_port,
1118df828598SMugunthan V N 				     ALE_PORT_STATE, ALE_PORT_STATE_DISABLE);
1119cfc08345SGrygorii Strashko 
1120cfc08345SGrygorii Strashko 		cpsw_sl_wait_for_idle(slave->mac_sl, 100);
1121cfc08345SGrygorii Strashko 
1122cfc08345SGrygorii Strashko 		cpsw_sl_ctl_reset(slave->mac_sl);
1123df828598SMugunthan V N 	}
1124df828598SMugunthan V N 
1125cfc08345SGrygorii Strashko 	if (mac_control != slave->mac_control)
1126df828598SMugunthan V N 		phy_print_status(phy);
1127df828598SMugunthan V N 
1128df828598SMugunthan V N 	slave->mac_control = mac_control;
1129df828598SMugunthan V N }
1130df828598SMugunthan V N 
11310be01b8eSIvan Khoronzhuk static int cpsw_get_common_speed(struct cpsw_common *cpsw)
11320be01b8eSIvan Khoronzhuk {
11330be01b8eSIvan Khoronzhuk 	int i, speed;
11340be01b8eSIvan Khoronzhuk 
11350be01b8eSIvan Khoronzhuk 	for (i = 0, speed = 0; i < cpsw->data.slaves; i++)
11360be01b8eSIvan Khoronzhuk 		if (cpsw->slaves[i].phy && cpsw->slaves[i].phy->link)
11370be01b8eSIvan Khoronzhuk 			speed += cpsw->slaves[i].phy->speed;
11380be01b8eSIvan Khoronzhuk 
11390be01b8eSIvan Khoronzhuk 	return speed;
11400be01b8eSIvan Khoronzhuk }
11410be01b8eSIvan Khoronzhuk 
11420be01b8eSIvan Khoronzhuk static int cpsw_need_resplit(struct cpsw_common *cpsw)
11430be01b8eSIvan Khoronzhuk {
11440be01b8eSIvan Khoronzhuk 	int i, rlim_ch_num;
11450be01b8eSIvan Khoronzhuk 	int speed, ch_rate;
11460be01b8eSIvan Khoronzhuk 
11470be01b8eSIvan Khoronzhuk 	/* re-split resources only in case speed was changed */
11480be01b8eSIvan Khoronzhuk 	speed = cpsw_get_common_speed(cpsw);
11490be01b8eSIvan Khoronzhuk 	if (speed == cpsw->speed || !speed)
11500be01b8eSIvan Khoronzhuk 		return 0;
11510be01b8eSIvan Khoronzhuk 
11520be01b8eSIvan Khoronzhuk 	cpsw->speed = speed;
11530be01b8eSIvan Khoronzhuk 
11540be01b8eSIvan Khoronzhuk 	for (i = 0, rlim_ch_num = 0; i < cpsw->tx_ch_num; i++) {
11550be01b8eSIvan Khoronzhuk 		ch_rate = cpdma_chan_get_rate(cpsw->txv[i].ch);
11560be01b8eSIvan Khoronzhuk 		if (!ch_rate)
11570be01b8eSIvan Khoronzhuk 			break;
11580be01b8eSIvan Khoronzhuk 
11590be01b8eSIvan Khoronzhuk 		rlim_ch_num++;
11600be01b8eSIvan Khoronzhuk 	}
11610be01b8eSIvan Khoronzhuk 
11620be01b8eSIvan Khoronzhuk 	/* cases not dependent on speed */
11630be01b8eSIvan Khoronzhuk 	if (!rlim_ch_num || rlim_ch_num == cpsw->tx_ch_num)
11640be01b8eSIvan Khoronzhuk 		return 0;
11650be01b8eSIvan Khoronzhuk 
11660be01b8eSIvan Khoronzhuk 	return 1;
11670be01b8eSIvan Khoronzhuk }
11680be01b8eSIvan Khoronzhuk 
1169df828598SMugunthan V N static void cpsw_adjust_link(struct net_device *ndev)
1170df828598SMugunthan V N {
1171df828598SMugunthan V N 	struct cpsw_priv	*priv = netdev_priv(ndev);
11720be01b8eSIvan Khoronzhuk 	struct cpsw_common	*cpsw = priv->cpsw;
1173df828598SMugunthan V N 	bool			link = false;
1174df828598SMugunthan V N 
1175df828598SMugunthan V N 	for_each_slave(priv, _cpsw_adjust_link, priv, &link);
1176df828598SMugunthan V N 
1177df828598SMugunthan V N 	if (link) {
11780be01b8eSIvan Khoronzhuk 		if (cpsw_need_resplit(cpsw))
11799763a891SGrygorii Strashko 			cpsw_split_res(cpsw);
11800be01b8eSIvan Khoronzhuk 
1181df828598SMugunthan V N 		netif_carrier_on(ndev);
1182df828598SMugunthan V N 		if (netif_running(ndev))
1183e05107e6SIvan Khoronzhuk 			netif_tx_wake_all_queues(ndev);
1184df828598SMugunthan V N 	} else {
1185df828598SMugunthan V N 		netif_carrier_off(ndev);
1186e05107e6SIvan Khoronzhuk 		netif_tx_stop_all_queues(ndev);
1187df828598SMugunthan V N 	}
1188df828598SMugunthan V N }
1189df828598SMugunthan V N 
1190d9ba8f9eSMugunthan V N static inline void cpsw_add_dual_emac_def_ale_entries(
1191d9ba8f9eSMugunthan V N 		struct cpsw_priv *priv, struct cpsw_slave *slave,
1192d9ba8f9eSMugunthan V N 		u32 slave_port)
1193d9ba8f9eSMugunthan V N {
11942a05a622SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
119571a2cbb7SGrygorii Strashko 	u32 port_mask = 1 << slave_port | ALE_PORT_HOST;
1196d9ba8f9eSMugunthan V N 
11972a05a622SIvan Khoronzhuk 	if (cpsw->version == CPSW_VERSION_1)
1198d9ba8f9eSMugunthan V N 		slave_write(slave, slave->port_vlan, CPSW1_PORT_VLAN);
1199d9ba8f9eSMugunthan V N 	else
1200d9ba8f9eSMugunthan V N 		slave_write(slave, slave->port_vlan, CPSW2_PORT_VLAN);
12012a05a622SIvan Khoronzhuk 	cpsw_ale_add_vlan(cpsw->ale, slave->port_vlan, port_mask,
1202d9ba8f9eSMugunthan V N 			  port_mask, port_mask, 0);
12032a05a622SIvan Khoronzhuk 	cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast,
12045b3a5a14SIvan Khoronzhuk 			   ALE_PORT_HOST, ALE_VLAN, slave->port_vlan, 0);
12052a05a622SIvan Khoronzhuk 	cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr,
12062a05a622SIvan Khoronzhuk 			   HOST_PORT_NUM, ALE_VLAN |
12072a05a622SIvan Khoronzhuk 			   ALE_SECURE, slave->port_vlan);
12085e5add17SGrygorii Strashko 	cpsw_ale_control_set(cpsw->ale, slave_port,
12095e5add17SGrygorii Strashko 			     ALE_PORT_DROP_UNKNOWN_VLAN, 1);
1210d9ba8f9eSMugunthan V N }
1211d9ba8f9eSMugunthan V N 
12121e7a2e21SDaniel Mack static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
12131e7a2e21SDaniel Mack {
1214df828598SMugunthan V N 	u32 slave_port;
121530c57f07SSekhar Nori 	struct phy_device *phy;
1216649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1217df828598SMugunthan V N 
1218cfc08345SGrygorii Strashko 	cpsw_sl_reset(slave->mac_sl, 100);
1219cfc08345SGrygorii Strashko 	cpsw_sl_ctl_reset(slave->mac_sl);
1220df828598SMugunthan V N 
1221df828598SMugunthan V N 	/* setup priority mapping */
1222cfc08345SGrygorii Strashko 	cpsw_sl_reg_write(slave->mac_sl, CPSW_SL_RX_PRI_MAP,
1223cfc08345SGrygorii Strashko 			  RX_PRIORITY_MAPPING);
12249750a3adSRichard Cochran 
12252a05a622SIvan Khoronzhuk 	switch (cpsw->version) {
12269750a3adSRichard Cochran 	case CPSW_VERSION_1:
12279750a3adSRichard Cochran 		slave_write(slave, TX_PRIORITY_MAPPING, CPSW1_TX_PRI_MAP);
122848f5bcccSGrygorii Strashko 		/* Increase RX FIFO size to 5 for supporting fullduplex
122948f5bcccSGrygorii Strashko 		 * flow control mode
123048f5bcccSGrygorii Strashko 		 */
123148f5bcccSGrygorii Strashko 		slave_write(slave,
123248f5bcccSGrygorii Strashko 			    (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) |
123348f5bcccSGrygorii Strashko 			    CPSW_MAX_BLKS_RX, CPSW1_MAX_BLKS);
12349750a3adSRichard Cochran 		break;
12359750a3adSRichard Cochran 	case CPSW_VERSION_2:
1236c193f365SMugunthan V N 	case CPSW_VERSION_3:
1237926489beSMugunthan V N 	case CPSW_VERSION_4:
12389750a3adSRichard Cochran 		slave_write(slave, TX_PRIORITY_MAPPING, CPSW2_TX_PRI_MAP);
123948f5bcccSGrygorii Strashko 		/* Increase RX FIFO size to 5 for supporting fullduplex
124048f5bcccSGrygorii Strashko 		 * flow control mode
124148f5bcccSGrygorii Strashko 		 */
124248f5bcccSGrygorii Strashko 		slave_write(slave,
124348f5bcccSGrygorii Strashko 			    (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) |
124448f5bcccSGrygorii Strashko 			    CPSW_MAX_BLKS_RX, CPSW2_MAX_BLKS);
12459750a3adSRichard Cochran 		break;
12469750a3adSRichard Cochran 	}
1247df828598SMugunthan V N 
1248df828598SMugunthan V N 	/* setup max packet size, and mac address */
1249cfc08345SGrygorii Strashko 	cpsw_sl_reg_write(slave->mac_sl, CPSW_SL_RX_MAXLEN,
1250cfc08345SGrygorii Strashko 			  cpsw->rx_packet_max);
1251df828598SMugunthan V N 	cpsw_set_slave_mac(slave, priv);
1252df828598SMugunthan V N 
1253df828598SMugunthan V N 	slave->mac_control = 0;	/* no link yet */
1254df828598SMugunthan V N 
12556f1f5836SIvan Khoronzhuk 	slave_port = cpsw_get_slave_port(slave->slave_num);
1256df828598SMugunthan V N 
1257606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac)
1258d9ba8f9eSMugunthan V N 		cpsw_add_dual_emac_def_ale_entries(priv, slave, slave_port);
1259d9ba8f9eSMugunthan V N 	else
12602a05a622SIvan Khoronzhuk 		cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast,
1261e11b220fSMugunthan V N 				   1 << slave_port, 0, 0, ALE_MCAST_FWD_2);
1262df828598SMugunthan V N 
1263d733f754SDavid Rivshin 	if (slave->data->phy_node) {
126430c57f07SSekhar Nori 		phy = of_phy_connect(priv->ndev, slave->data->phy_node,
12659e42f715SHeiko Schocher 				 &cpsw_adjust_link, 0, slave->data->phy_if);
126630c57f07SSekhar Nori 		if (!phy) {
1267f7ce9103SRob Herring 			dev_err(priv->dev, "phy \"%pOF\" not found on slave %d\n",
1268f7ce9103SRob Herring 				slave->data->phy_node,
1269d733f754SDavid Rivshin 				slave->slave_num);
1270d733f754SDavid Rivshin 			return;
1271d733f754SDavid Rivshin 		}
1272d733f754SDavid Rivshin 	} else {
127330c57f07SSekhar Nori 		phy = phy_connect(priv->ndev, slave->data->phy_id,
1274f9a8f83bSFlorian Fainelli 				 &cpsw_adjust_link, slave->data->phy_if);
127530c57f07SSekhar Nori 		if (IS_ERR(phy)) {
1276d733f754SDavid Rivshin 			dev_err(priv->dev,
1277d733f754SDavid Rivshin 				"phy \"%s\" not found on slave %d, err %ld\n",
1278d733f754SDavid Rivshin 				slave->data->phy_id, slave->slave_num,
127930c57f07SSekhar Nori 				PTR_ERR(phy));
1280d733f754SDavid Rivshin 			return;
1281d733f754SDavid Rivshin 		}
1282d733f754SDavid Rivshin 	}
1283d733f754SDavid Rivshin 
128430c57f07SSekhar Nori 	slave->phy = phy;
128530c57f07SSekhar Nori 
12862220943aSAndrew Lunn 	phy_attached_info(slave->phy);
12872220943aSAndrew Lunn 
1288df828598SMugunthan V N 	phy_start(slave->phy);
1289388367a5SMugunthan V N 
1290388367a5SMugunthan V N 	/* Configure GMII_SEL register */
12913ff18849SGrygorii Strashko 	if (!IS_ERR(slave->data->ifphy))
12923ff18849SGrygorii Strashko 		phy_set_mode_ext(slave->data->ifphy, PHY_MODE_ETHERNET,
12933ff18849SGrygorii Strashko 				 slave->data->phy_if);
12943ff18849SGrygorii Strashko 	else
12953ff18849SGrygorii Strashko 		cpsw_phy_sel(cpsw->dev, slave->phy->interface,
12963ff18849SGrygorii Strashko 			     slave->slave_num);
1297df828598SMugunthan V N }
1298df828598SMugunthan V N 
12993b72c2feSMugunthan V N static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
13003b72c2feSMugunthan V N {
1301606f3993SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1302606f3993SIvan Khoronzhuk 	const int vlan = cpsw->data.default_vlan;
13033b72c2feSMugunthan V N 	u32 reg;
13043b72c2feSMugunthan V N 	int i;
13051e5c4bc4SLennart Sorensen 	int unreg_mcast_mask;
13063b72c2feSMugunthan V N 
13072a05a622SIvan Khoronzhuk 	reg = (cpsw->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN :
13083b72c2feSMugunthan V N 	       CPSW2_PORT_VLAN;
13093b72c2feSMugunthan V N 
13105d8d0d4dSIvan Khoronzhuk 	writel(vlan, &cpsw->host_port_regs->port_vlan);
13113b72c2feSMugunthan V N 
1312606f3993SIvan Khoronzhuk 	for (i = 0; i < cpsw->data.slaves; i++)
1313606f3993SIvan Khoronzhuk 		slave_write(cpsw->slaves + i, vlan, reg);
13143b72c2feSMugunthan V N 
13151e5c4bc4SLennart Sorensen 	if (priv->ndev->flags & IFF_ALLMULTI)
13161e5c4bc4SLennart Sorensen 		unreg_mcast_mask = ALE_ALL_PORTS;
13171e5c4bc4SLennart Sorensen 	else
13181e5c4bc4SLennart Sorensen 		unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2;
13191e5c4bc4SLennart Sorensen 
13202a05a622SIvan Khoronzhuk 	cpsw_ale_add_vlan(cpsw->ale, vlan, ALE_ALL_PORTS,
132161f1cef9SGrygorii Strashko 			  ALE_ALL_PORTS, ALE_ALL_PORTS,
132261f1cef9SGrygorii Strashko 			  unreg_mcast_mask);
13233b72c2feSMugunthan V N }
13243b72c2feSMugunthan V N 
1325df828598SMugunthan V N static void cpsw_init_host_port(struct cpsw_priv *priv)
1326df828598SMugunthan V N {
1327d9ba8f9eSMugunthan V N 	u32 fifo_mode;
13285d8d0d4dSIvan Khoronzhuk 	u32 control_reg;
13295d8d0d4dSIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
13303b72c2feSMugunthan V N 
1331df828598SMugunthan V N 	/* soft reset the controller and initialize ale */
13325d8d0d4dSIvan Khoronzhuk 	soft_reset("cpsw", &cpsw->regs->soft_reset);
13332a05a622SIvan Khoronzhuk 	cpsw_ale_start(cpsw->ale);
1334df828598SMugunthan V N 
1335df828598SMugunthan V N 	/* switch to vlan unaware mode */
13362a05a622SIvan Khoronzhuk 	cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_VLAN_AWARE,
13373b72c2feSMugunthan V N 			     CPSW_ALE_VLAN_AWARE);
13385d8d0d4dSIvan Khoronzhuk 	control_reg = readl(&cpsw->regs->control);
1339a3a41d2fSGrygorii Strashko 	control_reg |= CPSW_VLAN_AWARE | CPSW_RX_VLAN_ENCAP;
13405d8d0d4dSIvan Khoronzhuk 	writel(control_reg, &cpsw->regs->control);
1341606f3993SIvan Khoronzhuk 	fifo_mode = (cpsw->data.dual_emac) ? CPSW_FIFO_DUAL_MAC_MODE :
1342d9ba8f9eSMugunthan V N 		     CPSW_FIFO_NORMAL_MODE;
13435d8d0d4dSIvan Khoronzhuk 	writel(fifo_mode, &cpsw->host_port_regs->tx_in_ctl);
1344df828598SMugunthan V N 
1345df828598SMugunthan V N 	/* setup host port priority mapping */
1346dda5f5feSGrygorii Strashko 	writel_relaxed(CPDMA_TX_PRIORITY_MAP,
13475d8d0d4dSIvan Khoronzhuk 		       &cpsw->host_port_regs->cpdma_tx_pri_map);
1348dda5f5feSGrygorii Strashko 	writel_relaxed(0, &cpsw->host_port_regs->cpdma_rx_chan_map);
1349df828598SMugunthan V N 
13502a05a622SIvan Khoronzhuk 	cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM,
1351df828598SMugunthan V N 			     ALE_PORT_STATE, ALE_PORT_STATE_FORWARD);
1352df828598SMugunthan V N 
1353606f3993SIvan Khoronzhuk 	if (!cpsw->data.dual_emac) {
13542a05a622SIvan Khoronzhuk 		cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr, HOST_PORT_NUM,
1355d9ba8f9eSMugunthan V N 				   0, 0);
13562a05a622SIvan Khoronzhuk 		cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast,
135771a2cbb7SGrygorii Strashko 				   ALE_PORT_HOST, 0, 0, ALE_MCAST_FWD_2);
1358df828598SMugunthan V N 	}
1359d9ba8f9eSMugunthan V N }
1360df828598SMugunthan V N 
1361c24eef28SGrygorii Strashko int cpsw_fill_rx_channels(struct cpsw_priv *priv)
13623802dce1SIvan Khoronzhuk {
13633802dce1SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
13649ed4050cSIvan Khoronzhuk 	struct cpsw_meta_xdp *xmeta;
13659ed4050cSIvan Khoronzhuk 	struct page_pool *pool;
13669ed4050cSIvan Khoronzhuk 	struct page *page;
13673802dce1SIvan Khoronzhuk 	int ch_buf_num;
1368e05107e6SIvan Khoronzhuk 	int ch, i, ret;
13699ed4050cSIvan Khoronzhuk 	dma_addr_t dma;
13703802dce1SIvan Khoronzhuk 
1371e05107e6SIvan Khoronzhuk 	for (ch = 0; ch < cpsw->rx_ch_num; ch++) {
13729ed4050cSIvan Khoronzhuk 		pool = cpsw->page_pool[ch];
13738feb0a19SIvan Khoronzhuk 		ch_buf_num = cpdma_chan_get_rx_buf_num(cpsw->rxv[ch].ch);
13743802dce1SIvan Khoronzhuk 		for (i = 0; i < ch_buf_num; i++) {
13759ed4050cSIvan Khoronzhuk 			page = page_pool_dev_alloc_pages(pool);
13769ed4050cSIvan Khoronzhuk 			if (!page) {
13779ed4050cSIvan Khoronzhuk 				cpsw_err(priv, ifup, "allocate rx page err\n");
13783802dce1SIvan Khoronzhuk 				return -ENOMEM;
13793802dce1SIvan Khoronzhuk 			}
13803802dce1SIvan Khoronzhuk 
13819ed4050cSIvan Khoronzhuk 			xmeta = page_address(page) + CPSW_XMETA_OFFSET;
13829ed4050cSIvan Khoronzhuk 			xmeta->ndev = priv->ndev;
13839ed4050cSIvan Khoronzhuk 			xmeta->ch = ch;
13849ed4050cSIvan Khoronzhuk 
13859ed4050cSIvan Khoronzhuk 			dma = page_pool_get_dma_addr(page) + CPSW_HEADROOM;
13869ed4050cSIvan Khoronzhuk 			ret = cpdma_chan_idle_submit_mapped(cpsw->rxv[ch].ch,
13879ed4050cSIvan Khoronzhuk 							    page, dma,
13889ed4050cSIvan Khoronzhuk 							    cpsw->rx_packet_max,
13899ed4050cSIvan Khoronzhuk 							    0);
13903802dce1SIvan Khoronzhuk 			if (ret < 0) {
13913802dce1SIvan Khoronzhuk 				cpsw_err(priv, ifup,
13929ed4050cSIvan Khoronzhuk 					 "cannot submit page to channel %d rx, error %d\n",
1393e05107e6SIvan Khoronzhuk 					 ch, ret);
13949ed4050cSIvan Khoronzhuk 				page_pool_recycle_direct(pool, page);
13953802dce1SIvan Khoronzhuk 				return ret;
13963802dce1SIvan Khoronzhuk 			}
13973802dce1SIvan Khoronzhuk 		}
13983802dce1SIvan Khoronzhuk 
1399e05107e6SIvan Khoronzhuk 		cpsw_info(priv, ifup, "ch %d rx, submitted %d descriptors\n",
1400e05107e6SIvan Khoronzhuk 			  ch, ch_buf_num);
1401e05107e6SIvan Khoronzhuk 	}
14023802dce1SIvan Khoronzhuk 
1403e05107e6SIvan Khoronzhuk 	return 0;
14043802dce1SIvan Khoronzhuk }
14053802dce1SIvan Khoronzhuk 
14062a05a622SIvan Khoronzhuk static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_common *cpsw)
1407aacebbf8SSebastian Siewior {
14083995d265SSchuyler Patton 	u32 slave_port;
14093995d265SSchuyler Patton 
14106f1f5836SIvan Khoronzhuk 	slave_port = cpsw_get_slave_port(slave->slave_num);
14113995d265SSchuyler Patton 
1412aacebbf8SSebastian Siewior 	if (!slave->phy)
1413aacebbf8SSebastian Siewior 		return;
1414aacebbf8SSebastian Siewior 	phy_stop(slave->phy);
1415aacebbf8SSebastian Siewior 	phy_disconnect(slave->phy);
1416aacebbf8SSebastian Siewior 	slave->phy = NULL;
14172a05a622SIvan Khoronzhuk 	cpsw_ale_control_set(cpsw->ale, slave_port,
14183995d265SSchuyler Patton 			     ALE_PORT_STATE, ALE_PORT_STATE_DISABLE);
1419cfc08345SGrygorii Strashko 	cpsw_sl_reset(slave->mac_sl, 100);
1420cfc08345SGrygorii Strashko 	cpsw_sl_ctl_reset(slave->mac_sl);
1421aacebbf8SSebastian Siewior }
1422aacebbf8SSebastian Siewior 
14237929a668SIvan Khoronzhuk static int cpsw_tc_to_fifo(int tc, int num_tc)
14247929a668SIvan Khoronzhuk {
14257929a668SIvan Khoronzhuk 	if (tc == num_tc - 1)
14267929a668SIvan Khoronzhuk 		return 0;
14277929a668SIvan Khoronzhuk 
14287929a668SIvan Khoronzhuk 	return CPSW_FIFO_SHAPERS_NUM - tc;
14297929a668SIvan Khoronzhuk }
14307929a668SIvan Khoronzhuk 
143157d90148SIvan Khoronzhuk static int cpsw_set_fifo_bw(struct cpsw_priv *priv, int fifo, int bw)
143257d90148SIvan Khoronzhuk {
143357d90148SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
143457d90148SIvan Khoronzhuk 	u32 val = 0, send_pct, shift;
143557d90148SIvan Khoronzhuk 	struct cpsw_slave *slave;
143657d90148SIvan Khoronzhuk 	int pct = 0, i;
143757d90148SIvan Khoronzhuk 
143857d90148SIvan Khoronzhuk 	if (bw > priv->shp_cfg_speed * 1000)
143957d90148SIvan Khoronzhuk 		goto err;
144057d90148SIvan Khoronzhuk 
144157d90148SIvan Khoronzhuk 	/* shaping has to stay enabled for highest fifos linearly
144257d90148SIvan Khoronzhuk 	 * and fifo bw no more then interface can allow
144357d90148SIvan Khoronzhuk 	 */
144457d90148SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
144557d90148SIvan Khoronzhuk 	send_pct = slave_read(slave, SEND_PERCENT);
144657d90148SIvan Khoronzhuk 	for (i = CPSW_FIFO_SHAPERS_NUM; i > 0; i--) {
144757d90148SIvan Khoronzhuk 		if (!bw) {
144857d90148SIvan Khoronzhuk 			if (i >= fifo || !priv->fifo_bw[i])
144957d90148SIvan Khoronzhuk 				continue;
145057d90148SIvan Khoronzhuk 
145157d90148SIvan Khoronzhuk 			dev_warn(priv->dev, "Prev FIFO%d is shaped", i);
145257d90148SIvan Khoronzhuk 			continue;
145357d90148SIvan Khoronzhuk 		}
145457d90148SIvan Khoronzhuk 
145557d90148SIvan Khoronzhuk 		if (!priv->fifo_bw[i] && i > fifo) {
145657d90148SIvan Khoronzhuk 			dev_err(priv->dev, "Upper FIFO%d is not shaped", i);
145757d90148SIvan Khoronzhuk 			return -EINVAL;
145857d90148SIvan Khoronzhuk 		}
145957d90148SIvan Khoronzhuk 
146057d90148SIvan Khoronzhuk 		shift = (i - 1) * 8;
146157d90148SIvan Khoronzhuk 		if (i == fifo) {
146257d90148SIvan Khoronzhuk 			send_pct &= ~(CPSW_PCT_MASK << shift);
146357d90148SIvan Khoronzhuk 			val = DIV_ROUND_UP(bw, priv->shp_cfg_speed * 10);
146457d90148SIvan Khoronzhuk 			if (!val)
146557d90148SIvan Khoronzhuk 				val = 1;
146657d90148SIvan Khoronzhuk 
146757d90148SIvan Khoronzhuk 			send_pct |= val << shift;
146857d90148SIvan Khoronzhuk 			pct += val;
146957d90148SIvan Khoronzhuk 			continue;
147057d90148SIvan Khoronzhuk 		}
147157d90148SIvan Khoronzhuk 
147257d90148SIvan Khoronzhuk 		if (priv->fifo_bw[i])
147357d90148SIvan Khoronzhuk 			pct += (send_pct >> shift) & CPSW_PCT_MASK;
147457d90148SIvan Khoronzhuk 	}
147557d90148SIvan Khoronzhuk 
147657d90148SIvan Khoronzhuk 	if (pct >= 100)
147757d90148SIvan Khoronzhuk 		goto err;
147857d90148SIvan Khoronzhuk 
147957d90148SIvan Khoronzhuk 	slave_write(slave, send_pct, SEND_PERCENT);
148057d90148SIvan Khoronzhuk 	priv->fifo_bw[fifo] = bw;
148157d90148SIvan Khoronzhuk 
148257d90148SIvan Khoronzhuk 	dev_warn(priv->dev, "set FIFO%d bw = %d\n", fifo,
148357d90148SIvan Khoronzhuk 		 DIV_ROUND_CLOSEST(val * priv->shp_cfg_speed, 100));
148457d90148SIvan Khoronzhuk 
148557d90148SIvan Khoronzhuk 	return 0;
148657d90148SIvan Khoronzhuk err:
148757d90148SIvan Khoronzhuk 	dev_err(priv->dev, "Bandwidth doesn't fit in tc configuration");
148857d90148SIvan Khoronzhuk 	return -EINVAL;
148957d90148SIvan Khoronzhuk }
149057d90148SIvan Khoronzhuk 
149157d90148SIvan Khoronzhuk static int cpsw_set_fifo_rlimit(struct cpsw_priv *priv, int fifo, int bw)
149257d90148SIvan Khoronzhuk {
149357d90148SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
149457d90148SIvan Khoronzhuk 	struct cpsw_slave *slave;
149557d90148SIvan Khoronzhuk 	u32 tx_in_ctl_rg, val;
149657d90148SIvan Khoronzhuk 	int ret;
149757d90148SIvan Khoronzhuk 
149857d90148SIvan Khoronzhuk 	ret = cpsw_set_fifo_bw(priv, fifo, bw);
149957d90148SIvan Khoronzhuk 	if (ret)
150057d90148SIvan Khoronzhuk 		return ret;
150157d90148SIvan Khoronzhuk 
150257d90148SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
150357d90148SIvan Khoronzhuk 	tx_in_ctl_rg = cpsw->version == CPSW_VERSION_1 ?
150457d90148SIvan Khoronzhuk 		       CPSW1_TX_IN_CTL : CPSW2_TX_IN_CTL;
150557d90148SIvan Khoronzhuk 
150657d90148SIvan Khoronzhuk 	if (!bw)
150757d90148SIvan Khoronzhuk 		cpsw_fifo_shp_on(priv, fifo, bw);
150857d90148SIvan Khoronzhuk 
150957d90148SIvan Khoronzhuk 	val = slave_read(slave, tx_in_ctl_rg);
151057d90148SIvan Khoronzhuk 	if (cpsw_shp_is_off(priv)) {
151157d90148SIvan Khoronzhuk 		/* disable FIFOs rate limited queues */
151257d90148SIvan Khoronzhuk 		val &= ~(0xf << CPSW_FIFO_RATE_EN_SHIFT);
151357d90148SIvan Khoronzhuk 
151457d90148SIvan Khoronzhuk 		/* set type of FIFO queues to normal priority mode */
151557d90148SIvan Khoronzhuk 		val &= ~(3 << CPSW_FIFO_QUEUE_TYPE_SHIFT);
151657d90148SIvan Khoronzhuk 
151757d90148SIvan Khoronzhuk 		/* set type of FIFO queues to be rate limited */
151857d90148SIvan Khoronzhuk 		if (bw)
151957d90148SIvan Khoronzhuk 			val |= 2 << CPSW_FIFO_QUEUE_TYPE_SHIFT;
152057d90148SIvan Khoronzhuk 		else
152157d90148SIvan Khoronzhuk 			priv->shp_cfg_speed = 0;
152257d90148SIvan Khoronzhuk 	}
152357d90148SIvan Khoronzhuk 
152457d90148SIvan Khoronzhuk 	/* toggle a FIFO rate limited queue */
152557d90148SIvan Khoronzhuk 	if (bw)
152657d90148SIvan Khoronzhuk 		val |= BIT(fifo + CPSW_FIFO_RATE_EN_SHIFT);
152757d90148SIvan Khoronzhuk 	else
152857d90148SIvan Khoronzhuk 		val &= ~BIT(fifo + CPSW_FIFO_RATE_EN_SHIFT);
152957d90148SIvan Khoronzhuk 	slave_write(slave, val, tx_in_ctl_rg);
153057d90148SIvan Khoronzhuk 
153157d90148SIvan Khoronzhuk 	/* FIFO transmit shape enable */
153257d90148SIvan Khoronzhuk 	cpsw_fifo_shp_on(priv, fifo, bw);
153357d90148SIvan Khoronzhuk 	return 0;
153457d90148SIvan Khoronzhuk }
153557d90148SIvan Khoronzhuk 
153657d90148SIvan Khoronzhuk /* Defaults:
153757d90148SIvan Khoronzhuk  * class A - prio 3
153857d90148SIvan Khoronzhuk  * class B - prio 2
153957d90148SIvan Khoronzhuk  * shaping for class A should be set first
154057d90148SIvan Khoronzhuk  */
154157d90148SIvan Khoronzhuk static int cpsw_set_cbs(struct net_device *ndev,
154257d90148SIvan Khoronzhuk 			struct tc_cbs_qopt_offload *qopt)
154357d90148SIvan Khoronzhuk {
154457d90148SIvan Khoronzhuk 	struct cpsw_priv *priv = netdev_priv(ndev);
154557d90148SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
154657d90148SIvan Khoronzhuk 	struct cpsw_slave *slave;
154757d90148SIvan Khoronzhuk 	int prev_speed = 0;
154857d90148SIvan Khoronzhuk 	int tc, ret, fifo;
154957d90148SIvan Khoronzhuk 	u32 bw = 0;
155057d90148SIvan Khoronzhuk 
155157d90148SIvan Khoronzhuk 	tc = netdev_txq_to_tc(priv->ndev, qopt->queue);
155257d90148SIvan Khoronzhuk 
155357d90148SIvan Khoronzhuk 	/* enable channels in backward order, as highest FIFOs must be rate
155457d90148SIvan Khoronzhuk 	 * limited first and for compliance with CPDMA rate limited channels
155557d90148SIvan Khoronzhuk 	 * that also used in bacward order. FIFO0 cannot be rate limited.
155657d90148SIvan Khoronzhuk 	 */
155757d90148SIvan Khoronzhuk 	fifo = cpsw_tc_to_fifo(tc, ndev->num_tc);
155857d90148SIvan Khoronzhuk 	if (!fifo) {
155957d90148SIvan Khoronzhuk 		dev_err(priv->dev, "Last tc%d can't be rate limited", tc);
156057d90148SIvan Khoronzhuk 		return -EINVAL;
156157d90148SIvan Khoronzhuk 	}
156257d90148SIvan Khoronzhuk 
156357d90148SIvan Khoronzhuk 	/* do nothing, it's disabled anyway */
156457d90148SIvan Khoronzhuk 	if (!qopt->enable && !priv->fifo_bw[fifo])
156557d90148SIvan Khoronzhuk 		return 0;
156657d90148SIvan Khoronzhuk 
156757d90148SIvan Khoronzhuk 	/* shapers can be set if link speed is known */
156857d90148SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
156957d90148SIvan Khoronzhuk 	if (slave->phy && slave->phy->link) {
157057d90148SIvan Khoronzhuk 		if (priv->shp_cfg_speed &&
157157d90148SIvan Khoronzhuk 		    priv->shp_cfg_speed != slave->phy->speed)
157257d90148SIvan Khoronzhuk 			prev_speed = priv->shp_cfg_speed;
157357d90148SIvan Khoronzhuk 
157457d90148SIvan Khoronzhuk 		priv->shp_cfg_speed = slave->phy->speed;
157557d90148SIvan Khoronzhuk 	}
157657d90148SIvan Khoronzhuk 
157757d90148SIvan Khoronzhuk 	if (!priv->shp_cfg_speed) {
157857d90148SIvan Khoronzhuk 		dev_err(priv->dev, "Link speed is not known");
157957d90148SIvan Khoronzhuk 		return -1;
158057d90148SIvan Khoronzhuk 	}
158157d90148SIvan Khoronzhuk 
158257d90148SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
158357d90148SIvan Khoronzhuk 	if (ret < 0) {
158457d90148SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
158557d90148SIvan Khoronzhuk 		return ret;
158657d90148SIvan Khoronzhuk 	}
158757d90148SIvan Khoronzhuk 
158857d90148SIvan Khoronzhuk 	bw = qopt->enable ? qopt->idleslope : 0;
158957d90148SIvan Khoronzhuk 	ret = cpsw_set_fifo_rlimit(priv, fifo, bw);
159057d90148SIvan Khoronzhuk 	if (ret) {
159157d90148SIvan Khoronzhuk 		priv->shp_cfg_speed = prev_speed;
159257d90148SIvan Khoronzhuk 		prev_speed = 0;
159357d90148SIvan Khoronzhuk 	}
159457d90148SIvan Khoronzhuk 
159557d90148SIvan Khoronzhuk 	if (bw && prev_speed)
159657d90148SIvan Khoronzhuk 		dev_warn(priv->dev,
159757d90148SIvan Khoronzhuk 			 "Speed was changed, CBS shaper speeds are changed!");
159857d90148SIvan Khoronzhuk 
159957d90148SIvan Khoronzhuk 	pm_runtime_put_sync(cpsw->dev);
160057d90148SIvan Khoronzhuk 	return ret;
160157d90148SIvan Khoronzhuk }
160257d90148SIvan Khoronzhuk 
16034b4255edSIvan Khoronzhuk static void cpsw_cbs_resume(struct cpsw_slave *slave, struct cpsw_priv *priv)
16044b4255edSIvan Khoronzhuk {
16054b4255edSIvan Khoronzhuk 	int fifo, bw;
16064b4255edSIvan Khoronzhuk 
16074b4255edSIvan Khoronzhuk 	for (fifo = CPSW_FIFO_SHAPERS_NUM; fifo > 0; fifo--) {
16084b4255edSIvan Khoronzhuk 		bw = priv->fifo_bw[fifo];
16094b4255edSIvan Khoronzhuk 		if (!bw)
16104b4255edSIvan Khoronzhuk 			continue;
16114b4255edSIvan Khoronzhuk 
16124b4255edSIvan Khoronzhuk 		cpsw_set_fifo_rlimit(priv, fifo, bw);
16134b4255edSIvan Khoronzhuk 	}
16144b4255edSIvan Khoronzhuk }
16154b4255edSIvan Khoronzhuk 
16164b4255edSIvan Khoronzhuk static void cpsw_mqprio_resume(struct cpsw_slave *slave, struct cpsw_priv *priv)
16174b4255edSIvan Khoronzhuk {
16184b4255edSIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
16194b4255edSIvan Khoronzhuk 	u32 tx_prio_map = 0;
16204b4255edSIvan Khoronzhuk 	int i, tc, fifo;
16214b4255edSIvan Khoronzhuk 	u32 tx_prio_rg;
16224b4255edSIvan Khoronzhuk 
16234b4255edSIvan Khoronzhuk 	if (!priv->mqprio_hw)
16244b4255edSIvan Khoronzhuk 		return;
16254b4255edSIvan Khoronzhuk 
16264b4255edSIvan Khoronzhuk 	for (i = 0; i < 8; i++) {
16274b4255edSIvan Khoronzhuk 		tc = netdev_get_prio_tc_map(priv->ndev, i);
16284b4255edSIvan Khoronzhuk 		fifo = CPSW_FIFO_SHAPERS_NUM - tc;
16294b4255edSIvan Khoronzhuk 		tx_prio_map |= fifo << (4 * i);
16304b4255edSIvan Khoronzhuk 	}
16314b4255edSIvan Khoronzhuk 
16324b4255edSIvan Khoronzhuk 	tx_prio_rg = cpsw->version == CPSW_VERSION_1 ?
16334b4255edSIvan Khoronzhuk 		     CPSW1_TX_PRI_MAP : CPSW2_TX_PRI_MAP;
16344b4255edSIvan Khoronzhuk 
16354b4255edSIvan Khoronzhuk 	slave_write(slave, tx_prio_map, tx_prio_rg);
16364b4255edSIvan Khoronzhuk }
16374b4255edSIvan Khoronzhuk 
163800fe4712SIvan Khoronzhuk static int cpsw_restore_vlans(struct net_device *vdev, int vid, void *arg)
163900fe4712SIvan Khoronzhuk {
164000fe4712SIvan Khoronzhuk 	struct cpsw_priv *priv = arg;
164100fe4712SIvan Khoronzhuk 
164200fe4712SIvan Khoronzhuk 	if (!vdev)
164300fe4712SIvan Khoronzhuk 		return 0;
164400fe4712SIvan Khoronzhuk 
164500fe4712SIvan Khoronzhuk 	cpsw_ndo_vlan_rx_add_vid(priv->ndev, 0, vid);
164600fe4712SIvan Khoronzhuk 	return 0;
164700fe4712SIvan Khoronzhuk }
164800fe4712SIvan Khoronzhuk 
16494b4255edSIvan Khoronzhuk /* restore resources after port reset */
16504b4255edSIvan Khoronzhuk static void cpsw_restore(struct cpsw_priv *priv)
16514b4255edSIvan Khoronzhuk {
165200fe4712SIvan Khoronzhuk 	/* restore vlan configurations */
165300fe4712SIvan Khoronzhuk 	vlan_for_each(priv->ndev, cpsw_restore_vlans, priv);
165400fe4712SIvan Khoronzhuk 
16554b4255edSIvan Khoronzhuk 	/* restore MQPRIO offload */
16564b4255edSIvan Khoronzhuk 	for_each_slave(priv, cpsw_mqprio_resume, priv);
16574b4255edSIvan Khoronzhuk 
16584b4255edSIvan Khoronzhuk 	/* restore CBS offload */
16594b4255edSIvan Khoronzhuk 	for_each_slave(priv, cpsw_cbs_resume, priv);
16604b4255edSIvan Khoronzhuk }
16614b4255edSIvan Khoronzhuk 
1662df828598SMugunthan V N static int cpsw_ndo_open(struct net_device *ndev)
1663df828598SMugunthan V N {
1664df828598SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
1665649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
16663802dce1SIvan Khoronzhuk 	int ret;
1667df828598SMugunthan V N 	u32 reg;
1668df828598SMugunthan V N 
166956e31bd8SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
1670108a6537SGrygorii Strashko 	if (ret < 0) {
167156e31bd8SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
1672108a6537SGrygorii Strashko 		return ret;
1673108a6537SGrygorii Strashko 	}
16743fa88c51SGrygorii Strashko 
1675df828598SMugunthan V N 	netif_carrier_off(ndev);
1676df828598SMugunthan V N 
1677e05107e6SIvan Khoronzhuk 	/* Notify the stack of the actual queue counts. */
1678e05107e6SIvan Khoronzhuk 	ret = netif_set_real_num_tx_queues(ndev, cpsw->tx_ch_num);
1679e05107e6SIvan Khoronzhuk 	if (ret) {
1680e05107e6SIvan Khoronzhuk 		dev_err(priv->dev, "cannot set real number of tx queues\n");
1681e05107e6SIvan Khoronzhuk 		goto err_cleanup;
1682e05107e6SIvan Khoronzhuk 	}
1683e05107e6SIvan Khoronzhuk 
1684e05107e6SIvan Khoronzhuk 	ret = netif_set_real_num_rx_queues(ndev, cpsw->rx_ch_num);
1685e05107e6SIvan Khoronzhuk 	if (ret) {
1686e05107e6SIvan Khoronzhuk 		dev_err(priv->dev, "cannot set real number of rx queues\n");
1687e05107e6SIvan Khoronzhuk 		goto err_cleanup;
1688e05107e6SIvan Khoronzhuk 	}
1689e05107e6SIvan Khoronzhuk 
16902a05a622SIvan Khoronzhuk 	reg = cpsw->version;
1691df828598SMugunthan V N 
1692df828598SMugunthan V N 	dev_info(priv->dev, "initializing cpsw version %d.%d (%d)\n",
1693df828598SMugunthan V N 		 CPSW_MAJOR_VERSION(reg), CPSW_MINOR_VERSION(reg),
1694df828598SMugunthan V N 		 CPSW_RTL_VERSION(reg));
1695df828598SMugunthan V N 
1696d5bc1613SIvan Khoronzhuk 	/* Initialize host and slave ports */
1697d5bc1613SIvan Khoronzhuk 	if (!cpsw->usage_count)
1698df828598SMugunthan V N 		cpsw_init_host_port(priv);
1699df828598SMugunthan V N 	for_each_slave(priv, cpsw_slave_open, priv);
1700df828598SMugunthan V N 
17013b72c2feSMugunthan V N 	/* Add default VLAN */
1702606f3993SIvan Khoronzhuk 	if (!cpsw->data.dual_emac)
17033b72c2feSMugunthan V N 		cpsw_add_default_vlan(priv);
1704e6afea0bSMugunthan V N 	else
17052a05a622SIvan Khoronzhuk 		cpsw_ale_add_vlan(cpsw->ale, cpsw->data.default_vlan,
170661f1cef9SGrygorii Strashko 				  ALE_ALL_PORTS, ALE_ALL_PORTS, 0, 0);
17073b72c2feSMugunthan V N 
1708d5bc1613SIvan Khoronzhuk 	/* initialize shared resources for every ndev */
1709d5bc1613SIvan Khoronzhuk 	if (!cpsw->usage_count) {
1710d9ba8f9eSMugunthan V N 		/* disable priority elevation */
1711dda5f5feSGrygorii Strashko 		writel_relaxed(0, &cpsw->regs->ptype);
1712df828598SMugunthan V N 
1713d9ba8f9eSMugunthan V N 		/* enable statistics collection only on all ports */
1714dda5f5feSGrygorii Strashko 		writel_relaxed(0x7, &cpsw->regs->stat_port_en);
1715df828598SMugunthan V N 
17161923d6e4SMugunthan V N 		/* Enable internal fifo flow control */
17175d8d0d4dSIvan Khoronzhuk 		writel(0x7, &cpsw->regs->flow_control);
17181923d6e4SMugunthan V N 
1719dbc4ec52SIvan Khoronzhuk 		napi_enable(&cpsw->napi_rx);
1720dbc4ec52SIvan Khoronzhuk 		napi_enable(&cpsw->napi_tx);
1721d354eb85SMugunthan V N 
1722e38b5a3dSIvan Khoronzhuk 		if (cpsw->tx_irq_disabled) {
1723e38b5a3dSIvan Khoronzhuk 			cpsw->tx_irq_disabled = false;
1724e38b5a3dSIvan Khoronzhuk 			enable_irq(cpsw->irqs_table[1]);
17257da11600SMugunthan V N 		}
17267da11600SMugunthan V N 
1727e38b5a3dSIvan Khoronzhuk 		if (cpsw->rx_irq_disabled) {
1728e38b5a3dSIvan Khoronzhuk 			cpsw->rx_irq_disabled = false;
1729e38b5a3dSIvan Khoronzhuk 			enable_irq(cpsw->irqs_table[0]);
17307da11600SMugunthan V N 		}
17317da11600SMugunthan V N 
17329ed4050cSIvan Khoronzhuk 		/* create rxqs for both infs in dual mac as they use same pool
17339ed4050cSIvan Khoronzhuk 		 * and must be destroyed together when no users.
17349ed4050cSIvan Khoronzhuk 		 */
17359ed4050cSIvan Khoronzhuk 		ret = cpsw_create_xdp_rxqs(cpsw);
17369ed4050cSIvan Khoronzhuk 		if (ret < 0)
17379ed4050cSIvan Khoronzhuk 			goto err_cleanup;
17389ed4050cSIvan Khoronzhuk 
17393802dce1SIvan Khoronzhuk 		ret = cpsw_fill_rx_channels(priv);
17403802dce1SIvan Khoronzhuk 		if (ret < 0)
1741aacebbf8SSebastian Siewior 			goto err_cleanup;
1742f280e89aSMugunthan V N 
17438a2c9a5aSGrygorii Strashko 		if (cpts_register(cpsw->cpts))
1744f280e89aSMugunthan V N 			dev_err(priv->dev, "error registering cpts device\n");
1745f280e89aSMugunthan V N 
1746d9ba8f9eSMugunthan V N 	}
1747df828598SMugunthan V N 
17484b4255edSIvan Khoronzhuk 	cpsw_restore(priv);
17494b4255edSIvan Khoronzhuk 
1750ff5b8ef2SMugunthan V N 	/* Enable Interrupt pacing if configured */
17512a05a622SIvan Khoronzhuk 	if (cpsw->coal_intvl != 0) {
1752ff5b8ef2SMugunthan V N 		struct ethtool_coalesce coal;
1753ff5b8ef2SMugunthan V N 
17542a05a622SIvan Khoronzhuk 		coal.rx_coalesce_usecs = cpsw->coal_intvl;
1755ff5b8ef2SMugunthan V N 		cpsw_set_coalesce(ndev, &coal);
1756ff5b8ef2SMugunthan V N 	}
1757ff5b8ef2SMugunthan V N 
17582c836bd9SIvan Khoronzhuk 	cpdma_ctlr_start(cpsw->dma);
17592c836bd9SIvan Khoronzhuk 	cpsw_intr_enable(cpsw);
1760d5bc1613SIvan Khoronzhuk 	cpsw->usage_count++;
1761f63a975eSMugunthan V N 
1762df828598SMugunthan V N 	return 0;
1763df828598SMugunthan V N 
1764aacebbf8SSebastian Siewior err_cleanup:
176502cacedeSIvan Khoronzhuk 	if (!cpsw->usage_count) {
17662c836bd9SIvan Khoronzhuk 		cpdma_ctlr_stop(cpsw->dma);
17679ed4050cSIvan Khoronzhuk 		cpsw_destroy_xdp_rxqs(cpsw);
176802cacedeSIvan Khoronzhuk 	}
176902cacedeSIvan Khoronzhuk 
17709ed4050cSIvan Khoronzhuk 	for_each_slave(priv, cpsw_slave_stop, cpsw);
177156e31bd8SIvan Khoronzhuk 	pm_runtime_put_sync(cpsw->dev);
1772aacebbf8SSebastian Siewior 	netif_carrier_off(priv->ndev);
1773aacebbf8SSebastian Siewior 	return ret;
1774df828598SMugunthan V N }
1775df828598SMugunthan V N 
1776df828598SMugunthan V N static int cpsw_ndo_stop(struct net_device *ndev)
1777df828598SMugunthan V N {
1778df828598SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
1779649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1780df828598SMugunthan V N 
1781df828598SMugunthan V N 	cpsw_info(priv, ifdown, "shutting down cpsw device\n");
178215180ecaSIvan Khoronzhuk 	__hw_addr_ref_unsync_dev(&ndev->mc, ndev, cpsw_purge_all_mc);
1783e05107e6SIvan Khoronzhuk 	netif_tx_stop_all_queues(priv->ndev);
1784df828598SMugunthan V N 	netif_carrier_off(priv->ndev);
1785d9ba8f9eSMugunthan V N 
1786d5bc1613SIvan Khoronzhuk 	if (cpsw->usage_count <= 1) {
1787dbc4ec52SIvan Khoronzhuk 		napi_disable(&cpsw->napi_rx);
1788dbc4ec52SIvan Khoronzhuk 		napi_disable(&cpsw->napi_tx);
17892a05a622SIvan Khoronzhuk 		cpts_unregister(cpsw->cpts);
17902c836bd9SIvan Khoronzhuk 		cpsw_intr_disable(cpsw);
17912c836bd9SIvan Khoronzhuk 		cpdma_ctlr_stop(cpsw->dma);
17922a05a622SIvan Khoronzhuk 		cpsw_ale_stop(cpsw->ale);
17939ed4050cSIvan Khoronzhuk 		cpsw_destroy_xdp_rxqs(cpsw);
1794d9ba8f9eSMugunthan V N 	}
17952a05a622SIvan Khoronzhuk 	for_each_slave(priv, cpsw_slave_stop, cpsw);
17960be01b8eSIvan Khoronzhuk 
17970be01b8eSIvan Khoronzhuk 	if (cpsw_need_resplit(cpsw))
17989763a891SGrygorii Strashko 		cpsw_split_res(cpsw);
17990be01b8eSIvan Khoronzhuk 
1800d5bc1613SIvan Khoronzhuk 	cpsw->usage_count--;
180156e31bd8SIvan Khoronzhuk 	pm_runtime_put_sync(cpsw->dev);
1802df828598SMugunthan V N 	return 0;
1803df828598SMugunthan V N }
1804df828598SMugunthan V N 
1805df828598SMugunthan V N static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
1806df828598SMugunthan V N 				       struct net_device *ndev)
1807df828598SMugunthan V N {
1808df828598SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
18092c836bd9SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1810f44f8417SIvan Khoronzhuk 	struct cpts *cpts = cpsw->cpts;
1811e05107e6SIvan Khoronzhuk 	struct netdev_queue *txq;
1812e05107e6SIvan Khoronzhuk 	struct cpdma_chan *txch;
1813e05107e6SIvan Khoronzhuk 	int ret, q_idx;
1814df828598SMugunthan V N 
1815df828598SMugunthan V N 	if (skb_padto(skb, CPSW_MIN_PACKET_SIZE)) {
1816df828598SMugunthan V N 		cpsw_err(priv, tx_err, "packet pad failed\n");
18178dc43ddcSTobias Klauser 		ndev->stats.tx_dropped++;
18181bf96050SIvan Khoronzhuk 		return NET_XMIT_DROP;
1819df828598SMugunthan V N 	}
1820df828598SMugunthan V N 
18219232b16dSMugunthan V N 	if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
1822a9423120SIvan Khoronzhuk 	    priv->tx_ts_enabled && cpts_can_timestamp(cpts, skb))
18232e5b38abSRichard Cochran 		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
18242e5b38abSRichard Cochran 
1825e05107e6SIvan Khoronzhuk 	q_idx = skb_get_queue_mapping(skb);
1826e05107e6SIvan Khoronzhuk 	if (q_idx >= cpsw->tx_ch_num)
1827e05107e6SIvan Khoronzhuk 		q_idx = q_idx % cpsw->tx_ch_num;
1828e05107e6SIvan Khoronzhuk 
18298feb0a19SIvan Khoronzhuk 	txch = cpsw->txv[q_idx].ch;
183062f94c21SGrygorii Strashko 	txq = netdev_get_tx_queue(ndev, q_idx);
183110ae8054SGrygorii Strashko 	skb_tx_timestamp(skb);
183210ae8054SGrygorii Strashko 	ret = cpdma_chan_submit(txch, skb, skb->data, skb->len,
183310ae8054SGrygorii Strashko 				priv->emac_port + cpsw->data.dual_emac);
1834df828598SMugunthan V N 	if (unlikely(ret != 0)) {
1835df828598SMugunthan V N 		cpsw_err(priv, tx_err, "desc submit failed\n");
1836df828598SMugunthan V N 		goto fail;
1837df828598SMugunthan V N 	}
1838df828598SMugunthan V N 
1839fae50823SMugunthan V N 	/* If there is no more tx desc left free then we need to
1840fae50823SMugunthan V N 	 * tell the kernel to stop sending us tx frames.
1841fae50823SMugunthan V N 	 */
1842e05107e6SIvan Khoronzhuk 	if (unlikely(!cpdma_check_free_tx_desc(txch))) {
1843e05107e6SIvan Khoronzhuk 		netif_tx_stop_queue(txq);
184462f94c21SGrygorii Strashko 
184562f94c21SGrygorii Strashko 		/* Barrier, so that stop_queue visible to other cpus */
184662f94c21SGrygorii Strashko 		smp_mb__after_atomic();
184762f94c21SGrygorii Strashko 
184862f94c21SGrygorii Strashko 		if (cpdma_check_free_tx_desc(txch))
184962f94c21SGrygorii Strashko 			netif_tx_wake_queue(txq);
1850e05107e6SIvan Khoronzhuk 	}
1851fae50823SMugunthan V N 
1852df828598SMugunthan V N 	return NETDEV_TX_OK;
1853df828598SMugunthan V N fail:
18548dc43ddcSTobias Klauser 	ndev->stats.tx_dropped++;
1855e05107e6SIvan Khoronzhuk 	netif_tx_stop_queue(txq);
185662f94c21SGrygorii Strashko 
185762f94c21SGrygorii Strashko 	/* Barrier, so that stop_queue visible to other cpus */
185862f94c21SGrygorii Strashko 	smp_mb__after_atomic();
185962f94c21SGrygorii Strashko 
186062f94c21SGrygorii Strashko 	if (cpdma_check_free_tx_desc(txch))
186162f94c21SGrygorii Strashko 		netif_tx_wake_queue(txq);
186262f94c21SGrygorii Strashko 
1863df828598SMugunthan V N 	return NETDEV_TX_BUSY;
1864df828598SMugunthan V N }
1865df828598SMugunthan V N 
1866c8395d4eSGrygorii Strashko #if IS_ENABLED(CONFIG_TI_CPTS)
18672e5b38abSRichard Cochran 
1868a9423120SIvan Khoronzhuk static void cpsw_hwtstamp_v1(struct cpsw_priv *priv)
18692e5b38abSRichard Cochran {
1870a9423120SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1871606f3993SIvan Khoronzhuk 	struct cpsw_slave *slave = &cpsw->slaves[cpsw->data.active_slave];
18722e5b38abSRichard Cochran 	u32 ts_en, seq_id;
18732e5b38abSRichard Cochran 
1874a9423120SIvan Khoronzhuk 	if (!priv->tx_ts_enabled && !priv->rx_ts_enabled) {
18752e5b38abSRichard Cochran 		slave_write(slave, 0, CPSW1_TS_CTL);
18762e5b38abSRichard Cochran 		return;
18772e5b38abSRichard Cochran 	}
18782e5b38abSRichard Cochran 
18792e5b38abSRichard Cochran 	seq_id = (30 << CPSW_V1_SEQ_ID_OFS_SHIFT) | ETH_P_1588;
18802e5b38abSRichard Cochran 	ts_en = EVENT_MSG_BITS << CPSW_V1_MSG_TYPE_OFS;
18812e5b38abSRichard Cochran 
1882a9423120SIvan Khoronzhuk 	if (priv->tx_ts_enabled)
18832e5b38abSRichard Cochran 		ts_en |= CPSW_V1_TS_TX_EN;
18842e5b38abSRichard Cochran 
1885a9423120SIvan Khoronzhuk 	if (priv->rx_ts_enabled)
18862e5b38abSRichard Cochran 		ts_en |= CPSW_V1_TS_RX_EN;
18872e5b38abSRichard Cochran 
18882e5b38abSRichard Cochran 	slave_write(slave, ts_en, CPSW1_TS_CTL);
18892e5b38abSRichard Cochran 	slave_write(slave, seq_id, CPSW1_TS_SEQ_LTYPE);
18902e5b38abSRichard Cochran }
18912e5b38abSRichard Cochran 
18922e5b38abSRichard Cochran static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
18932e5b38abSRichard Cochran {
1894d9ba8f9eSMugunthan V N 	struct cpsw_slave *slave;
18955d8d0d4dSIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
18962e5b38abSRichard Cochran 	u32 ctrl, mtype;
18972e5b38abSRichard Cochran 
1898cb7d78d0SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
1899d9ba8f9eSMugunthan V N 
19002e5b38abSRichard Cochran 	ctrl = slave_read(slave, CPSW2_CONTROL);
19012a05a622SIvan Khoronzhuk 	switch (cpsw->version) {
190209c55372SGeorge Cherian 	case CPSW_VERSION_2:
190309c55372SGeorge Cherian 		ctrl &= ~CTRL_V2_ALL_TS_MASK;
19042e5b38abSRichard Cochran 
1905a9423120SIvan Khoronzhuk 		if (priv->tx_ts_enabled)
190609c55372SGeorge Cherian 			ctrl |= CTRL_V2_TX_TS_BITS;
19072e5b38abSRichard Cochran 
1908a9423120SIvan Khoronzhuk 		if (priv->rx_ts_enabled)
190909c55372SGeorge Cherian 			ctrl |= CTRL_V2_RX_TS_BITS;
191009c55372SGeorge Cherian 		break;
191109c55372SGeorge Cherian 	case CPSW_VERSION_3:
191209c55372SGeorge Cherian 	default:
191309c55372SGeorge Cherian 		ctrl &= ~CTRL_V3_ALL_TS_MASK;
191409c55372SGeorge Cherian 
1915a9423120SIvan Khoronzhuk 		if (priv->tx_ts_enabled)
191609c55372SGeorge Cherian 			ctrl |= CTRL_V3_TX_TS_BITS;
191709c55372SGeorge Cherian 
1918a9423120SIvan Khoronzhuk 		if (priv->rx_ts_enabled)
191909c55372SGeorge Cherian 			ctrl |= CTRL_V3_RX_TS_BITS;
192009c55372SGeorge Cherian 		break;
192109c55372SGeorge Cherian 	}
19222e5b38abSRichard Cochran 
19232e5b38abSRichard Cochran 	mtype = (30 << TS_SEQ_ID_OFFSET_SHIFT) | EVENT_MSG_BITS;
19242e5b38abSRichard Cochran 
19252e5b38abSRichard Cochran 	slave_write(slave, mtype, CPSW2_TS_SEQ_MTYPE);
19262e5b38abSRichard Cochran 	slave_write(slave, ctrl, CPSW2_CONTROL);
1927dda5f5feSGrygorii Strashko 	writel_relaxed(ETH_P_1588, &cpsw->regs->ts_ltype);
19281ebb2446SIvan Khoronzhuk 	writel_relaxed(ETH_P_8021Q, &cpsw->regs->vlan_ltype);
19292e5b38abSRichard Cochran }
19302e5b38abSRichard Cochran 
1931a5b4145bSBen Hutchings static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
19322e5b38abSRichard Cochran {
19333177bf6fSMugunthan V N 	struct cpsw_priv *priv = netdev_priv(dev);
19342e5b38abSRichard Cochran 	struct hwtstamp_config cfg;
19352a05a622SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
19362e5b38abSRichard Cochran 
19372a05a622SIvan Khoronzhuk 	if (cpsw->version != CPSW_VERSION_1 &&
19382a05a622SIvan Khoronzhuk 	    cpsw->version != CPSW_VERSION_2 &&
19392a05a622SIvan Khoronzhuk 	    cpsw->version != CPSW_VERSION_3)
19402ee91e54SBen Hutchings 		return -EOPNOTSUPP;
19412ee91e54SBen Hutchings 
19422e5b38abSRichard Cochran 	if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
19432e5b38abSRichard Cochran 		return -EFAULT;
19442e5b38abSRichard Cochran 
19452e5b38abSRichard Cochran 	/* reserved for future extensions */
19462e5b38abSRichard Cochran 	if (cfg.flags)
19472e5b38abSRichard Cochran 		return -EINVAL;
19482e5b38abSRichard Cochran 
19492ee91e54SBen Hutchings 	if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON)
19502e5b38abSRichard Cochran 		return -ERANGE;
19512e5b38abSRichard Cochran 
19522e5b38abSRichard Cochran 	switch (cfg.rx_filter) {
19532e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_NONE:
1954a9423120SIvan Khoronzhuk 		priv->rx_ts_enabled = 0;
19552e5b38abSRichard Cochran 		break;
19562e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_ALL:
1957e9523a5aSGrygorii Strashko 	case HWTSTAMP_FILTER_NTP_ALL:
1958e9523a5aSGrygorii Strashko 		return -ERANGE;
19592e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
19602e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
19612e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
1962a9423120SIvan Khoronzhuk 		priv->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
1963e9523a5aSGrygorii Strashko 		cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
1964e9523a5aSGrygorii Strashko 		break;
19652e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
19662e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
19672e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
19682e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
19692e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
19702e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
19712e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_EVENT:
19722e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_SYNC:
19732e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
1974a9423120SIvan Khoronzhuk 		priv->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V2_EVENT;
19752e5b38abSRichard Cochran 		cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
19762e5b38abSRichard Cochran 		break;
19772e5b38abSRichard Cochran 	default:
19782e5b38abSRichard Cochran 		return -ERANGE;
19792e5b38abSRichard Cochran 	}
19802e5b38abSRichard Cochran 
1981a9423120SIvan Khoronzhuk 	priv->tx_ts_enabled = cfg.tx_type == HWTSTAMP_TX_ON;
19822ee91e54SBen Hutchings 
19832a05a622SIvan Khoronzhuk 	switch (cpsw->version) {
19842e5b38abSRichard Cochran 	case CPSW_VERSION_1:
1985a9423120SIvan Khoronzhuk 		cpsw_hwtstamp_v1(priv);
19862e5b38abSRichard Cochran 		break;
19872e5b38abSRichard Cochran 	case CPSW_VERSION_2:
1988f7d403cbSGeorge Cherian 	case CPSW_VERSION_3:
19892e5b38abSRichard Cochran 		cpsw_hwtstamp_v2(priv);
19902e5b38abSRichard Cochran 		break;
19912e5b38abSRichard Cochran 	default:
19922ee91e54SBen Hutchings 		WARN_ON(1);
19932e5b38abSRichard Cochran 	}
19942e5b38abSRichard Cochran 
19952e5b38abSRichard Cochran 	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
19962e5b38abSRichard Cochran }
19972e5b38abSRichard Cochran 
1998a5b4145bSBen Hutchings static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
1999a5b4145bSBen Hutchings {
20002a05a622SIvan Khoronzhuk 	struct cpsw_common *cpsw = ndev_to_cpsw(dev);
2001a9423120SIvan Khoronzhuk 	struct cpsw_priv *priv = netdev_priv(dev);
2002a5b4145bSBen Hutchings 	struct hwtstamp_config cfg;
2003a5b4145bSBen Hutchings 
20042a05a622SIvan Khoronzhuk 	if (cpsw->version != CPSW_VERSION_1 &&
20052a05a622SIvan Khoronzhuk 	    cpsw->version != CPSW_VERSION_2 &&
20062a05a622SIvan Khoronzhuk 	    cpsw->version != CPSW_VERSION_3)
2007a5b4145bSBen Hutchings 		return -EOPNOTSUPP;
2008a5b4145bSBen Hutchings 
2009a5b4145bSBen Hutchings 	cfg.flags = 0;
2010a9423120SIvan Khoronzhuk 	cfg.tx_type = priv->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
2011a9423120SIvan Khoronzhuk 	cfg.rx_filter = priv->rx_ts_enabled;
2012a5b4145bSBen Hutchings 
2013a5b4145bSBen Hutchings 	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
2014a5b4145bSBen Hutchings }
2015c8395d4eSGrygorii Strashko #else
2016c8395d4eSGrygorii Strashko static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
2017c8395d4eSGrygorii Strashko {
2018c8395d4eSGrygorii Strashko 	return -EOPNOTSUPP;
2019c8395d4eSGrygorii Strashko }
2020a5b4145bSBen Hutchings 
2021c8395d4eSGrygorii Strashko static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
2022c8395d4eSGrygorii Strashko {
2023c8395d4eSGrygorii Strashko 	return -EOPNOTSUPP;
2024c8395d4eSGrygorii Strashko }
20252e5b38abSRichard Cochran #endif /*CONFIG_TI_CPTS*/
20262e5b38abSRichard Cochran 
20272e5b38abSRichard Cochran static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
20282e5b38abSRichard Cochran {
202911f2c988SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(dev);
2030606f3993SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
2031606f3993SIvan Khoronzhuk 	int slave_no = cpsw_slave_index(cpsw, priv);
203211f2c988SMugunthan V N 
20332e5b38abSRichard Cochran 	if (!netif_running(dev))
20342e5b38abSRichard Cochran 		return -EINVAL;
20352e5b38abSRichard Cochran 
203611f2c988SMugunthan V N 	switch (cmd) {
203711f2c988SMugunthan V N 	case SIOCSHWTSTAMP:
2038a5b4145bSBen Hutchings 		return cpsw_hwtstamp_set(dev, req);
2039a5b4145bSBen Hutchings 	case SIOCGHWTSTAMP:
2040a5b4145bSBen Hutchings 		return cpsw_hwtstamp_get(dev, req);
20412e5b38abSRichard Cochran 	}
20422e5b38abSRichard Cochran 
2043606f3993SIvan Khoronzhuk 	if (!cpsw->slaves[slave_no].phy)
2044c1b59947SStefan Sørensen 		return -EOPNOTSUPP;
2045606f3993SIvan Khoronzhuk 	return phy_mii_ioctl(cpsw->slaves[slave_no].phy, req, cmd);
204611f2c988SMugunthan V N }
204711f2c988SMugunthan V N 
2048df828598SMugunthan V N static void cpsw_ndo_tx_timeout(struct net_device *ndev)
2049df828598SMugunthan V N {
2050df828598SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
20512c836bd9SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
2052e05107e6SIvan Khoronzhuk 	int ch;
2053df828598SMugunthan V N 
2054df828598SMugunthan V N 	cpsw_err(priv, tx_err, "transmit timeout, restarting dma\n");
20558dc43ddcSTobias Klauser 	ndev->stats.tx_errors++;
20562c836bd9SIvan Khoronzhuk 	cpsw_intr_disable(cpsw);
2057e05107e6SIvan Khoronzhuk 	for (ch = 0; ch < cpsw->tx_ch_num; ch++) {
20588feb0a19SIvan Khoronzhuk 		cpdma_chan_stop(cpsw->txv[ch].ch);
20598feb0a19SIvan Khoronzhuk 		cpdma_chan_start(cpsw->txv[ch].ch);
2060e05107e6SIvan Khoronzhuk 	}
2061e05107e6SIvan Khoronzhuk 
20622c836bd9SIvan Khoronzhuk 	cpsw_intr_enable(cpsw);
206375514b66SGrygorii Strashko 	netif_trans_update(ndev);
206475514b66SGrygorii Strashko 	netif_tx_wake_all_queues(ndev);
2065df828598SMugunthan V N }
2066df828598SMugunthan V N 
2067dcfd8d58SMugunthan V N static int cpsw_ndo_set_mac_address(struct net_device *ndev, void *p)
2068dcfd8d58SMugunthan V N {
2069dcfd8d58SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
2070dcfd8d58SMugunthan V N 	struct sockaddr *addr = (struct sockaddr *)p;
2071649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
2072dcfd8d58SMugunthan V N 	int flags = 0;
2073dcfd8d58SMugunthan V N 	u16 vid = 0;
2074a6c5d14fSGrygorii Strashko 	int ret;
2075dcfd8d58SMugunthan V N 
2076dcfd8d58SMugunthan V N 	if (!is_valid_ether_addr(addr->sa_data))
2077dcfd8d58SMugunthan V N 		return -EADDRNOTAVAIL;
2078dcfd8d58SMugunthan V N 
207956e31bd8SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
2080a6c5d14fSGrygorii Strashko 	if (ret < 0) {
208156e31bd8SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
2082a6c5d14fSGrygorii Strashko 		return ret;
2083a6c5d14fSGrygorii Strashko 	}
2084a6c5d14fSGrygorii Strashko 
2085606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
2086606f3993SIvan Khoronzhuk 		vid = cpsw->slaves[priv->emac_port].port_vlan;
2087dcfd8d58SMugunthan V N 		flags = ALE_VLAN;
2088dcfd8d58SMugunthan V N 	}
2089dcfd8d58SMugunthan V N 
20902a05a622SIvan Khoronzhuk 	cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr, HOST_PORT_NUM,
2091dcfd8d58SMugunthan V N 			   flags, vid);
20922a05a622SIvan Khoronzhuk 	cpsw_ale_add_ucast(cpsw->ale, addr->sa_data, HOST_PORT_NUM,
2093dcfd8d58SMugunthan V N 			   flags, vid);
2094dcfd8d58SMugunthan V N 
2095dcfd8d58SMugunthan V N 	memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
2096dcfd8d58SMugunthan V N 	memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
2097dcfd8d58SMugunthan V N 	for_each_slave(priv, cpsw_set_slave_mac, priv);
2098dcfd8d58SMugunthan V N 
209956e31bd8SIvan Khoronzhuk 	pm_runtime_put(cpsw->dev);
2100a6c5d14fSGrygorii Strashko 
2101dcfd8d58SMugunthan V N 	return 0;
2102dcfd8d58SMugunthan V N }
2103dcfd8d58SMugunthan V N 
21043b72c2feSMugunthan V N static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv,
21053b72c2feSMugunthan V N 				unsigned short vid)
21063b72c2feSMugunthan V N {
21073b72c2feSMugunthan V N 	int ret;
21089f6bd8faSMugunthan V N 	int unreg_mcast_mask = 0;
21095b3a5a14SIvan Khoronzhuk 	int mcast_mask;
21109f6bd8faSMugunthan V N 	u32 port_mask;
2111606f3993SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
21129f6bd8faSMugunthan V N 
2113606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
21149f6bd8faSMugunthan V N 		port_mask = (1 << (priv->emac_port + 1)) | ALE_PORT_HOST;
21159f6bd8faSMugunthan V N 
21165b3a5a14SIvan Khoronzhuk 		mcast_mask = ALE_PORT_HOST;
21179f6bd8faSMugunthan V N 		if (priv->ndev->flags & IFF_ALLMULTI)
21185b3a5a14SIvan Khoronzhuk 			unreg_mcast_mask = mcast_mask;
21199f6bd8faSMugunthan V N 	} else {
21209f6bd8faSMugunthan V N 		port_mask = ALE_ALL_PORTS;
21215b3a5a14SIvan Khoronzhuk 		mcast_mask = port_mask;
21221e5c4bc4SLennart Sorensen 
21231e5c4bc4SLennart Sorensen 		if (priv->ndev->flags & IFF_ALLMULTI)
21241e5c4bc4SLennart Sorensen 			unreg_mcast_mask = ALE_ALL_PORTS;
21251e5c4bc4SLennart Sorensen 		else
21261e5c4bc4SLennart Sorensen 			unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2;
21279f6bd8faSMugunthan V N 	}
21283b72c2feSMugunthan V N 
21292a05a622SIvan Khoronzhuk 	ret = cpsw_ale_add_vlan(cpsw->ale, vid, port_mask, 0, port_mask,
213061f1cef9SGrygorii Strashko 				unreg_mcast_mask);
21313b72c2feSMugunthan V N 	if (ret != 0)
21323b72c2feSMugunthan V N 		return ret;
21333b72c2feSMugunthan V N 
21342a05a622SIvan Khoronzhuk 	ret = cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr,
213571a2cbb7SGrygorii Strashko 				 HOST_PORT_NUM, ALE_VLAN, vid);
21363b72c2feSMugunthan V N 	if (ret != 0)
21373b72c2feSMugunthan V N 		goto clean_vid;
21383b72c2feSMugunthan V N 
21392a05a622SIvan Khoronzhuk 	ret = cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast,
21405b3a5a14SIvan Khoronzhuk 				 mcast_mask, ALE_VLAN, vid, 0);
21413b72c2feSMugunthan V N 	if (ret != 0)
21423b72c2feSMugunthan V N 		goto clean_vlan_ucast;
21433b72c2feSMugunthan V N 	return 0;
21443b72c2feSMugunthan V N 
21453b72c2feSMugunthan V N clean_vlan_ucast:
21462a05a622SIvan Khoronzhuk 	cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr,
214771a2cbb7SGrygorii Strashko 			   HOST_PORT_NUM, ALE_VLAN, vid);
21483b72c2feSMugunthan V N clean_vid:
21492a05a622SIvan Khoronzhuk 	cpsw_ale_del_vlan(cpsw->ale, vid, 0);
21503b72c2feSMugunthan V N 	return ret;
21513b72c2feSMugunthan V N }
21523b72c2feSMugunthan V N 
21533b72c2feSMugunthan V N static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev,
215480d5c368SPatrick McHardy 				    __be16 proto, u16 vid)
21553b72c2feSMugunthan V N {
21563b72c2feSMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
2157649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
2158a6c5d14fSGrygorii Strashko 	int ret;
21593b72c2feSMugunthan V N 
2160606f3993SIvan Khoronzhuk 	if (vid == cpsw->data.default_vlan)
21613b72c2feSMugunthan V N 		return 0;
21623b72c2feSMugunthan V N 
216356e31bd8SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
2164a6c5d14fSGrygorii Strashko 	if (ret < 0) {
216556e31bd8SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
2166a6c5d14fSGrygorii Strashko 		return ret;
2167a6c5d14fSGrygorii Strashko 	}
2168a6c5d14fSGrygorii Strashko 
2169606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
217002a54164SMugunthan V N 		/* In dual EMAC, reserved VLAN id should not be used for
217102a54164SMugunthan V N 		 * creating VLAN interfaces as this can break the dual
217202a54164SMugunthan V N 		 * EMAC port separation
217302a54164SMugunthan V N 		 */
217402a54164SMugunthan V N 		int i;
217502a54164SMugunthan V N 
2176606f3993SIvan Khoronzhuk 		for (i = 0; i < cpsw->data.slaves; i++) {
2177803c4f64SIvan Khoronzhuk 			if (vid == cpsw->slaves[i].port_vlan) {
2178803c4f64SIvan Khoronzhuk 				ret = -EINVAL;
2179803c4f64SIvan Khoronzhuk 				goto err;
2180803c4f64SIvan Khoronzhuk 			}
218102a54164SMugunthan V N 		}
218202a54164SMugunthan V N 	}
218302a54164SMugunthan V N 
21843b72c2feSMugunthan V N 	dev_info(priv->dev, "Adding vlanid %d to vlan filter\n", vid);
2185a6c5d14fSGrygorii Strashko 	ret = cpsw_add_vlan_ale_entry(priv, vid);
2186803c4f64SIvan Khoronzhuk err:
218756e31bd8SIvan Khoronzhuk 	pm_runtime_put(cpsw->dev);
2188a6c5d14fSGrygorii Strashko 	return ret;
21893b72c2feSMugunthan V N }
21903b72c2feSMugunthan V N 
21913b72c2feSMugunthan V N static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev,
219280d5c368SPatrick McHardy 				     __be16 proto, u16 vid)
21933b72c2feSMugunthan V N {
21943b72c2feSMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
2195649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
21963b72c2feSMugunthan V N 	int ret;
21973b72c2feSMugunthan V N 
2198606f3993SIvan Khoronzhuk 	if (vid == cpsw->data.default_vlan)
21993b72c2feSMugunthan V N 		return 0;
22003b72c2feSMugunthan V N 
220156e31bd8SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
2202a6c5d14fSGrygorii Strashko 	if (ret < 0) {
220356e31bd8SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
2204a6c5d14fSGrygorii Strashko 		return ret;
2205a6c5d14fSGrygorii Strashko 	}
2206a6c5d14fSGrygorii Strashko 
2207606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
220802a54164SMugunthan V N 		int i;
220902a54164SMugunthan V N 
2210606f3993SIvan Khoronzhuk 		for (i = 0; i < cpsw->data.slaves; i++) {
2211606f3993SIvan Khoronzhuk 			if (vid == cpsw->slaves[i].port_vlan)
2212803c4f64SIvan Khoronzhuk 				goto err;
221302a54164SMugunthan V N 		}
221402a54164SMugunthan V N 	}
221502a54164SMugunthan V N 
22163b72c2feSMugunthan V N 	dev_info(priv->dev, "removing vlanid %d from vlan filter\n", vid);
22172a05a622SIvan Khoronzhuk 	ret = cpsw_ale_del_vlan(cpsw->ale, vid, 0);
2218be35b982SIvan Khoronzhuk 	ret |= cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr,
221961f1cef9SGrygorii Strashko 				  HOST_PORT_NUM, ALE_VLAN, vid);
2220be35b982SIvan Khoronzhuk 	ret |= cpsw_ale_del_mcast(cpsw->ale, priv->ndev->broadcast,
22213b72c2feSMugunthan V N 				  0, ALE_VLAN, vid);
222215180ecaSIvan Khoronzhuk 	ret |= cpsw_ale_flush_multicast(cpsw->ale, 0, vid);
2223803c4f64SIvan Khoronzhuk err:
222456e31bd8SIvan Khoronzhuk 	pm_runtime_put(cpsw->dev);
2225a6c5d14fSGrygorii Strashko 	return ret;
22263b72c2feSMugunthan V N }
22273b72c2feSMugunthan V N 
222883fcad0cSIvan Khoronzhuk static int cpsw_ndo_set_tx_maxrate(struct net_device *ndev, int queue, u32 rate)
222983fcad0cSIvan Khoronzhuk {
223083fcad0cSIvan Khoronzhuk 	struct cpsw_priv *priv = netdev_priv(ndev);
223183fcad0cSIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
223252986a2fSIvan Khoronzhuk 	struct cpsw_slave *slave;
223332b78d85SIvan Khoronzhuk 	u32 min_rate;
223483fcad0cSIvan Khoronzhuk 	u32 ch_rate;
223552986a2fSIvan Khoronzhuk 	int i, ret;
223683fcad0cSIvan Khoronzhuk 
223783fcad0cSIvan Khoronzhuk 	ch_rate = netdev_get_tx_queue(ndev, queue)->tx_maxrate;
223883fcad0cSIvan Khoronzhuk 	if (ch_rate == rate)
223983fcad0cSIvan Khoronzhuk 		return 0;
224083fcad0cSIvan Khoronzhuk 
224132b78d85SIvan Khoronzhuk 	ch_rate = rate * 1000;
224283fcad0cSIvan Khoronzhuk 	min_rate = cpdma_chan_get_min_rate(cpsw->dma);
224332b78d85SIvan Khoronzhuk 	if ((ch_rate < min_rate && ch_rate)) {
224432b78d85SIvan Khoronzhuk 		dev_err(priv->dev, "The channel rate cannot be less than %dMbps",
224583fcad0cSIvan Khoronzhuk 			min_rate);
224683fcad0cSIvan Khoronzhuk 		return -EINVAL;
224783fcad0cSIvan Khoronzhuk 	}
224883fcad0cSIvan Khoronzhuk 
22490be01b8eSIvan Khoronzhuk 	if (rate > cpsw->speed) {
225032b78d85SIvan Khoronzhuk 		dev_err(priv->dev, "The channel rate cannot be more than 2Gbps");
225132b78d85SIvan Khoronzhuk 		return -EINVAL;
225232b78d85SIvan Khoronzhuk 	}
225332b78d85SIvan Khoronzhuk 
225483fcad0cSIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
225583fcad0cSIvan Khoronzhuk 	if (ret < 0) {
225683fcad0cSIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
225783fcad0cSIvan Khoronzhuk 		return ret;
225883fcad0cSIvan Khoronzhuk 	}
225983fcad0cSIvan Khoronzhuk 
226032b78d85SIvan Khoronzhuk 	ret = cpdma_chan_set_rate(cpsw->txv[queue].ch, ch_rate);
226183fcad0cSIvan Khoronzhuk 	pm_runtime_put(cpsw->dev);
226232b78d85SIvan Khoronzhuk 
226332b78d85SIvan Khoronzhuk 	if (ret)
226432b78d85SIvan Khoronzhuk 		return ret;
226532b78d85SIvan Khoronzhuk 
226652986a2fSIvan Khoronzhuk 	/* update rates for slaves tx queues */
226752986a2fSIvan Khoronzhuk 	for (i = 0; i < cpsw->data.slaves; i++) {
226852986a2fSIvan Khoronzhuk 		slave = &cpsw->slaves[i];
226952986a2fSIvan Khoronzhuk 		if (!slave->ndev)
227052986a2fSIvan Khoronzhuk 			continue;
227152986a2fSIvan Khoronzhuk 
227252986a2fSIvan Khoronzhuk 		netdev_get_tx_queue(slave->ndev, queue)->tx_maxrate = rate;
227352986a2fSIvan Khoronzhuk 	}
227452986a2fSIvan Khoronzhuk 
22759763a891SGrygorii Strashko 	cpsw_split_res(cpsw);
227683fcad0cSIvan Khoronzhuk 	return ret;
227783fcad0cSIvan Khoronzhuk }
227883fcad0cSIvan Khoronzhuk 
22797929a668SIvan Khoronzhuk static int cpsw_set_mqprio(struct net_device *ndev, void *type_data)
22807929a668SIvan Khoronzhuk {
22817929a668SIvan Khoronzhuk 	struct tc_mqprio_qopt_offload *mqprio = type_data;
22827929a668SIvan Khoronzhuk 	struct cpsw_priv *priv = netdev_priv(ndev);
22837929a668SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
22847929a668SIvan Khoronzhuk 	int fifo, num_tc, count, offset;
22857929a668SIvan Khoronzhuk 	struct cpsw_slave *slave;
22867929a668SIvan Khoronzhuk 	u32 tx_prio_map = 0;
22877929a668SIvan Khoronzhuk 	int i, tc, ret;
22887929a668SIvan Khoronzhuk 
22897929a668SIvan Khoronzhuk 	num_tc = mqprio->qopt.num_tc;
22907929a668SIvan Khoronzhuk 	if (num_tc > CPSW_TC_NUM)
22917929a668SIvan Khoronzhuk 		return -EINVAL;
22927929a668SIvan Khoronzhuk 
22937929a668SIvan Khoronzhuk 	if (mqprio->mode != TC_MQPRIO_MODE_DCB)
22947929a668SIvan Khoronzhuk 		return -EINVAL;
22957929a668SIvan Khoronzhuk 
22967929a668SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
22977929a668SIvan Khoronzhuk 	if (ret < 0) {
22987929a668SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
22997929a668SIvan Khoronzhuk 		return ret;
23007929a668SIvan Khoronzhuk 	}
23017929a668SIvan Khoronzhuk 
23027929a668SIvan Khoronzhuk 	if (num_tc) {
23037929a668SIvan Khoronzhuk 		for (i = 0; i < 8; i++) {
23047929a668SIvan Khoronzhuk 			tc = mqprio->qopt.prio_tc_map[i];
23057929a668SIvan Khoronzhuk 			fifo = cpsw_tc_to_fifo(tc, num_tc);
23067929a668SIvan Khoronzhuk 			tx_prio_map |= fifo << (4 * i);
23077929a668SIvan Khoronzhuk 		}
23087929a668SIvan Khoronzhuk 
23097929a668SIvan Khoronzhuk 		netdev_set_num_tc(ndev, num_tc);
23107929a668SIvan Khoronzhuk 		for (i = 0; i < num_tc; i++) {
23117929a668SIvan Khoronzhuk 			count = mqprio->qopt.count[i];
23127929a668SIvan Khoronzhuk 			offset = mqprio->qopt.offset[i];
23137929a668SIvan Khoronzhuk 			netdev_set_tc_queue(ndev, i, count, offset);
23147929a668SIvan Khoronzhuk 		}
23157929a668SIvan Khoronzhuk 	}
23167929a668SIvan Khoronzhuk 
23177929a668SIvan Khoronzhuk 	if (!mqprio->qopt.hw) {
23187929a668SIvan Khoronzhuk 		/* restore default configuration */
23197929a668SIvan Khoronzhuk 		netdev_reset_tc(ndev);
23207929a668SIvan Khoronzhuk 		tx_prio_map = TX_PRIORITY_MAPPING;
23217929a668SIvan Khoronzhuk 	}
23227929a668SIvan Khoronzhuk 
23237929a668SIvan Khoronzhuk 	priv->mqprio_hw = mqprio->qopt.hw;
23247929a668SIvan Khoronzhuk 
23257929a668SIvan Khoronzhuk 	offset = cpsw->version == CPSW_VERSION_1 ?
23267929a668SIvan Khoronzhuk 		 CPSW1_TX_PRI_MAP : CPSW2_TX_PRI_MAP;
23277929a668SIvan Khoronzhuk 
23287929a668SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
23297929a668SIvan Khoronzhuk 	slave_write(slave, tx_prio_map, offset);
23307929a668SIvan Khoronzhuk 
23317929a668SIvan Khoronzhuk 	pm_runtime_put_sync(cpsw->dev);
23327929a668SIvan Khoronzhuk 
23337929a668SIvan Khoronzhuk 	return 0;
23347929a668SIvan Khoronzhuk }
23357929a668SIvan Khoronzhuk 
23367929a668SIvan Khoronzhuk static int cpsw_ndo_setup_tc(struct net_device *ndev, enum tc_setup_type type,
23377929a668SIvan Khoronzhuk 			     void *type_data)
23387929a668SIvan Khoronzhuk {
23397929a668SIvan Khoronzhuk 	switch (type) {
234057d90148SIvan Khoronzhuk 	case TC_SETUP_QDISC_CBS:
234157d90148SIvan Khoronzhuk 		return cpsw_set_cbs(ndev, type_data);
234257d90148SIvan Khoronzhuk 
23437929a668SIvan Khoronzhuk 	case TC_SETUP_QDISC_MQPRIO:
23447929a668SIvan Khoronzhuk 		return cpsw_set_mqprio(ndev, type_data);
23457929a668SIvan Khoronzhuk 
23467929a668SIvan Khoronzhuk 	default:
23477929a668SIvan Khoronzhuk 		return -EOPNOTSUPP;
23487929a668SIvan Khoronzhuk 	}
23497929a668SIvan Khoronzhuk }
23507929a668SIvan Khoronzhuk 
23519ed4050cSIvan Khoronzhuk static int cpsw_xdp_prog_setup(struct cpsw_priv *priv, struct netdev_bpf *bpf)
23529ed4050cSIvan Khoronzhuk {
23539ed4050cSIvan Khoronzhuk 	struct bpf_prog *prog = bpf->prog;
23549ed4050cSIvan Khoronzhuk 
23559ed4050cSIvan Khoronzhuk 	if (!priv->xdpi.prog && !prog)
23569ed4050cSIvan Khoronzhuk 		return 0;
23579ed4050cSIvan Khoronzhuk 
23589ed4050cSIvan Khoronzhuk 	if (!xdp_attachment_flags_ok(&priv->xdpi, bpf))
23599ed4050cSIvan Khoronzhuk 		return -EBUSY;
23609ed4050cSIvan Khoronzhuk 
23619ed4050cSIvan Khoronzhuk 	WRITE_ONCE(priv->xdp_prog, prog);
23629ed4050cSIvan Khoronzhuk 
23639ed4050cSIvan Khoronzhuk 	xdp_attachment_setup(&priv->xdpi, bpf);
23649ed4050cSIvan Khoronzhuk 
23659ed4050cSIvan Khoronzhuk 	return 0;
23669ed4050cSIvan Khoronzhuk }
23679ed4050cSIvan Khoronzhuk 
23689ed4050cSIvan Khoronzhuk static int cpsw_ndo_bpf(struct net_device *ndev, struct netdev_bpf *bpf)
23699ed4050cSIvan Khoronzhuk {
23709ed4050cSIvan Khoronzhuk 	struct cpsw_priv *priv = netdev_priv(ndev);
23719ed4050cSIvan Khoronzhuk 
23729ed4050cSIvan Khoronzhuk 	switch (bpf->command) {
23739ed4050cSIvan Khoronzhuk 	case XDP_SETUP_PROG:
23749ed4050cSIvan Khoronzhuk 		return cpsw_xdp_prog_setup(priv, bpf);
23759ed4050cSIvan Khoronzhuk 
23769ed4050cSIvan Khoronzhuk 	case XDP_QUERY_PROG:
23779ed4050cSIvan Khoronzhuk 		return xdp_attachment_query(&priv->xdpi, bpf);
23789ed4050cSIvan Khoronzhuk 
23799ed4050cSIvan Khoronzhuk 	default:
23809ed4050cSIvan Khoronzhuk 		return -EINVAL;
23819ed4050cSIvan Khoronzhuk 	}
23829ed4050cSIvan Khoronzhuk }
23839ed4050cSIvan Khoronzhuk 
23849ed4050cSIvan Khoronzhuk static int cpsw_ndo_xdp_xmit(struct net_device *ndev, int n,
23859ed4050cSIvan Khoronzhuk 			     struct xdp_frame **frames, u32 flags)
23869ed4050cSIvan Khoronzhuk {
23879ed4050cSIvan Khoronzhuk 	struct cpsw_priv *priv = netdev_priv(ndev);
23889ed4050cSIvan Khoronzhuk 	struct xdp_frame *xdpf;
23899ed4050cSIvan Khoronzhuk 	int i, drops = 0;
23909ed4050cSIvan Khoronzhuk 
23919ed4050cSIvan Khoronzhuk 	if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
23929ed4050cSIvan Khoronzhuk 		return -EINVAL;
23939ed4050cSIvan Khoronzhuk 
23949ed4050cSIvan Khoronzhuk 	for (i = 0; i < n; i++) {
23959ed4050cSIvan Khoronzhuk 		xdpf = frames[i];
23969ed4050cSIvan Khoronzhuk 		if (xdpf->len < CPSW_MIN_PACKET_SIZE) {
23979ed4050cSIvan Khoronzhuk 			xdp_return_frame_rx_napi(xdpf);
23989ed4050cSIvan Khoronzhuk 			drops++;
23999ed4050cSIvan Khoronzhuk 			continue;
24009ed4050cSIvan Khoronzhuk 		}
24019ed4050cSIvan Khoronzhuk 
24029ed4050cSIvan Khoronzhuk 		if (cpsw_xdp_tx_frame(priv, xdpf, NULL))
24039ed4050cSIvan Khoronzhuk 			drops++;
24049ed4050cSIvan Khoronzhuk 	}
24059ed4050cSIvan Khoronzhuk 
24069ed4050cSIvan Khoronzhuk 	return n - drops;
24079ed4050cSIvan Khoronzhuk }
24089ed4050cSIvan Khoronzhuk 
2409026cc9c3SDavid S. Miller #ifdef CONFIG_NET_POLL_CONTROLLER
2410026cc9c3SDavid S. Miller static void cpsw_ndo_poll_controller(struct net_device *ndev)
2411026cc9c3SDavid S. Miller {
2412026cc9c3SDavid S. Miller 	struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
2413026cc9c3SDavid S. Miller 
2414026cc9c3SDavid S. Miller 	cpsw_intr_disable(cpsw);
2415026cc9c3SDavid S. Miller 	cpsw_rx_interrupt(cpsw->irqs_table[0], cpsw);
2416026cc9c3SDavid S. Miller 	cpsw_tx_interrupt(cpsw->irqs_table[1], cpsw);
2417026cc9c3SDavid S. Miller 	cpsw_intr_enable(cpsw);
2418026cc9c3SDavid S. Miller }
2419026cc9c3SDavid S. Miller #endif
2420026cc9c3SDavid S. Miller 
2421df828598SMugunthan V N static const struct net_device_ops cpsw_netdev_ops = {
2422df828598SMugunthan V N 	.ndo_open		= cpsw_ndo_open,
2423df828598SMugunthan V N 	.ndo_stop		= cpsw_ndo_stop,
2424df828598SMugunthan V N 	.ndo_start_xmit		= cpsw_ndo_start_xmit,
2425dcfd8d58SMugunthan V N 	.ndo_set_mac_address	= cpsw_ndo_set_mac_address,
24262e5b38abSRichard Cochran 	.ndo_do_ioctl		= cpsw_ndo_ioctl,
2427df828598SMugunthan V N 	.ndo_validate_addr	= eth_validate_addr,
2428df828598SMugunthan V N 	.ndo_tx_timeout		= cpsw_ndo_tx_timeout,
24295c50a856SMugunthan V N 	.ndo_set_rx_mode	= cpsw_ndo_set_rx_mode,
243083fcad0cSIvan Khoronzhuk 	.ndo_set_tx_maxrate	= cpsw_ndo_set_tx_maxrate,
2431df828598SMugunthan V N #ifdef CONFIG_NET_POLL_CONTROLLER
2432df828598SMugunthan V N 	.ndo_poll_controller	= cpsw_ndo_poll_controller,
2433df828598SMugunthan V N #endif
24343b72c2feSMugunthan V N 	.ndo_vlan_rx_add_vid	= cpsw_ndo_vlan_rx_add_vid,
24353b72c2feSMugunthan V N 	.ndo_vlan_rx_kill_vid	= cpsw_ndo_vlan_rx_kill_vid,
24367929a668SIvan Khoronzhuk 	.ndo_setup_tc           = cpsw_ndo_setup_tc,
24379ed4050cSIvan Khoronzhuk 	.ndo_bpf		= cpsw_ndo_bpf,
24389ed4050cSIvan Khoronzhuk 	.ndo_xdp_xmit		= cpsw_ndo_xdp_xmit,
2439df828598SMugunthan V N };
2440df828598SMugunthan V N 
2441df828598SMugunthan V N static void cpsw_get_drvinfo(struct net_device *ndev,
2442df828598SMugunthan V N 			     struct ethtool_drvinfo *info)
2443df828598SMugunthan V N {
2444649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
244556e31bd8SIvan Khoronzhuk 	struct platform_device	*pdev = to_platform_device(cpsw->dev);
24467826d43fSJiri Pirko 
244752c4f0ecSMugunthan V N 	strlcpy(info->driver, "cpsw", sizeof(info->driver));
24487826d43fSJiri Pirko 	strlcpy(info->version, "1.0", sizeof(info->version));
244956e31bd8SIvan Khoronzhuk 	strlcpy(info->bus_info, pdev->name, sizeof(info->bus_info));
2450df828598SMugunthan V N }
2451df828598SMugunthan V N 
24521923d6e4SMugunthan V N static int cpsw_set_pauseparam(struct net_device *ndev,
24531923d6e4SMugunthan V N 			       struct ethtool_pauseparam *pause)
24541923d6e4SMugunthan V N {
24551923d6e4SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
24561923d6e4SMugunthan V N 	bool link;
24571923d6e4SMugunthan V N 
24581923d6e4SMugunthan V N 	priv->rx_pause = pause->rx_pause ? true : false;
24591923d6e4SMugunthan V N 	priv->tx_pause = pause->tx_pause ? true : false;
24601923d6e4SMugunthan V N 
24611923d6e4SMugunthan V N 	for_each_slave(priv, _cpsw_adjust_link, priv, &link);
24621923d6e4SMugunthan V N 	return 0;
24631923d6e4SMugunthan V N }
24641923d6e4SMugunthan V N 
2465022d7ad7SIvan Khoronzhuk static int cpsw_set_channels(struct net_device *ndev,
2466022d7ad7SIvan Khoronzhuk 			     struct ethtool_channels *chs)
2467022d7ad7SIvan Khoronzhuk {
2468c24eef28SGrygorii Strashko 	return cpsw_set_channels_common(ndev, chs, cpsw_rx_handler);
2469be034fc1SGrygorii Strashko }
2470be034fc1SGrygorii Strashko 
2471df828598SMugunthan V N static const struct ethtool_ops cpsw_ethtool_ops = {
2472df828598SMugunthan V N 	.get_drvinfo	= cpsw_get_drvinfo,
2473df828598SMugunthan V N 	.get_msglevel	= cpsw_get_msglevel,
2474df828598SMugunthan V N 	.set_msglevel	= cpsw_set_msglevel,
2475df828598SMugunthan V N 	.get_link	= ethtool_op_get_link,
24762e5b38abSRichard Cochran 	.get_ts_info	= cpsw_get_ts_info,
2477ff5b8ef2SMugunthan V N 	.get_coalesce	= cpsw_get_coalesce,
2478ff5b8ef2SMugunthan V N 	.set_coalesce	= cpsw_set_coalesce,
2479d9718546SMugunthan V N 	.get_sset_count		= cpsw_get_sset_count,
2480d9718546SMugunthan V N 	.get_strings		= cpsw_get_strings,
2481d9718546SMugunthan V N 	.get_ethtool_stats	= cpsw_get_ethtool_stats,
24821923d6e4SMugunthan V N 	.get_pauseparam		= cpsw_get_pauseparam,
24831923d6e4SMugunthan V N 	.set_pauseparam		= cpsw_set_pauseparam,
2484d8a64420SMatus Ujhelyi 	.get_wol	= cpsw_get_wol,
2485d8a64420SMatus Ujhelyi 	.set_wol	= cpsw_set_wol,
248652c4f0ecSMugunthan V N 	.get_regs_len	= cpsw_get_regs_len,
248752c4f0ecSMugunthan V N 	.get_regs	= cpsw_get_regs,
24887898b1daSGrygorii Strashko 	.begin		= cpsw_ethtool_op_begin,
24897898b1daSGrygorii Strashko 	.complete	= cpsw_ethtool_op_complete,
2490ce52c744SIvan Khoronzhuk 	.get_channels	= cpsw_get_channels,
2491ce52c744SIvan Khoronzhuk 	.set_channels	= cpsw_set_channels,
24922479876dSPhilippe Reynes 	.get_link_ksettings	= cpsw_get_link_ksettings,
24932479876dSPhilippe Reynes 	.set_link_ksettings	= cpsw_set_link_ksettings,
2494a0909949SYegor Yefremov 	.get_eee	= cpsw_get_eee,
2495a0909949SYegor Yefremov 	.set_eee	= cpsw_set_eee,
24966bb10c2bSYegor Yefremov 	.nway_reset	= cpsw_nway_reset,
2497be034fc1SGrygorii Strashko 	.get_ringparam = cpsw_get_ringparam,
2498be034fc1SGrygorii Strashko 	.set_ringparam = cpsw_set_ringparam,
2499df828598SMugunthan V N };
2500df828598SMugunthan V N 
2501552165bcSDavid Rivshin static int cpsw_probe_dt(struct cpsw_platform_data *data,
25022eb32b0aSMugunthan V N 			 struct platform_device *pdev)
25032eb32b0aSMugunthan V N {
25042eb32b0aSMugunthan V N 	struct device_node *node = pdev->dev.of_node;
25052eb32b0aSMugunthan V N 	struct device_node *slave_node;
25062eb32b0aSMugunthan V N 	int i = 0, ret;
25072eb32b0aSMugunthan V N 	u32 prop;
25082eb32b0aSMugunthan V N 
25092eb32b0aSMugunthan V N 	if (!node)
25102eb32b0aSMugunthan V N 		return -EINVAL;
25112eb32b0aSMugunthan V N 
25122eb32b0aSMugunthan V N 	if (of_property_read_u32(node, "slaves", &prop)) {
251388c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing slaves property in the DT.\n");
25142eb32b0aSMugunthan V N 		return -EINVAL;
25152eb32b0aSMugunthan V N 	}
25162eb32b0aSMugunthan V N 	data->slaves = prop;
25172eb32b0aSMugunthan V N 
2518e86ac13bSMugunthan V N 	if (of_property_read_u32(node, "active_slave", &prop)) {
251988c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing active_slave property in the DT.\n");
2520aa1a15e2SDaniel Mack 		return -EINVAL;
252178ca0b28SRichard Cochran 	}
2522e86ac13bSMugunthan V N 	data->active_slave = prop;
252378ca0b28SRichard Cochran 
2524a86854d0SKees Cook 	data->slave_data = devm_kcalloc(&pdev->dev,
2525a86854d0SKees Cook 					data->slaves,
2526a86854d0SKees Cook 					sizeof(struct cpsw_slave_data),
2527b2adaca9SJoe Perches 					GFP_KERNEL);
2528b2adaca9SJoe Perches 	if (!data->slave_data)
2529aa1a15e2SDaniel Mack 		return -ENOMEM;
25302eb32b0aSMugunthan V N 
25312eb32b0aSMugunthan V N 	if (of_property_read_u32(node, "cpdma_channels", &prop)) {
253288c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing cpdma_channels property in the DT.\n");
2533aa1a15e2SDaniel Mack 		return -EINVAL;
25342eb32b0aSMugunthan V N 	}
25352eb32b0aSMugunthan V N 	data->channels = prop;
25362eb32b0aSMugunthan V N 
25372eb32b0aSMugunthan V N 	if (of_property_read_u32(node, "ale_entries", &prop)) {
253888c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing ale_entries property in the DT.\n");
2539aa1a15e2SDaniel Mack 		return -EINVAL;
25402eb32b0aSMugunthan V N 	}
25412eb32b0aSMugunthan V N 	data->ale_entries = prop;
25422eb32b0aSMugunthan V N 
25432eb32b0aSMugunthan V N 	if (of_property_read_u32(node, "bd_ram_size", &prop)) {
254488c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing bd_ram_size property in the DT.\n");
2545aa1a15e2SDaniel Mack 		return -EINVAL;
25462eb32b0aSMugunthan V N 	}
25472eb32b0aSMugunthan V N 	data->bd_ram_size = prop;
25482eb32b0aSMugunthan V N 
25492eb32b0aSMugunthan V N 	if (of_property_read_u32(node, "mac_control", &prop)) {
255088c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing mac_control property in the DT.\n");
2551aa1a15e2SDaniel Mack 		return -EINVAL;
25522eb32b0aSMugunthan V N 	}
25532eb32b0aSMugunthan V N 	data->mac_control = prop;
25542eb32b0aSMugunthan V N 
2555281abd96SMarkus Pargmann 	if (of_property_read_bool(node, "dual_emac"))
2556281abd96SMarkus Pargmann 		data->dual_emac = 1;
2557d9ba8f9eSMugunthan V N 
25581fb19aa7SVaibhav Hiremath 	/*
25591fb19aa7SVaibhav Hiremath 	 * Populate all the child nodes here...
25601fb19aa7SVaibhav Hiremath 	 */
25611fb19aa7SVaibhav Hiremath 	ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
25621fb19aa7SVaibhav Hiremath 	/* We do not want to force this, as in some cases may not have child */
25631fb19aa7SVaibhav Hiremath 	if (ret)
256488c99ff6SGeorge Cherian 		dev_warn(&pdev->dev, "Doesn't have any child node\n");
25651fb19aa7SVaibhav Hiremath 
25668658aaf2SBen Hutchings 	for_each_available_child_of_node(node, slave_node) {
2567549985eeSRichard Cochran 		struct cpsw_slave_data *slave_data = data->slave_data + i;
2568549985eeSRichard Cochran 		const void *mac_addr = NULL;
2569549985eeSRichard Cochran 		int lenp;
2570549985eeSRichard Cochran 		const __be32 *parp;
2571549985eeSRichard Cochran 
2572f468b10eSMarkus Pargmann 		/* This is no slave child node, continue */
2573bf5849f1SRob Herring 		if (!of_node_name_eq(slave_node, "slave"))
2574f468b10eSMarkus Pargmann 			continue;
2575f468b10eSMarkus Pargmann 
25763ff18849SGrygorii Strashko 		slave_data->ifphy = devm_of_phy_get(&pdev->dev, slave_node,
25773ff18849SGrygorii Strashko 						    NULL);
25783ff18849SGrygorii Strashko 		if (!IS_ENABLED(CONFIG_TI_CPSW_PHY_SEL) &&
25793ff18849SGrygorii Strashko 		    IS_ERR(slave_data->ifphy)) {
25803ff18849SGrygorii Strashko 			ret = PTR_ERR(slave_data->ifphy);
25813ff18849SGrygorii Strashko 			dev_err(&pdev->dev,
25823ff18849SGrygorii Strashko 				"%d: Error retrieving port phy: %d\n", i, ret);
25833cd6e20fSNishka Dasgupta 			goto err_node_put;
25843ff18849SGrygorii Strashko 		}
25853ff18849SGrygorii Strashko 
2586337d1727SMarek Vasut 		slave_data->slave_node = slave_node;
2587552165bcSDavid Rivshin 		slave_data->phy_node = of_parse_phandle(slave_node,
2588552165bcSDavid Rivshin 							"phy-handle", 0);
2589f1eea5c1SDavid Rivshin 		parp = of_get_property(slave_node, "phy_id", &lenp);
2590ae092b5bSDavid Rivshin 		if (slave_data->phy_node) {
2591ae092b5bSDavid Rivshin 			dev_dbg(&pdev->dev,
2592f7ce9103SRob Herring 				"slave[%d] using phy-handle=\"%pOF\"\n",
2593f7ce9103SRob Herring 				i, slave_data->phy_node);
2594ae092b5bSDavid Rivshin 		} else if (of_phy_is_fixed_link(slave_node)) {
2595dfc0a6d3SDavid Rivshin 			/* In the case of a fixed PHY, the DT node associated
2596dfc0a6d3SDavid Rivshin 			 * to the PHY is the Ethernet MAC DT node.
2597dfc0a6d3SDavid Rivshin 			 */
25981f71e8c9SMarkus Brunner 			ret = of_phy_register_fixed_link(slave_node);
259923a09873SJohan Hovold 			if (ret) {
260023a09873SJohan Hovold 				if (ret != -EPROBE_DEFER)
260123a09873SJohan Hovold 					dev_err(&pdev->dev, "failed to register fixed-link phy: %d\n", ret);
26023cd6e20fSNishka Dasgupta 				goto err_node_put;
260323a09873SJohan Hovold 			}
260406cd6d6eSDavid Rivshin 			slave_data->phy_node = of_node_get(slave_node);
2605f1eea5c1SDavid Rivshin 		} else if (parp) {
2606f1eea5c1SDavid Rivshin 			u32 phyid;
2607f1eea5c1SDavid Rivshin 			struct device_node *mdio_node;
2608f1eea5c1SDavid Rivshin 			struct platform_device *mdio;
2609f1eea5c1SDavid Rivshin 
2610f1eea5c1SDavid Rivshin 			if (lenp != (sizeof(__be32) * 2)) {
2611f1eea5c1SDavid Rivshin 				dev_err(&pdev->dev, "Invalid slave[%d] phy_id property\n", i);
261247276fccSMugunthan V N 				goto no_phy_slave;
2613549985eeSRichard Cochran 			}
2614549985eeSRichard Cochran 			mdio_node = of_find_node_by_phandle(be32_to_cpup(parp));
2615549985eeSRichard Cochran 			phyid = be32_to_cpup(parp+1);
2616549985eeSRichard Cochran 			mdio = of_find_device_by_node(mdio_node);
261760e71ab5SJohan Hovold 			of_node_put(mdio_node);
26186954cc1fSJohan Hovold 			if (!mdio) {
261956fdb2e0SMarkus Pargmann 				dev_err(&pdev->dev, "Missing mdio platform device\n");
26203cd6e20fSNishka Dasgupta 				ret = -EINVAL;
26213cd6e20fSNishka Dasgupta 				goto err_node_put;
26226954cc1fSJohan Hovold 			}
2623549985eeSRichard Cochran 			snprintf(slave_data->phy_id, sizeof(slave_data->phy_id),
2624549985eeSRichard Cochran 				 PHY_ID_FMT, mdio->name, phyid);
262586e1d5adSJohan Hovold 			put_device(&mdio->dev);
2626f1eea5c1SDavid Rivshin 		} else {
2627ae092b5bSDavid Rivshin 			dev_err(&pdev->dev,
2628ae092b5bSDavid Rivshin 				"No slave[%d] phy_id, phy-handle, or fixed-link property\n",
2629ae092b5bSDavid Rivshin 				i);
2630f1eea5c1SDavid Rivshin 			goto no_phy_slave;
2631f1eea5c1SDavid Rivshin 		}
26320c65b2b9SAndrew Lunn 		ret = of_get_phy_mode(slave_node, &slave_data->phy_if);
26330c65b2b9SAndrew Lunn 		if (ret) {
263447276fccSMugunthan V N 			dev_err(&pdev->dev, "Missing or malformed slave[%d] phy-mode property\n",
263547276fccSMugunthan V N 				i);
26363cd6e20fSNishka Dasgupta 			goto err_node_put;
263747276fccSMugunthan V N 		}
263847276fccSMugunthan V N 
263947276fccSMugunthan V N no_phy_slave:
2640549985eeSRichard Cochran 		mac_addr = of_get_mac_address(slave_node);
2641a51645f7SPetr Štetiar 		if (!IS_ERR(mac_addr)) {
26422d2924afSPetr Štetiar 			ether_addr_copy(slave_data->mac_addr, mac_addr);
26430ba517b1SMarkus Pargmann 		} else {
2644b6745f6eSMugunthan V N 			ret = ti_cm_get_macid(&pdev->dev, i,
26450ba517b1SMarkus Pargmann 					      slave_data->mac_addr);
26460ba517b1SMarkus Pargmann 			if (ret)
26473cd6e20fSNishka Dasgupta 				goto err_node_put;
26480ba517b1SMarkus Pargmann 		}
2649d9ba8f9eSMugunthan V N 		if (data->dual_emac) {
265091c4166cSMugunthan V N 			if (of_property_read_u32(slave_node, "dual_emac_res_vlan",
2651d9ba8f9eSMugunthan V N 						 &prop)) {
265288c99ff6SGeorge Cherian 				dev_err(&pdev->dev, "Missing dual_emac_res_vlan in DT.\n");
2653d9ba8f9eSMugunthan V N 				slave_data->dual_emac_res_vlan = i+1;
265488c99ff6SGeorge Cherian 				dev_err(&pdev->dev, "Using %d as Reserved VLAN for %d slave\n",
2655d9ba8f9eSMugunthan V N 					slave_data->dual_emac_res_vlan, i);
2656d9ba8f9eSMugunthan V N 			} else {
2657d9ba8f9eSMugunthan V N 				slave_data->dual_emac_res_vlan = prop;
2658d9ba8f9eSMugunthan V N 			}
2659d9ba8f9eSMugunthan V N 		}
2660d9ba8f9eSMugunthan V N 
2661549985eeSRichard Cochran 		i++;
26623cd6e20fSNishka Dasgupta 		if (i == data->slaves) {
26633cd6e20fSNishka Dasgupta 			ret = 0;
26643cd6e20fSNishka Dasgupta 			goto err_node_put;
26653cd6e20fSNishka Dasgupta 		}
2666549985eeSRichard Cochran 	}
2667549985eeSRichard Cochran 
26682eb32b0aSMugunthan V N 	return 0;
26693cd6e20fSNishka Dasgupta 
26703cd6e20fSNishka Dasgupta err_node_put:
26713cd6e20fSNishka Dasgupta 	of_node_put(slave_node);
26723cd6e20fSNishka Dasgupta 	return ret;
26732eb32b0aSMugunthan V N }
26742eb32b0aSMugunthan V N 
2675a4e32b0dSJohan Hovold static void cpsw_remove_dt(struct platform_device *pdev)
2676a4e32b0dSJohan Hovold {
2677bfe59032SIvan Khoronzhuk 	struct cpsw_common *cpsw = platform_get_drvdata(pdev);
26788cbcc466SJohan Hovold 	struct cpsw_platform_data *data = &cpsw->data;
26798cbcc466SJohan Hovold 	struct device_node *node = pdev->dev.of_node;
26808cbcc466SJohan Hovold 	struct device_node *slave_node;
26818cbcc466SJohan Hovold 	int i = 0;
26828cbcc466SJohan Hovold 
26838cbcc466SJohan Hovold 	for_each_available_child_of_node(node, slave_node) {
26848cbcc466SJohan Hovold 		struct cpsw_slave_data *slave_data = &data->slave_data[i];
26858cbcc466SJohan Hovold 
2686bf5849f1SRob Herring 		if (!of_node_name_eq(slave_node, "slave"))
26878cbcc466SJohan Hovold 			continue;
26888cbcc466SJohan Hovold 
26893f65047cSJohan Hovold 		if (of_phy_is_fixed_link(slave_node))
26903f65047cSJohan Hovold 			of_phy_deregister_fixed_link(slave_node);
26918cbcc466SJohan Hovold 
26928cbcc466SJohan Hovold 		of_node_put(slave_data->phy_node);
26938cbcc466SJohan Hovold 
26948cbcc466SJohan Hovold 		i++;
26953cd6e20fSNishka Dasgupta 		if (i == data->slaves) {
26963cd6e20fSNishka Dasgupta 			of_node_put(slave_node);
26978cbcc466SJohan Hovold 			break;
26988cbcc466SJohan Hovold 		}
26993cd6e20fSNishka Dasgupta 	}
27008cbcc466SJohan Hovold 
2701a4e32b0dSJohan Hovold 	of_platform_depopulate(&pdev->dev);
2702a4e32b0dSJohan Hovold }
2703a4e32b0dSJohan Hovold 
270456e31bd8SIvan Khoronzhuk static int cpsw_probe_dual_emac(struct cpsw_priv *priv)
2705d9ba8f9eSMugunthan V N {
2706606f3993SIvan Khoronzhuk 	struct cpsw_common		*cpsw = priv->cpsw;
2707606f3993SIvan Khoronzhuk 	struct cpsw_platform_data	*data = &cpsw->data;
2708d9ba8f9eSMugunthan V N 	struct net_device		*ndev;
2709d9ba8f9eSMugunthan V N 	struct cpsw_priv		*priv_sl2;
2710e38b5a3dSIvan Khoronzhuk 	int ret = 0;
2711d9ba8f9eSMugunthan V N 
2712d183a942SGrygorii Strashko 	ndev = devm_alloc_etherdev_mqs(cpsw->dev, sizeof(struct cpsw_priv),
2713d183a942SGrygorii Strashko 				       CPSW_MAX_QUEUES, CPSW_MAX_QUEUES);
2714d9ba8f9eSMugunthan V N 	if (!ndev) {
271556e31bd8SIvan Khoronzhuk 		dev_err(cpsw->dev, "cpsw: error allocating net_device\n");
2716d9ba8f9eSMugunthan V N 		return -ENOMEM;
2717d9ba8f9eSMugunthan V N 	}
2718d9ba8f9eSMugunthan V N 
2719d9ba8f9eSMugunthan V N 	priv_sl2 = netdev_priv(ndev);
2720606f3993SIvan Khoronzhuk 	priv_sl2->cpsw = cpsw;
2721d9ba8f9eSMugunthan V N 	priv_sl2->ndev = ndev;
2722d9ba8f9eSMugunthan V N 	priv_sl2->dev  = &ndev->dev;
2723d9ba8f9eSMugunthan V N 	priv_sl2->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
2724d9ba8f9eSMugunthan V N 
2725d9ba8f9eSMugunthan V N 	if (is_valid_ether_addr(data->slave_data[1].mac_addr)) {
2726d9ba8f9eSMugunthan V N 		memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr,
2727d9ba8f9eSMugunthan V N 			ETH_ALEN);
272856e31bd8SIvan Khoronzhuk 		dev_info(cpsw->dev, "cpsw: Detected MACID = %pM\n",
272956e31bd8SIvan Khoronzhuk 			 priv_sl2->mac_addr);
2730d9ba8f9eSMugunthan V N 	} else {
27316c1f0a1fSJoe Perches 		eth_random_addr(priv_sl2->mac_addr);
273256e31bd8SIvan Khoronzhuk 		dev_info(cpsw->dev, "cpsw: Random MACID = %pM\n",
273356e31bd8SIvan Khoronzhuk 			 priv_sl2->mac_addr);
2734d9ba8f9eSMugunthan V N 	}
2735d9ba8f9eSMugunthan V N 	memcpy(ndev->dev_addr, priv_sl2->mac_addr, ETH_ALEN);
2736d9ba8f9eSMugunthan V N 
2737d9ba8f9eSMugunthan V N 	priv_sl2->emac_port = 1;
2738606f3993SIvan Khoronzhuk 	cpsw->slaves[1].ndev = ndev;
2739193736c8SIvan Khoronzhuk 	ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX;
2740d9ba8f9eSMugunthan V N 
2741d9ba8f9eSMugunthan V N 	ndev->netdev_ops = &cpsw_netdev_ops;
27427ad24ea4SWilfried Klaebe 	ndev->ethtool_ops = &cpsw_ethtool_ops;
2743d9ba8f9eSMugunthan V N 
2744d9ba8f9eSMugunthan V N 	/* register the network device */
274556e31bd8SIvan Khoronzhuk 	SET_NETDEV_DEV(ndev, cpsw->dev);
2746337d1727SMarek Vasut 	ndev->dev.of_node = cpsw->slaves[1].data->slave_node;
2747d9ba8f9eSMugunthan V N 	ret = register_netdev(ndev);
2748d183a942SGrygorii Strashko 	if (ret)
274956e31bd8SIvan Khoronzhuk 		dev_err(cpsw->dev, "cpsw: error registering net device\n");
2750d9ba8f9eSMugunthan V N 
2751d9ba8f9eSMugunthan V N 	return ret;
2752d9ba8f9eSMugunthan V N }
2753d9ba8f9eSMugunthan V N 
27547da11600SMugunthan V N static const struct of_device_id cpsw_of_mtable[] = {
27559611d6d6SIvan Khoronzhuk 	{ .compatible = "ti,cpsw"},
27569611d6d6SIvan Khoronzhuk 	{ .compatible = "ti,am335x-cpsw"},
27579611d6d6SIvan Khoronzhuk 	{ .compatible = "ti,am4372-cpsw"},
27589611d6d6SIvan Khoronzhuk 	{ .compatible = "ti,dra7-cpsw"},
27597da11600SMugunthan V N 	{ /* sentinel */ },
27607da11600SMugunthan V N };
27617da11600SMugunthan V N MODULE_DEVICE_TABLE(of, cpsw_of_mtable);
27627da11600SMugunthan V N 
27639611d6d6SIvan Khoronzhuk static const struct soc_device_attribute cpsw_soc_devices[] = {
27649611d6d6SIvan Khoronzhuk 	{ .family = "AM33xx", .revision = "ES1.0"},
27659611d6d6SIvan Khoronzhuk 	{ /* sentinel */ }
27669611d6d6SIvan Khoronzhuk };
27679611d6d6SIvan Khoronzhuk 
2768663e12e6SBill Pemberton static int cpsw_probe(struct platform_device *pdev)
2769df828598SMugunthan V N {
2770c8fb5668SGrygorii Strashko 	struct device			*dev = &pdev->dev;
2771ef4183a1SIvan Khoronzhuk 	struct clk			*clk;
2772d1bd9acfSSebastian Siewior 	struct cpsw_platform_data	*data;
2773df828598SMugunthan V N 	struct net_device		*ndev;
2774df828598SMugunthan V N 	struct cpsw_priv		*priv;
2775aa1a15e2SDaniel Mack 	void __iomem			*ss_regs;
2776c8ace62fSYueHaibing 	struct resource			*ss_res;
27771d147ccbSMugunthan V N 	struct gpio_descs		*mode;
27789611d6d6SIvan Khoronzhuk 	const struct soc_device_attribute *soc;
2779649a1688SIvan Khoronzhuk 	struct cpsw_common		*cpsw;
2780e6a84624SGrygorii Strashko 	int ret = 0, ch;
27815087b915SFelipe Balbi 	int irq;
2782df828598SMugunthan V N 
2783c8fb5668SGrygorii Strashko 	cpsw = devm_kzalloc(dev, sizeof(struct cpsw_common), GFP_KERNEL);
27843420ea88SJohan Hovold 	if (!cpsw)
27853420ea88SJohan Hovold 		return -ENOMEM;
27863420ea88SJohan Hovold 
27872d683eaaSAntoine Tenart 	platform_set_drvdata(pdev, cpsw);
278851a95337SGrygorii Strashko 	cpsw_slave_index = cpsw_slave_index_priv;
278951a95337SGrygorii Strashko 
2790c8fb5668SGrygorii Strashko 	cpsw->dev = dev;
2791649a1688SIvan Khoronzhuk 
2792c8fb5668SGrygorii Strashko 	mode = devm_gpiod_get_array_optional(dev, "mode", GPIOD_OUT_LOW);
27931d147ccbSMugunthan V N 	if (IS_ERR(mode)) {
27941d147ccbSMugunthan V N 		ret = PTR_ERR(mode);
2795c8fb5668SGrygorii Strashko 		dev_err(dev, "gpio request failed, ret %d\n", ret);
2796d183a942SGrygorii Strashko 		return ret;
27971d147ccbSMugunthan V N 	}
27981d147ccbSMugunthan V N 
279983a8471bSGrygorii Strashko 	clk = devm_clk_get(dev, "fck");
280083a8471bSGrygorii Strashko 	if (IS_ERR(clk)) {
2801ac97a359SYueHaibing 		ret = PTR_ERR(clk);
280283a8471bSGrygorii Strashko 		dev_err(dev, "fck is not found %d\n", ret);
280383a8471bSGrygorii Strashko 		return ret;
280483a8471bSGrygorii Strashko 	}
280583a8471bSGrygorii Strashko 	cpsw->bus_freq_mhz = clk_get_rate(clk) / 1000000;
280683a8471bSGrygorii Strashko 
280783a8471bSGrygorii Strashko 	ss_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
280883a8471bSGrygorii Strashko 	ss_regs = devm_ioremap_resource(dev, ss_res);
280983a8471bSGrygorii Strashko 	if (IS_ERR(ss_regs))
281083a8471bSGrygorii Strashko 		return PTR_ERR(ss_regs);
281183a8471bSGrygorii Strashko 	cpsw->regs = ss_regs;
281283a8471bSGrygorii Strashko 
2813c8ace62fSYueHaibing 	cpsw->wr_regs = devm_platform_ioremap_resource(pdev, 1);
281483a8471bSGrygorii Strashko 	if (IS_ERR(cpsw->wr_regs))
281583a8471bSGrygorii Strashko 		return PTR_ERR(cpsw->wr_regs);
281683a8471bSGrygorii Strashko 
281783a8471bSGrygorii Strashko 	/* RX IRQ */
281883a8471bSGrygorii Strashko 	irq = platform_get_irq(pdev, 1);
281983a8471bSGrygorii Strashko 	if (irq < 0)
282083a8471bSGrygorii Strashko 		return irq;
282183a8471bSGrygorii Strashko 	cpsw->irqs_table[0] = irq;
282283a8471bSGrygorii Strashko 
282383a8471bSGrygorii Strashko 	/* TX IRQ */
282483a8471bSGrygorii Strashko 	irq = platform_get_irq(pdev, 2);
282583a8471bSGrygorii Strashko 	if (irq < 0)
282683a8471bSGrygorii Strashko 		return irq;
282783a8471bSGrygorii Strashko 	cpsw->irqs_table[1] = irq;
282883a8471bSGrygorii Strashko 
28291fb19aa7SVaibhav Hiremath 	/*
28301fb19aa7SVaibhav Hiremath 	 * This may be required here for child devices.
28311fb19aa7SVaibhav Hiremath 	 */
2832c8fb5668SGrygorii Strashko 	pm_runtime_enable(dev);
28331fb19aa7SVaibhav Hiremath 
2834a4e32b0dSJohan Hovold 	/* Need to enable clocks with runtime PM api to access module
2835a4e32b0dSJohan Hovold 	 * registers
2836a4e32b0dSJohan Hovold 	 */
2837c8fb5668SGrygorii Strashko 	ret = pm_runtime_get_sync(dev);
2838a4e32b0dSJohan Hovold 	if (ret < 0) {
2839c8fb5668SGrygorii Strashko 		pm_runtime_put_noidle(dev);
2840aa1a15e2SDaniel Mack 		goto clean_runtime_disable_ret;
28412eb32b0aSMugunthan V N 	}
2842a4e32b0dSJohan Hovold 
284323a09873SJohan Hovold 	ret = cpsw_probe_dt(&cpsw->data, pdev);
284423a09873SJohan Hovold 	if (ret)
2845a4e32b0dSJohan Hovold 		goto clean_dt_ret;
284623a09873SJohan Hovold 
284783a8471bSGrygorii Strashko 	soc = soc_device_match(cpsw_soc_devices);
284883a8471bSGrygorii Strashko 	if (soc)
284983a8471bSGrygorii Strashko 		cpsw->quirk_irq = 1;
285083a8471bSGrygorii Strashko 
2851606f3993SIvan Khoronzhuk 	data = &cpsw->data;
2852c8fb5668SGrygorii Strashko 	cpsw->slaves = devm_kcalloc(dev,
2853a86854d0SKees Cook 				    data->slaves, sizeof(struct cpsw_slave),
2854df828598SMugunthan V N 				    GFP_KERNEL);
2855606f3993SIvan Khoronzhuk 	if (!cpsw->slaves) {
2856aa1a15e2SDaniel Mack 		ret = -ENOMEM;
2857a4e32b0dSJohan Hovold 		goto clean_dt_ret;
2858df828598SMugunthan V N 	}
2859df828598SMugunthan V N 
286083a8471bSGrygorii Strashko 	cpsw->rx_packet_max = max(rx_packet_max, CPSW_MAX_PACKET_SIZE);
2861c24eef28SGrygorii Strashko 	cpsw->descs_pool_size = descs_pool_size;
2862df828598SMugunthan V N 
2863e6a84624SGrygorii Strashko 	ret = cpsw_init_common(cpsw, ss_regs, ale_ageout,
2864e6a84624SGrygorii Strashko 			       ss_res->start + CPSW2_BD_OFFSET,
2865e6a84624SGrygorii Strashko 			       descs_pool_size);
2866e6a84624SGrygorii Strashko 	if (ret)
2867a4e32b0dSJohan Hovold 		goto clean_dt_ret;
28688a2c9a5aSGrygorii Strashko 
286983a8471bSGrygorii Strashko 	ch = cpsw->quirk_irq ? 0 : 7;
287083a8471bSGrygorii Strashko 	cpsw->txv[0].ch = cpdma_chan_create(cpsw->dma, ch, cpsw_tx_handler, 0);
287183a8471bSGrygorii Strashko 	if (IS_ERR(cpsw->txv[0].ch)) {
287283a8471bSGrygorii Strashko 		dev_err(dev, "error initializing tx dma channel\n");
287383a8471bSGrygorii Strashko 		ret = PTR_ERR(cpsw->txv[0].ch);
287483a8471bSGrygorii Strashko 		goto clean_cpts;
2875df828598SMugunthan V N 	}
2876df828598SMugunthan V N 
287783a8471bSGrygorii Strashko 	cpsw->rxv[0].ch = cpdma_chan_create(cpsw->dma, 0, cpsw_rx_handler, 1);
287883a8471bSGrygorii Strashko 	if (IS_ERR(cpsw->rxv[0].ch)) {
287983a8471bSGrygorii Strashko 		dev_err(dev, "error initializing rx dma channel\n");
288083a8471bSGrygorii Strashko 		ret = PTR_ERR(cpsw->rxv[0].ch);
288183a8471bSGrygorii Strashko 		goto clean_cpts;
288283a8471bSGrygorii Strashko 	}
288383a8471bSGrygorii Strashko 	cpsw_split_res(cpsw);
288483a8471bSGrygorii Strashko 
288583a8471bSGrygorii Strashko 	/* setup netdev */
288683a8471bSGrygorii Strashko 	ndev = devm_alloc_etherdev_mqs(dev, sizeof(struct cpsw_priv),
288783a8471bSGrygorii Strashko 				       CPSW_MAX_QUEUES, CPSW_MAX_QUEUES);
288883a8471bSGrygorii Strashko 	if (!ndev) {
288983a8471bSGrygorii Strashko 		dev_err(dev, "error allocating net_device\n");
289083a8471bSGrygorii Strashko 		goto clean_cpts;
289183a8471bSGrygorii Strashko 	}
289283a8471bSGrygorii Strashko 
289383a8471bSGrygorii Strashko 	priv = netdev_priv(ndev);
289483a8471bSGrygorii Strashko 	priv->cpsw = cpsw;
289583a8471bSGrygorii Strashko 	priv->ndev = ndev;
289683a8471bSGrygorii Strashko 	priv->dev  = dev;
289783a8471bSGrygorii Strashko 	priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
289883a8471bSGrygorii Strashko 	priv->emac_port = 0;
289983a8471bSGrygorii Strashko 
290083a8471bSGrygorii Strashko 	if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
290183a8471bSGrygorii Strashko 		memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN);
290283a8471bSGrygorii Strashko 		dev_info(dev, "Detected MACID = %pM\n", priv->mac_addr);
290383a8471bSGrygorii Strashko 	} else {
290483a8471bSGrygorii Strashko 		eth_random_addr(priv->mac_addr);
290583a8471bSGrygorii Strashko 		dev_info(dev, "Random MACID = %pM\n", priv->mac_addr);
290683a8471bSGrygorii Strashko 	}
290783a8471bSGrygorii Strashko 
290883a8471bSGrygorii Strashko 	memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
290983a8471bSGrygorii Strashko 
291083a8471bSGrygorii Strashko 	cpsw->slaves[0].ndev = ndev;
291183a8471bSGrygorii Strashko 
2912a3a41d2fSGrygorii Strashko 	ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX;
2913070f9c65SKeerthy 
2914070f9c65SKeerthy 	ndev->netdev_ops = &cpsw_netdev_ops;
2915070f9c65SKeerthy 	ndev->ethtool_ops = &cpsw_ethtool_ops;
29169611d6d6SIvan Khoronzhuk 	netif_napi_add(ndev, &cpsw->napi_rx,
29179611d6d6SIvan Khoronzhuk 		       cpsw->quirk_irq ? cpsw_rx_poll : cpsw_rx_mq_poll,
29189611d6d6SIvan Khoronzhuk 		       CPSW_POLL_WEIGHT);
29199611d6d6SIvan Khoronzhuk 	netif_tx_napi_add(ndev, &cpsw->napi_tx,
29209611d6d6SIvan Khoronzhuk 			  cpsw->quirk_irq ? cpsw_tx_poll : cpsw_tx_mq_poll,
29219611d6d6SIvan Khoronzhuk 			  CPSW_POLL_WEIGHT);
2922070f9c65SKeerthy 
2923070f9c65SKeerthy 	/* register the network device */
2924c8fb5668SGrygorii Strashko 	SET_NETDEV_DEV(ndev, dev);
2925337d1727SMarek Vasut 	ndev->dev.of_node = cpsw->slaves[0].data->slave_node;
2926070f9c65SKeerthy 	ret = register_netdev(ndev);
2927070f9c65SKeerthy 	if (ret) {
2928c8fb5668SGrygorii Strashko 		dev_err(dev, "error registering net device\n");
2929070f9c65SKeerthy 		ret = -ENODEV;
293083a8471bSGrygorii Strashko 		goto clean_cpts;
2931070f9c65SKeerthy 	}
2932070f9c65SKeerthy 
2933070f9c65SKeerthy 	if (cpsw->data.dual_emac) {
2934070f9c65SKeerthy 		ret = cpsw_probe_dual_emac(priv);
2935070f9c65SKeerthy 		if (ret) {
2936070f9c65SKeerthy 			cpsw_err(priv, probe, "error probe slave 2 emac interface\n");
2937070f9c65SKeerthy 			goto clean_unregister_netdev_ret;
2938070f9c65SKeerthy 		}
2939070f9c65SKeerthy 	}
2940070f9c65SKeerthy 
2941c03abd84SFelipe Balbi 	/* Grab RX and TX IRQs. Note that we also have RX_THRESHOLD and
2942c03abd84SFelipe Balbi 	 * MISC IRQs which are always kept disabled with this driver so
2943c03abd84SFelipe Balbi 	 * we will not request them.
2944c03abd84SFelipe Balbi 	 *
2945c03abd84SFelipe Balbi 	 * If anyone wants to implement support for those, make sure to
2946c03abd84SFelipe Balbi 	 * first request and append them to irqs_table array.
2947c03abd84SFelipe Balbi 	 */
294883a8471bSGrygorii Strashko 	ret = devm_request_irq(dev, cpsw->irqs_table[0], cpsw_rx_interrupt,
2949c8fb5668SGrygorii Strashko 			       0, dev_name(dev), cpsw);
29505087b915SFelipe Balbi 	if (ret < 0) {
2951c8fb5668SGrygorii Strashko 		dev_err(dev, "error attaching irq (%d)\n", ret);
295283a8471bSGrygorii Strashko 		goto clean_unregister_netdev_ret;
2953df828598SMugunthan V N 	}
2954df828598SMugunthan V N 
29555087b915SFelipe Balbi 
295683a8471bSGrygorii Strashko 	ret = devm_request_irq(dev, cpsw->irqs_table[1], cpsw_tx_interrupt,
2957dbc4ec52SIvan Khoronzhuk 			       0, dev_name(&pdev->dev), cpsw);
29585087b915SFelipe Balbi 	if (ret < 0) {
2959c8fb5668SGrygorii Strashko 		dev_err(dev, "error attaching irq (%d)\n", ret);
296083a8471bSGrygorii Strashko 		goto clean_unregister_netdev_ret;
29615087b915SFelipe Balbi 	}
2962c2b32e58SDaniel Mack 
296390225bf0SGrygorii Strashko 	cpsw_notice(priv, probe,
296490225bf0SGrygorii Strashko 		    "initialized device (regs %pa, irq %d, pool size %d)\n",
296583a8471bSGrygorii Strashko 		    &ss_res->start, cpsw->irqs_table[0], descs_pool_size);
2966d9ba8f9eSMugunthan V N 
2967c46ab7e0SJohan Hovold 	pm_runtime_put(&pdev->dev);
2968c46ab7e0SJohan Hovold 
2969df828598SMugunthan V N 	return 0;
2970df828598SMugunthan V N 
2971a7fe9d46SJohan Hovold clean_unregister_netdev_ret:
2972a7fe9d46SJohan Hovold 	unregister_netdev(ndev);
297383a8471bSGrygorii Strashko clean_cpts:
297483a8471bSGrygorii Strashko 	cpts_release(cpsw->cpts);
29752c836bd9SIvan Khoronzhuk 	cpdma_ctlr_destroy(cpsw->dma);
2976a4e32b0dSJohan Hovold clean_dt_ret:
2977a4e32b0dSJohan Hovold 	cpsw_remove_dt(pdev);
2978c46ab7e0SJohan Hovold 	pm_runtime_put_sync(&pdev->dev);
2979aa1a15e2SDaniel Mack clean_runtime_disable_ret:
2980f150bd7fSMugunthan V N 	pm_runtime_disable(&pdev->dev);
2981df828598SMugunthan V N 	return ret;
2982df828598SMugunthan V N }
2983df828598SMugunthan V N 
2984663e12e6SBill Pemberton static int cpsw_remove(struct platform_device *pdev)
2985df828598SMugunthan V N {
2986bfe59032SIvan Khoronzhuk 	struct cpsw_common *cpsw = platform_get_drvdata(pdev);
2987bfe59032SIvan Khoronzhuk 	int i, ret;
29888a0b6dc9SGrygorii Strashko 
29898a0b6dc9SGrygorii Strashko 	ret = pm_runtime_get_sync(&pdev->dev);
29908a0b6dc9SGrygorii Strashko 	if (ret < 0) {
29918a0b6dc9SGrygorii Strashko 		pm_runtime_put_noidle(&pdev->dev);
29928a0b6dc9SGrygorii Strashko 		return ret;
29938a0b6dc9SGrygorii Strashko 	}
2994df828598SMugunthan V N 
2995bfe59032SIvan Khoronzhuk 	for (i = 0; i < cpsw->data.slaves; i++)
2996bfe59032SIvan Khoronzhuk 		if (cpsw->slaves[i].ndev)
2997bfe59032SIvan Khoronzhuk 			unregister_netdev(cpsw->slaves[i].ndev);
2998df828598SMugunthan V N 
29998a2c9a5aSGrygorii Strashko 	cpts_release(cpsw->cpts);
30002c836bd9SIvan Khoronzhuk 	cpdma_ctlr_destroy(cpsw->dma);
3001a4e32b0dSJohan Hovold 	cpsw_remove_dt(pdev);
30028a0b6dc9SGrygorii Strashko 	pm_runtime_put_sync(&pdev->dev);
30038a0b6dc9SGrygorii Strashko 	pm_runtime_disable(&pdev->dev);
3004df828598SMugunthan V N 	return 0;
3005df828598SMugunthan V N }
3006df828598SMugunthan V N 
30078963a504SGrygorii Strashko #ifdef CONFIG_PM_SLEEP
3008df828598SMugunthan V N static int cpsw_suspend(struct device *dev)
3009df828598SMugunthan V N {
30102f9b0d93SKeerthy 	struct cpsw_common *cpsw = dev_get_drvdata(dev);
3011618073e3SMugunthan V N 	int i;
3012618073e3SMugunthan V N 
30132f9b0d93SKeerthy 	for (i = 0; i < cpsw->data.slaves; i++)
30142f9b0d93SKeerthy 		if (cpsw->slaves[i].ndev)
3015606f3993SIvan Khoronzhuk 			if (netif_running(cpsw->slaves[i].ndev))
3016606f3993SIvan Khoronzhuk 				cpsw_ndo_stop(cpsw->slaves[i].ndev);
30171e7a2e21SDaniel Mack 
3018739683b4SMugunthan V N 	/* Select sleep pin state */
301956e31bd8SIvan Khoronzhuk 	pinctrl_pm_select_sleep_state(dev);
3020739683b4SMugunthan V N 
3021df828598SMugunthan V N 	return 0;
3022df828598SMugunthan V N }
3023df828598SMugunthan V N 
3024df828598SMugunthan V N static int cpsw_resume(struct device *dev)
3025df828598SMugunthan V N {
30262f9b0d93SKeerthy 	struct cpsw_common *cpsw = dev_get_drvdata(dev);
30272f9b0d93SKeerthy 	int i;
3028df828598SMugunthan V N 
3029739683b4SMugunthan V N 	/* Select default pin state */
303056e31bd8SIvan Khoronzhuk 	pinctrl_pm_select_default_state(dev);
3031739683b4SMugunthan V N 
30324ccfd638SGrygorii Strashko 	/* shut up ASSERT_RTNL() warning in netif_set_real_num_tx/rx_queues */
30334ccfd638SGrygorii Strashko 	rtnl_lock();
3034618073e3SMugunthan V N 
30352f9b0d93SKeerthy 	for (i = 0; i < cpsw->data.slaves; i++)
30362f9b0d93SKeerthy 		if (cpsw->slaves[i].ndev)
3037606f3993SIvan Khoronzhuk 			if (netif_running(cpsw->slaves[i].ndev))
3038606f3993SIvan Khoronzhuk 				cpsw_ndo_open(cpsw->slaves[i].ndev);
30392f9b0d93SKeerthy 
30404ccfd638SGrygorii Strashko 	rtnl_unlock();
30414ccfd638SGrygorii Strashko 
3042df828598SMugunthan V N 	return 0;
3043df828598SMugunthan V N }
30448963a504SGrygorii Strashko #endif
3045df828598SMugunthan V N 
30468963a504SGrygorii Strashko static SIMPLE_DEV_PM_OPS(cpsw_pm_ops, cpsw_suspend, cpsw_resume);
3047df828598SMugunthan V N 
3048df828598SMugunthan V N static struct platform_driver cpsw_driver = {
3049df828598SMugunthan V N 	.driver = {
3050df828598SMugunthan V N 		.name	 = "cpsw",
3051df828598SMugunthan V N 		.pm	 = &cpsw_pm_ops,
30521e5c76d4SSachin Kamat 		.of_match_table = cpsw_of_mtable,
3053df828598SMugunthan V N 	},
3054df828598SMugunthan V N 	.probe = cpsw_probe,
3055663e12e6SBill Pemberton 	.remove = cpsw_remove,
3056df828598SMugunthan V N };
3057df828598SMugunthan V N 
30586fb3b6b5SGrygorii Strashko module_platform_driver(cpsw_driver);
3059df828598SMugunthan V N 
3060df828598SMugunthan V N MODULE_LICENSE("GPL");
3061df828598SMugunthan V N MODULE_AUTHOR("Cyril Chemparathy <cyril@ti.com>");
3062df828598SMugunthan V N MODULE_AUTHOR("Mugunthan V N <mugunthanvnm@ti.com>");
3063df828598SMugunthan V N MODULE_DESCRIPTION("TI CPSW Ethernet driver");
3064