xref: /openbmc/linux/drivers/net/ethernet/ti/cpsw.c (revision bfe59032)
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>
34df828598SMugunthan V N 
35739683b4SMugunthan V N #include <linux/pinctrl/consumer.h>
367929a668SIvan Khoronzhuk #include <net/pkt_cls.h>
37df828598SMugunthan V N 
38dbe34724SMugunthan V N #include "cpsw.h"
39df828598SMugunthan V N #include "cpsw_ale.h"
40814b4a67SGrygorii Strashko #include "cpsw_priv.h"
41cfc08345SGrygorii Strashko #include "cpsw_sl.h"
422e5b38abSRichard Cochran #include "cpts.h"
43df828598SMugunthan V N #include "davinci_cpdma.h"
44df828598SMugunthan V N 
4557d90148SIvan Khoronzhuk #include <net/pkt_sched.h>
4657d90148SIvan Khoronzhuk 
47df828598SMugunthan V N static int debug_level;
48df828598SMugunthan V N module_param(debug_level, int, 0);
49df828598SMugunthan V N MODULE_PARM_DESC(debug_level, "cpsw debug level (NETIF_MSG bits)");
50df828598SMugunthan V N 
51df828598SMugunthan V N static int ale_ageout = 10;
52df828598SMugunthan V N module_param(ale_ageout, int, 0);
53df828598SMugunthan V N MODULE_PARM_DESC(ale_ageout, "cpsw ale ageout interval (seconds)");
54df828598SMugunthan V N 
55df828598SMugunthan V N static int rx_packet_max = CPSW_MAX_PACKET_SIZE;
56df828598SMugunthan V N module_param(rx_packet_max, int, 0);
57df828598SMugunthan V N MODULE_PARM_DESC(rx_packet_max, "maximum receive packet size (bytes)");
58df828598SMugunthan V N 
5990225bf0SGrygorii Strashko static int descs_pool_size = CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT;
6090225bf0SGrygorii Strashko module_param(descs_pool_size, int, 0444);
6190225bf0SGrygorii Strashko MODULE_PARM_DESC(descs_pool_size, "Number of CPDMA CPPI descriptors in pool");
6290225bf0SGrygorii Strashko 
63df828598SMugunthan V N #define for_each_slave(priv, func, arg...)				\
64df828598SMugunthan V N 	do {								\
656e6ceaedSSebastian Siewior 		struct cpsw_slave *slave;				\
66606f3993SIvan Khoronzhuk 		struct cpsw_common *cpsw = (priv)->cpsw;		\
676e6ceaedSSebastian Siewior 		int n;							\
68606f3993SIvan Khoronzhuk 		if (cpsw->data.dual_emac)				\
69606f3993SIvan Khoronzhuk 			(func)((cpsw)->slaves + priv->emac_port, ##arg);\
70d9ba8f9eSMugunthan V N 		else							\
71606f3993SIvan Khoronzhuk 			for (n = cpsw->data.slaves,			\
72606f3993SIvan Khoronzhuk 					slave = cpsw->slaves;		\
736e6ceaedSSebastian Siewior 					n; n--)				\
746e6ceaedSSebastian Siewior 				(func)(slave++, ##arg);			\
75df828598SMugunthan V N 	} while (0)
76d9ba8f9eSMugunthan V N 
7700fe4712SIvan Khoronzhuk static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev,
7800fe4712SIvan Khoronzhuk 				    __be16 proto, u16 vid);
7900fe4712SIvan Khoronzhuk 
800cd8f9ccSMugunthan V N static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
810cd8f9ccSMugunthan V N {
822a05a622SIvan Khoronzhuk 	struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
832a05a622SIvan Khoronzhuk 	struct cpsw_ale *ale = cpsw->ale;
840cd8f9ccSMugunthan V N 	int i;
850cd8f9ccSMugunthan V N 
86606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
870cd8f9ccSMugunthan V N 		bool flag = false;
880cd8f9ccSMugunthan V N 
890cd8f9ccSMugunthan V N 		/* Enabling promiscuous mode for one interface will be
900cd8f9ccSMugunthan V N 		 * common for both the interface as the interface shares
910cd8f9ccSMugunthan V N 		 * the same hardware resource.
920cd8f9ccSMugunthan V N 		 */
93606f3993SIvan Khoronzhuk 		for (i = 0; i < cpsw->data.slaves; i++)
94606f3993SIvan Khoronzhuk 			if (cpsw->slaves[i].ndev->flags & IFF_PROMISC)
950cd8f9ccSMugunthan V N 				flag = true;
960cd8f9ccSMugunthan V N 
970cd8f9ccSMugunthan V N 		if (!enable && flag) {
980cd8f9ccSMugunthan V N 			enable = true;
990cd8f9ccSMugunthan V N 			dev_err(&ndev->dev, "promiscuity not disabled as the other interface is still in promiscuity mode\n");
1000cd8f9ccSMugunthan V N 		}
1010cd8f9ccSMugunthan V N 
1020cd8f9ccSMugunthan V N 		if (enable) {
1030cd8f9ccSMugunthan V N 			/* Enable Bypass */
1040cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_BYPASS, 1);
1050cd8f9ccSMugunthan V N 
1060cd8f9ccSMugunthan V N 			dev_dbg(&ndev->dev, "promiscuity enabled\n");
1070cd8f9ccSMugunthan V N 		} else {
1080cd8f9ccSMugunthan V N 			/* Disable Bypass */
1090cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_BYPASS, 0);
1100cd8f9ccSMugunthan V N 			dev_dbg(&ndev->dev, "promiscuity disabled\n");
1110cd8f9ccSMugunthan V N 		}
1120cd8f9ccSMugunthan V N 	} else {
1130cd8f9ccSMugunthan V N 		if (enable) {
1140cd8f9ccSMugunthan V N 			unsigned long timeout = jiffies + HZ;
1150cd8f9ccSMugunthan V N 
1166f979eb3SLennart Sorensen 			/* Disable Learn for all ports (host is port 0 and slaves are port 1 and up */
117606f3993SIvan Khoronzhuk 			for (i = 0; i <= cpsw->data.slaves; i++) {
1180cd8f9ccSMugunthan V N 				cpsw_ale_control_set(ale, i,
1190cd8f9ccSMugunthan V N 						     ALE_PORT_NOLEARN, 1);
1200cd8f9ccSMugunthan V N 				cpsw_ale_control_set(ale, i,
1210cd8f9ccSMugunthan V N 						     ALE_PORT_NO_SA_UPDATE, 1);
1220cd8f9ccSMugunthan V N 			}
1230cd8f9ccSMugunthan V N 
1240cd8f9ccSMugunthan V N 			/* Clear All Untouched entries */
1250cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
1260cd8f9ccSMugunthan V N 			do {
1270cd8f9ccSMugunthan V N 				cpu_relax();
1280cd8f9ccSMugunthan V N 				if (cpsw_ale_control_get(ale, 0, ALE_AGEOUT))
1290cd8f9ccSMugunthan V N 					break;
1300cd8f9ccSMugunthan V N 			} while (time_after(timeout, jiffies));
1310cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
1320cd8f9ccSMugunthan V N 
1330cd8f9ccSMugunthan V N 			/* Clear all mcast from ALE */
13461f1cef9SGrygorii Strashko 			cpsw_ale_flush_multicast(ale, ALE_ALL_PORTS, -1);
13515180ecaSIvan Khoronzhuk 			__hw_addr_ref_unsync_dev(&ndev->mc, ndev, NULL);
1360cd8f9ccSMugunthan V N 
1370cd8f9ccSMugunthan V N 			/* Flood All Unicast Packets to Host port */
1380cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 1);
1390cd8f9ccSMugunthan V N 			dev_dbg(&ndev->dev, "promiscuity enabled\n");
1400cd8f9ccSMugunthan V N 		} else {
1416f979eb3SLennart Sorensen 			/* Don't Flood All Unicast Packets to Host port */
1420cd8f9ccSMugunthan V N 			cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 0);
1430cd8f9ccSMugunthan V N 
1446f979eb3SLennart Sorensen 			/* Enable Learn for all ports (host is port 0 and slaves are port 1 and up */
145606f3993SIvan Khoronzhuk 			for (i = 0; i <= cpsw->data.slaves; i++) {
1460cd8f9ccSMugunthan V N 				cpsw_ale_control_set(ale, i,
1470cd8f9ccSMugunthan V N 						     ALE_PORT_NOLEARN, 0);
1480cd8f9ccSMugunthan V N 				cpsw_ale_control_set(ale, i,
1490cd8f9ccSMugunthan V N 						     ALE_PORT_NO_SA_UPDATE, 0);
1500cd8f9ccSMugunthan V N 			}
1510cd8f9ccSMugunthan V N 			dev_dbg(&ndev->dev, "promiscuity disabled\n");
1520cd8f9ccSMugunthan V N 		}
1530cd8f9ccSMugunthan V N 	}
1540cd8f9ccSMugunthan V N }
1550cd8f9ccSMugunthan V N 
15615180ecaSIvan Khoronzhuk /**
15715180ecaSIvan Khoronzhuk  * cpsw_set_mc - adds multicast entry to the table if it's not added or deletes
15815180ecaSIvan Khoronzhuk  * if it's not deleted
15915180ecaSIvan Khoronzhuk  * @ndev: device to sync
16015180ecaSIvan Khoronzhuk  * @addr: address to be added or deleted
16115180ecaSIvan Khoronzhuk  * @vid: vlan id, if vid < 0 set/unset address for real device
16215180ecaSIvan Khoronzhuk  * @add: add address if the flag is set or remove otherwise
16315180ecaSIvan Khoronzhuk  */
16415180ecaSIvan Khoronzhuk static int cpsw_set_mc(struct net_device *ndev, const u8 *addr,
16515180ecaSIvan Khoronzhuk 		       int vid, int add)
1665c50a856SMugunthan V N {
1675c50a856SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
168606f3993SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
16915180ecaSIvan Khoronzhuk 	int mask, flags, ret;
17025906052SMugunthan V N 
17115180ecaSIvan Khoronzhuk 	if (vid < 0) {
17215180ecaSIvan Khoronzhuk 		if (cpsw->data.dual_emac)
173606f3993SIvan Khoronzhuk 			vid = cpsw->slaves[priv->emac_port].port_vlan;
17415180ecaSIvan Khoronzhuk 		else
1755da19489SIvan Khoronzhuk 			vid = 0;
1765da19489SIvan Khoronzhuk 	}
1775da19489SIvan Khoronzhuk 
17815180ecaSIvan Khoronzhuk 	mask = cpsw->data.dual_emac ? ALE_PORT_HOST : ALE_ALL_PORTS;
17915180ecaSIvan Khoronzhuk 	flags = vid ? ALE_VLAN : 0;
18015180ecaSIvan Khoronzhuk 
18115180ecaSIvan Khoronzhuk 	if (add)
18215180ecaSIvan Khoronzhuk 		ret = cpsw_ale_add_mcast(cpsw->ale, addr, mask, flags, vid, 0);
18315180ecaSIvan Khoronzhuk 	else
18415180ecaSIvan Khoronzhuk 		ret = cpsw_ale_del_mcast(cpsw->ale, addr, 0, flags, vid);
18515180ecaSIvan Khoronzhuk 
18615180ecaSIvan Khoronzhuk 	return ret;
18715180ecaSIvan Khoronzhuk }
18815180ecaSIvan Khoronzhuk 
18915180ecaSIvan Khoronzhuk static int cpsw_update_vlan_mc(struct net_device *vdev, int vid, void *ctx)
19015180ecaSIvan Khoronzhuk {
19115180ecaSIvan Khoronzhuk 	struct addr_sync_ctx *sync_ctx = ctx;
19215180ecaSIvan Khoronzhuk 	struct netdev_hw_addr *ha;
19315180ecaSIvan Khoronzhuk 	int found = 0, ret = 0;
19415180ecaSIvan Khoronzhuk 
19515180ecaSIvan Khoronzhuk 	if (!vdev || !(vdev->flags & IFF_UP))
19615180ecaSIvan Khoronzhuk 		return 0;
19715180ecaSIvan Khoronzhuk 
19815180ecaSIvan Khoronzhuk 	/* vlan address is relevant if its sync_cnt != 0 */
19915180ecaSIvan Khoronzhuk 	netdev_for_each_mc_addr(ha, vdev) {
20015180ecaSIvan Khoronzhuk 		if (ether_addr_equal(ha->addr, sync_ctx->addr)) {
20115180ecaSIvan Khoronzhuk 			found = ha->sync_cnt;
20215180ecaSIvan Khoronzhuk 			break;
20315180ecaSIvan Khoronzhuk 		}
20415180ecaSIvan Khoronzhuk 	}
20515180ecaSIvan Khoronzhuk 
20615180ecaSIvan Khoronzhuk 	if (found)
20715180ecaSIvan Khoronzhuk 		sync_ctx->consumed++;
20815180ecaSIvan Khoronzhuk 
20915180ecaSIvan Khoronzhuk 	if (sync_ctx->flush) {
21015180ecaSIvan Khoronzhuk 		if (!found)
21115180ecaSIvan Khoronzhuk 			cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0);
21215180ecaSIvan Khoronzhuk 		return 0;
21315180ecaSIvan Khoronzhuk 	}
21415180ecaSIvan Khoronzhuk 
21515180ecaSIvan Khoronzhuk 	if (found)
21615180ecaSIvan Khoronzhuk 		ret = cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 1);
21715180ecaSIvan Khoronzhuk 
21815180ecaSIvan Khoronzhuk 	return ret;
21915180ecaSIvan Khoronzhuk }
22015180ecaSIvan Khoronzhuk 
22115180ecaSIvan Khoronzhuk static int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr, int num)
22215180ecaSIvan Khoronzhuk {
22315180ecaSIvan Khoronzhuk 	struct addr_sync_ctx sync_ctx;
22415180ecaSIvan Khoronzhuk 	int ret;
22515180ecaSIvan Khoronzhuk 
22615180ecaSIvan Khoronzhuk 	sync_ctx.consumed = 0;
22715180ecaSIvan Khoronzhuk 	sync_ctx.addr = addr;
22815180ecaSIvan Khoronzhuk 	sync_ctx.ndev = ndev;
22915180ecaSIvan Khoronzhuk 	sync_ctx.flush = 0;
23015180ecaSIvan Khoronzhuk 
23115180ecaSIvan Khoronzhuk 	ret = vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx);
23215180ecaSIvan Khoronzhuk 	if (sync_ctx.consumed < num && !ret)
23315180ecaSIvan Khoronzhuk 		ret = cpsw_set_mc(ndev, addr, -1, 1);
23415180ecaSIvan Khoronzhuk 
23515180ecaSIvan Khoronzhuk 	return ret;
23615180ecaSIvan Khoronzhuk }
23715180ecaSIvan Khoronzhuk 
23815180ecaSIvan Khoronzhuk static int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr, int num)
23915180ecaSIvan Khoronzhuk {
24015180ecaSIvan Khoronzhuk 	struct addr_sync_ctx sync_ctx;
24115180ecaSIvan Khoronzhuk 
24215180ecaSIvan Khoronzhuk 	sync_ctx.consumed = 0;
24315180ecaSIvan Khoronzhuk 	sync_ctx.addr = addr;
24415180ecaSIvan Khoronzhuk 	sync_ctx.ndev = ndev;
24515180ecaSIvan Khoronzhuk 	sync_ctx.flush = 1;
24615180ecaSIvan Khoronzhuk 
24715180ecaSIvan Khoronzhuk 	vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx);
24815180ecaSIvan Khoronzhuk 	if (sync_ctx.consumed == num)
24915180ecaSIvan Khoronzhuk 		cpsw_set_mc(ndev, addr, -1, 0);
25015180ecaSIvan Khoronzhuk 
25115180ecaSIvan Khoronzhuk 	return 0;
25215180ecaSIvan Khoronzhuk }
25315180ecaSIvan Khoronzhuk 
25415180ecaSIvan Khoronzhuk static int cpsw_purge_vlan_mc(struct net_device *vdev, int vid, void *ctx)
25515180ecaSIvan Khoronzhuk {
25615180ecaSIvan Khoronzhuk 	struct addr_sync_ctx *sync_ctx = ctx;
25715180ecaSIvan Khoronzhuk 	struct netdev_hw_addr *ha;
25815180ecaSIvan Khoronzhuk 	int found = 0;
25915180ecaSIvan Khoronzhuk 
26015180ecaSIvan Khoronzhuk 	if (!vdev || !(vdev->flags & IFF_UP))
26115180ecaSIvan Khoronzhuk 		return 0;
26215180ecaSIvan Khoronzhuk 
26315180ecaSIvan Khoronzhuk 	/* vlan address is relevant if its sync_cnt != 0 */
26415180ecaSIvan Khoronzhuk 	netdev_for_each_mc_addr(ha, vdev) {
26515180ecaSIvan Khoronzhuk 		if (ether_addr_equal(ha->addr, sync_ctx->addr)) {
26615180ecaSIvan Khoronzhuk 			found = ha->sync_cnt;
26715180ecaSIvan Khoronzhuk 			break;
26815180ecaSIvan Khoronzhuk 		}
26915180ecaSIvan Khoronzhuk 	}
27015180ecaSIvan Khoronzhuk 
27115180ecaSIvan Khoronzhuk 	if (!found)
27215180ecaSIvan Khoronzhuk 		return 0;
27315180ecaSIvan Khoronzhuk 
27415180ecaSIvan Khoronzhuk 	sync_ctx->consumed++;
27515180ecaSIvan Khoronzhuk 	cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0);
27615180ecaSIvan Khoronzhuk 	return 0;
27715180ecaSIvan Khoronzhuk }
27815180ecaSIvan Khoronzhuk 
27915180ecaSIvan Khoronzhuk static int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num)
28015180ecaSIvan Khoronzhuk {
28115180ecaSIvan Khoronzhuk 	struct addr_sync_ctx sync_ctx;
28215180ecaSIvan Khoronzhuk 
28315180ecaSIvan Khoronzhuk 	sync_ctx.addr = addr;
28415180ecaSIvan Khoronzhuk 	sync_ctx.ndev = ndev;
28515180ecaSIvan Khoronzhuk 	sync_ctx.consumed = 0;
28615180ecaSIvan Khoronzhuk 
28715180ecaSIvan Khoronzhuk 	vlan_for_each(ndev, cpsw_purge_vlan_mc, &sync_ctx);
28815180ecaSIvan Khoronzhuk 	if (sync_ctx.consumed < num)
28915180ecaSIvan Khoronzhuk 		cpsw_set_mc(ndev, addr, -1, 0);
29015180ecaSIvan Khoronzhuk 
2915da19489SIvan Khoronzhuk 	return 0;
2925da19489SIvan Khoronzhuk }
2935da19489SIvan Khoronzhuk 
2945da19489SIvan Khoronzhuk static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
2955da19489SIvan Khoronzhuk {
29606095f34SGrygorii Strashko 	struct cpsw_priv *priv = netdev_priv(ndev);
29706095f34SGrygorii Strashko 	struct cpsw_common *cpsw = priv->cpsw;
29806095f34SGrygorii Strashko 	int slave_port = -1;
29906095f34SGrygorii Strashko 
30006095f34SGrygorii Strashko 	if (cpsw->data.dual_emac)
30106095f34SGrygorii Strashko 		slave_port = priv->emac_port + 1;
3025c50a856SMugunthan V N 
3035c50a856SMugunthan V N 	if (ndev->flags & IFF_PROMISC) {
3045c50a856SMugunthan V N 		/* Enable promiscuous mode */
3050cd8f9ccSMugunthan V N 		cpsw_set_promiscious(ndev, true);
30606095f34SGrygorii Strashko 		cpsw_ale_set_allmulti(cpsw->ale, IFF_ALLMULTI, slave_port);
3075c50a856SMugunthan V N 		return;
3080cd8f9ccSMugunthan V N 	} else {
3090cd8f9ccSMugunthan V N 		/* Disable promiscuous mode */
3100cd8f9ccSMugunthan V N 		cpsw_set_promiscious(ndev, false);
3115c50a856SMugunthan V N 	}
3125c50a856SMugunthan V N 
3131e5c4bc4SLennart Sorensen 	/* Restore allmulti on vlans if necessary */
31406095f34SGrygorii Strashko 	cpsw_ale_set_allmulti(cpsw->ale,
31506095f34SGrygorii Strashko 			      ndev->flags & IFF_ALLMULTI, slave_port);
3161e5c4bc4SLennart Sorensen 
31715180ecaSIvan Khoronzhuk 	/* add/remove mcast address either for real netdev or for vlan */
31815180ecaSIvan Khoronzhuk 	__hw_addr_ref_sync_dev(&ndev->mc, ndev, cpsw_add_mc_addr,
31915180ecaSIvan Khoronzhuk 			       cpsw_del_mc_addr);
3205c50a856SMugunthan V N }
3215c50a856SMugunthan V N 
322c24eef28SGrygorii Strashko void cpsw_intr_enable(struct cpsw_common *cpsw)
323df828598SMugunthan V N {
324dda5f5feSGrygorii Strashko 	writel_relaxed(0xFF, &cpsw->wr_regs->tx_en);
325dda5f5feSGrygorii Strashko 	writel_relaxed(0xFF, &cpsw->wr_regs->rx_en);
326df828598SMugunthan V N 
3272c836bd9SIvan Khoronzhuk 	cpdma_ctlr_int_ctrl(cpsw->dma, true);
328df828598SMugunthan V N 	return;
329df828598SMugunthan V N }
330df828598SMugunthan V N 
331c24eef28SGrygorii Strashko void cpsw_intr_disable(struct cpsw_common *cpsw)
332df828598SMugunthan V N {
333dda5f5feSGrygorii Strashko 	writel_relaxed(0, &cpsw->wr_regs->tx_en);
334dda5f5feSGrygorii Strashko 	writel_relaxed(0, &cpsw->wr_regs->rx_en);
335df828598SMugunthan V N 
3362c836bd9SIvan Khoronzhuk 	cpdma_ctlr_int_ctrl(cpsw->dma, false);
337df828598SMugunthan V N 	return;
338df828598SMugunthan V N }
339df828598SMugunthan V N 
340c24eef28SGrygorii Strashko void cpsw_tx_handler(void *token, int len, int status)
341df828598SMugunthan V N {
342e05107e6SIvan Khoronzhuk 	struct netdev_queue	*txq;
343df828598SMugunthan V N 	struct sk_buff		*skb = token;
344df828598SMugunthan V N 	struct net_device	*ndev = skb->dev;
3452a05a622SIvan Khoronzhuk 	struct cpsw_common	*cpsw = ndev_to_cpsw(ndev);
346df828598SMugunthan V N 
347fae50823SMugunthan V N 	/* Check whether the queue is stopped due to stalled tx dma, if the
348fae50823SMugunthan V N 	 * queue is stopped then start the queue as we have free desc for tx
349fae50823SMugunthan V N 	 */
350e05107e6SIvan Khoronzhuk 	txq = netdev_get_tx_queue(ndev, skb_get_queue_mapping(skb));
351e05107e6SIvan Khoronzhuk 	if (unlikely(netif_tx_queue_stopped(txq)))
352e05107e6SIvan Khoronzhuk 		netif_tx_wake_queue(txq);
353e05107e6SIvan Khoronzhuk 
3542a05a622SIvan Khoronzhuk 	cpts_tx_timestamp(cpsw->cpts, skb);
3558dc43ddcSTobias Klauser 	ndev->stats.tx_packets++;
3568dc43ddcSTobias Klauser 	ndev->stats.tx_bytes += len;
357df828598SMugunthan V N 	dev_kfree_skb_any(skb);
358df828598SMugunthan V N }
359df828598SMugunthan V N 
360a3a41d2fSGrygorii Strashko static void cpsw_rx_vlan_encap(struct sk_buff *skb)
361a3a41d2fSGrygorii Strashko {
362a3a41d2fSGrygorii Strashko 	struct cpsw_priv *priv = netdev_priv(skb->dev);
363a3a41d2fSGrygorii Strashko 	struct cpsw_common *cpsw = priv->cpsw;
364a3a41d2fSGrygorii Strashko 	u32 rx_vlan_encap_hdr = *((u32 *)skb->data);
365a3a41d2fSGrygorii Strashko 	u16 vtag, vid, prio, pkt_type;
366a3a41d2fSGrygorii Strashko 
367a3a41d2fSGrygorii Strashko 	/* Remove VLAN header encapsulation word */
368a3a41d2fSGrygorii Strashko 	skb_pull(skb, CPSW_RX_VLAN_ENCAP_HDR_SIZE);
369a3a41d2fSGrygorii Strashko 
370a3a41d2fSGrygorii Strashko 	pkt_type = (rx_vlan_encap_hdr >>
371a3a41d2fSGrygorii Strashko 		    CPSW_RX_VLAN_ENCAP_HDR_PKT_TYPE_SHIFT) &
372a3a41d2fSGrygorii Strashko 		    CPSW_RX_VLAN_ENCAP_HDR_PKT_TYPE_MSK;
373a3a41d2fSGrygorii Strashko 	/* Ignore unknown & Priority-tagged packets*/
374a3a41d2fSGrygorii Strashko 	if (pkt_type == CPSW_RX_VLAN_ENCAP_HDR_PKT_RESERV ||
375a3a41d2fSGrygorii Strashko 	    pkt_type == CPSW_RX_VLAN_ENCAP_HDR_PKT_PRIO_TAG)
376a3a41d2fSGrygorii Strashko 		return;
377a3a41d2fSGrygorii Strashko 
378a3a41d2fSGrygorii Strashko 	vid = (rx_vlan_encap_hdr >>
379a3a41d2fSGrygorii Strashko 	       CPSW_RX_VLAN_ENCAP_HDR_VID_SHIFT) &
380a3a41d2fSGrygorii Strashko 	       VLAN_VID_MASK;
381a3a41d2fSGrygorii Strashko 	/* Ignore vid 0 and pass packet as is */
382a3a41d2fSGrygorii Strashko 	if (!vid)
383a3a41d2fSGrygorii Strashko 		return;
384a3a41d2fSGrygorii Strashko 	/* Ignore default vlans in dual mac mode */
385a3a41d2fSGrygorii Strashko 	if (cpsw->data.dual_emac &&
386a3a41d2fSGrygorii Strashko 	    vid == cpsw->slaves[priv->emac_port].port_vlan)
387a3a41d2fSGrygorii Strashko 		return;
388a3a41d2fSGrygorii Strashko 
389a3a41d2fSGrygorii Strashko 	prio = (rx_vlan_encap_hdr >>
390a3a41d2fSGrygorii Strashko 		CPSW_RX_VLAN_ENCAP_HDR_PRIO_SHIFT) &
391a3a41d2fSGrygorii Strashko 		CPSW_RX_VLAN_ENCAP_HDR_PRIO_MSK;
392a3a41d2fSGrygorii Strashko 
393a3a41d2fSGrygorii Strashko 	vtag = (prio << VLAN_PRIO_SHIFT) | vid;
394a3a41d2fSGrygorii Strashko 	__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag);
395a3a41d2fSGrygorii Strashko 
396a3a41d2fSGrygorii Strashko 	/* strip vlan tag for VLAN-tagged packet */
397a3a41d2fSGrygorii Strashko 	if (pkt_type == CPSW_RX_VLAN_ENCAP_HDR_PKT_VLAN_TAG) {
398a3a41d2fSGrygorii Strashko 		memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
399a3a41d2fSGrygorii Strashko 		skb_pull(skb, VLAN_HLEN);
400a3a41d2fSGrygorii Strashko 	}
401a3a41d2fSGrygorii Strashko }
402a3a41d2fSGrygorii Strashko 
4031a3b5056SOlof Johansson static void cpsw_rx_handler(void *token, int len, int status)
404df828598SMugunthan V N {
405e05107e6SIvan Khoronzhuk 	struct cpdma_chan	*ch;
406df828598SMugunthan V N 	struct sk_buff		*skb = token;
407b4727e69SSebastian Siewior 	struct sk_buff		*new_skb;
408df828598SMugunthan V N 	struct net_device	*ndev = skb->dev;
409fea49f60SIvan Khoronzhuk 	int			ret = 0, port;
4102a05a622SIvan Khoronzhuk 	struct cpsw_common	*cpsw = ndev_to_cpsw(ndev);
411a9423120SIvan Khoronzhuk 	struct cpsw_priv	*priv;
412df828598SMugunthan V N 
413fea49f60SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
414fea49f60SIvan Khoronzhuk 		port = CPDMA_RX_SOURCE_PORT(status);
415fea49f60SIvan Khoronzhuk 		if (port) {
416fea49f60SIvan Khoronzhuk 			ndev = cpsw->slaves[--port].ndev;
417fea49f60SIvan Khoronzhuk 			skb->dev = ndev;
418fea49f60SIvan Khoronzhuk 		}
419fea49f60SIvan Khoronzhuk 	}
420d9ba8f9eSMugunthan V N 
42116e5c57dSMugunthan V N 	if (unlikely(status < 0) || unlikely(!netif_running(ndev))) {
422a0e2c822SMugunthan V N 		/* In dual emac mode check for all interfaces */
423d5bc1613SIvan Khoronzhuk 		if (cpsw->data.dual_emac && cpsw->usage_count &&
424fe734d0aSIvan Khoronzhuk 		    (status >= 0)) {
425a0e2c822SMugunthan V N 			/* The packet received is for the interface which
426a0e2c822SMugunthan V N 			 * is already down and the other interface is up
427dbedd44eSJoe Perches 			 * and running, instead of freeing which results
428a0e2c822SMugunthan V N 			 * in reducing of the number of rx descriptor in
429a0e2c822SMugunthan V N 			 * DMA engine, requeue skb back to cpdma.
430a0e2c822SMugunthan V N 			 */
431a0e2c822SMugunthan V N 			new_skb = skb;
432a0e2c822SMugunthan V N 			goto requeue;
433a0e2c822SMugunthan V N 		}
434a0e2c822SMugunthan V N 
435b4727e69SSebastian Siewior 		/* the interface is going down, skbs are purged */
436df828598SMugunthan V N 		dev_kfree_skb_any(skb);
437df828598SMugunthan V N 		return;
438df828598SMugunthan V N 	}
439b4727e69SSebastian Siewior 
4402a05a622SIvan Khoronzhuk 	new_skb = netdev_alloc_skb_ip_align(ndev, cpsw->rx_packet_max);
441b4727e69SSebastian Siewior 	if (new_skb) {
442e05107e6SIvan Khoronzhuk 		skb_copy_queue_mapping(new_skb, skb);
443df828598SMugunthan V N 		skb_put(skb, len);
444a3a41d2fSGrygorii Strashko 		if (status & CPDMA_RX_VLAN_ENCAP)
445a3a41d2fSGrygorii Strashko 			cpsw_rx_vlan_encap(skb);
446a9423120SIvan Khoronzhuk 		priv = netdev_priv(ndev);
447a9423120SIvan Khoronzhuk 		if (priv->rx_ts_enabled)
4482a05a622SIvan Khoronzhuk 			cpts_rx_timestamp(cpsw->cpts, skb);
449df828598SMugunthan V N 		skb->protocol = eth_type_trans(skb, ndev);
450df828598SMugunthan V N 		netif_receive_skb(skb);
4518dc43ddcSTobias Klauser 		ndev->stats.rx_bytes += len;
4528dc43ddcSTobias Klauser 		ndev->stats.rx_packets++;
453254a49d5SGrygorii Strashko 		kmemleak_not_leak(new_skb);
454b4727e69SSebastian Siewior 	} else {
4558dc43ddcSTobias Klauser 		ndev->stats.rx_dropped++;
456b4727e69SSebastian Siewior 		new_skb = skb;
457df828598SMugunthan V N 	}
458df828598SMugunthan V N 
459a0e2c822SMugunthan V N requeue:
460ce52c744SIvan Khoronzhuk 	if (netif_dormant(ndev)) {
461ce52c744SIvan Khoronzhuk 		dev_kfree_skb_any(new_skb);
462ce52c744SIvan Khoronzhuk 		return;
463ce52c744SIvan Khoronzhuk 	}
464ce52c744SIvan Khoronzhuk 
4658feb0a19SIvan Khoronzhuk 	ch = cpsw->rxv[skb_get_queue_mapping(new_skb)].ch;
466e05107e6SIvan Khoronzhuk 	ret = cpdma_chan_submit(ch, new_skb, new_skb->data,
467b4727e69SSebastian Siewior 				skb_tailroom(new_skb), 0);
468b4727e69SSebastian Siewior 	if (WARN_ON(ret < 0))
469b4727e69SSebastian Siewior 		dev_kfree_skb_any(new_skb);
470df828598SMugunthan V N }
471df828598SMugunthan V N 
472c24eef28SGrygorii Strashko void cpsw_split_res(struct cpsw_common *cpsw)
47348e0a83eSIvan Khoronzhuk {
47432b78d85SIvan Khoronzhuk 	u32 consumed_rate = 0, bigest_rate = 0;
47548e0a83eSIvan Khoronzhuk 	struct cpsw_vector *txv = cpsw->txv;
47632b78d85SIvan Khoronzhuk 	int i, ch_weight, rlim_ch_num = 0;
47748e0a83eSIvan Khoronzhuk 	int budget, bigest_rate_ch = 0;
47848e0a83eSIvan Khoronzhuk 	u32 ch_rate, max_rate;
47948e0a83eSIvan Khoronzhuk 	int ch_budget = 0;
48048e0a83eSIvan Khoronzhuk 
48148e0a83eSIvan Khoronzhuk 	for (i = 0; i < cpsw->tx_ch_num; i++) {
48248e0a83eSIvan Khoronzhuk 		ch_rate = cpdma_chan_get_rate(txv[i].ch);
48348e0a83eSIvan Khoronzhuk 		if (!ch_rate)
48448e0a83eSIvan Khoronzhuk 			continue;
48548e0a83eSIvan Khoronzhuk 
48648e0a83eSIvan Khoronzhuk 		rlim_ch_num++;
48748e0a83eSIvan Khoronzhuk 		consumed_rate += ch_rate;
48848e0a83eSIvan Khoronzhuk 	}
48948e0a83eSIvan Khoronzhuk 
49048e0a83eSIvan Khoronzhuk 	if (cpsw->tx_ch_num == rlim_ch_num) {
49148e0a83eSIvan Khoronzhuk 		max_rate = consumed_rate;
49232b78d85SIvan Khoronzhuk 	} else if (!rlim_ch_num) {
49332b78d85SIvan Khoronzhuk 		ch_budget = CPSW_POLL_WEIGHT / cpsw->tx_ch_num;
49432b78d85SIvan Khoronzhuk 		bigest_rate = 0;
49532b78d85SIvan Khoronzhuk 		max_rate = consumed_rate;
49648e0a83eSIvan Khoronzhuk 	} else {
4970be01b8eSIvan Khoronzhuk 		max_rate = cpsw->speed * 1000;
4980be01b8eSIvan Khoronzhuk 
4990be01b8eSIvan Khoronzhuk 		/* if max_rate is less then expected due to reduced link speed,
5000be01b8eSIvan Khoronzhuk 		 * split proportionally according next potential max speed
5010be01b8eSIvan Khoronzhuk 		 */
5020be01b8eSIvan Khoronzhuk 		if (max_rate < consumed_rate)
5030be01b8eSIvan Khoronzhuk 			max_rate *= 10;
5040be01b8eSIvan Khoronzhuk 
5050be01b8eSIvan Khoronzhuk 		if (max_rate < consumed_rate)
5060be01b8eSIvan Khoronzhuk 			max_rate *= 10;
50732b78d85SIvan Khoronzhuk 
50848e0a83eSIvan Khoronzhuk 		ch_budget = (consumed_rate * CPSW_POLL_WEIGHT) / max_rate;
50948e0a83eSIvan Khoronzhuk 		ch_budget = (CPSW_POLL_WEIGHT - ch_budget) /
51048e0a83eSIvan Khoronzhuk 			    (cpsw->tx_ch_num - rlim_ch_num);
51148e0a83eSIvan Khoronzhuk 		bigest_rate = (max_rate - consumed_rate) /
51248e0a83eSIvan Khoronzhuk 			      (cpsw->tx_ch_num - rlim_ch_num);
51348e0a83eSIvan Khoronzhuk 	}
51448e0a83eSIvan Khoronzhuk 
51532b78d85SIvan Khoronzhuk 	/* split tx weight/budget */
51648e0a83eSIvan Khoronzhuk 	budget = CPSW_POLL_WEIGHT;
51748e0a83eSIvan Khoronzhuk 	for (i = 0; i < cpsw->tx_ch_num; i++) {
51848e0a83eSIvan Khoronzhuk 		ch_rate = cpdma_chan_get_rate(txv[i].ch);
51948e0a83eSIvan Khoronzhuk 		if (ch_rate) {
52048e0a83eSIvan Khoronzhuk 			txv[i].budget = (ch_rate * CPSW_POLL_WEIGHT) / max_rate;
52148e0a83eSIvan Khoronzhuk 			if (!txv[i].budget)
52232b78d85SIvan Khoronzhuk 				txv[i].budget++;
52348e0a83eSIvan Khoronzhuk 			if (ch_rate > bigest_rate) {
52448e0a83eSIvan Khoronzhuk 				bigest_rate_ch = i;
52548e0a83eSIvan Khoronzhuk 				bigest_rate = ch_rate;
52648e0a83eSIvan Khoronzhuk 			}
52732b78d85SIvan Khoronzhuk 
52832b78d85SIvan Khoronzhuk 			ch_weight = (ch_rate * 100) / max_rate;
52932b78d85SIvan Khoronzhuk 			if (!ch_weight)
53032b78d85SIvan Khoronzhuk 				ch_weight++;
53132b78d85SIvan Khoronzhuk 			cpdma_chan_set_weight(cpsw->txv[i].ch, ch_weight);
53248e0a83eSIvan Khoronzhuk 		} else {
53348e0a83eSIvan Khoronzhuk 			txv[i].budget = ch_budget;
53448e0a83eSIvan Khoronzhuk 			if (!bigest_rate_ch)
53548e0a83eSIvan Khoronzhuk 				bigest_rate_ch = i;
53632b78d85SIvan Khoronzhuk 			cpdma_chan_set_weight(cpsw->txv[i].ch, 0);
53748e0a83eSIvan Khoronzhuk 		}
53848e0a83eSIvan Khoronzhuk 
53948e0a83eSIvan Khoronzhuk 		budget -= txv[i].budget;
54048e0a83eSIvan Khoronzhuk 	}
54148e0a83eSIvan Khoronzhuk 
54248e0a83eSIvan Khoronzhuk 	if (budget)
54348e0a83eSIvan Khoronzhuk 		txv[bigest_rate_ch].budget += budget;
54448e0a83eSIvan Khoronzhuk 
54548e0a83eSIvan Khoronzhuk 	/* split rx budget */
54648e0a83eSIvan Khoronzhuk 	budget = CPSW_POLL_WEIGHT;
54748e0a83eSIvan Khoronzhuk 	ch_budget = budget / cpsw->rx_ch_num;
54848e0a83eSIvan Khoronzhuk 	for (i = 0; i < cpsw->rx_ch_num; i++) {
54948e0a83eSIvan Khoronzhuk 		cpsw->rxv[i].budget = ch_budget;
55048e0a83eSIvan Khoronzhuk 		budget -= ch_budget;
55148e0a83eSIvan Khoronzhuk 	}
55248e0a83eSIvan Khoronzhuk 
55348e0a83eSIvan Khoronzhuk 	if (budget)
55448e0a83eSIvan Khoronzhuk 		cpsw->rxv[0].budget += budget;
55548e0a83eSIvan Khoronzhuk }
55648e0a83eSIvan Khoronzhuk 
557c03abd84SFelipe Balbi static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id)
558df828598SMugunthan V N {
559dbc4ec52SIvan Khoronzhuk 	struct cpsw_common *cpsw = dev_id;
5607ce67a38SFelipe Balbi 
5615d8d0d4dSIvan Khoronzhuk 	writel(0, &cpsw->wr_regs->tx_en);
5622c836bd9SIvan Khoronzhuk 	cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_TX);
563c03abd84SFelipe Balbi 
564e38b5a3dSIvan Khoronzhuk 	if (cpsw->quirk_irq) {
565e38b5a3dSIvan Khoronzhuk 		disable_irq_nosync(cpsw->irqs_table[1]);
566e38b5a3dSIvan Khoronzhuk 		cpsw->tx_irq_disabled = true;
5677da11600SMugunthan V N 	}
5687da11600SMugunthan V N 
569dbc4ec52SIvan Khoronzhuk 	napi_schedule(&cpsw->napi_tx);
570c03abd84SFelipe Balbi 	return IRQ_HANDLED;
571c03abd84SFelipe Balbi }
572c03abd84SFelipe Balbi 
573c03abd84SFelipe Balbi static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id)
574c03abd84SFelipe Balbi {
575dbc4ec52SIvan Khoronzhuk 	struct cpsw_common *cpsw = dev_id;
576c03abd84SFelipe Balbi 
5772c836bd9SIvan Khoronzhuk 	cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_RX);
5785d8d0d4dSIvan Khoronzhuk 	writel(0, &cpsw->wr_regs->rx_en);
579fd51cf19SSebastian Siewior 
580e38b5a3dSIvan Khoronzhuk 	if (cpsw->quirk_irq) {
581e38b5a3dSIvan Khoronzhuk 		disable_irq_nosync(cpsw->irqs_table[0]);
582e38b5a3dSIvan Khoronzhuk 		cpsw->rx_irq_disabled = true;
5837da11600SMugunthan V N 	}
5847da11600SMugunthan V N 
585dbc4ec52SIvan Khoronzhuk 	napi_schedule(&cpsw->napi_rx);
586df828598SMugunthan V N 	return IRQ_HANDLED;
587df828598SMugunthan V N }
588df828598SMugunthan V N 
5899611d6d6SIvan Khoronzhuk static int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget)
590df828598SMugunthan V N {
591e05107e6SIvan Khoronzhuk 	u32			ch_map;
5928feb0a19SIvan Khoronzhuk 	int			num_tx, cur_budget, ch;
593dbc4ec52SIvan Khoronzhuk 	struct cpsw_common	*cpsw = napi_to_cpsw(napi_tx);
5948feb0a19SIvan Khoronzhuk 	struct cpsw_vector	*txv;
59532a7432cSMugunthan V N 
596e05107e6SIvan Khoronzhuk 	/* process every unprocessed channel */
597e05107e6SIvan Khoronzhuk 	ch_map = cpdma_ctrl_txchs_state(cpsw->dma);
59879b3325dSIvan Khoronzhuk 	for (ch = 0, num_tx = 0; ch_map & 0xff; ch_map <<= 1, ch++) {
59979b3325dSIvan Khoronzhuk 		if (!(ch_map & 0x80))
600e05107e6SIvan Khoronzhuk 			continue;
601e05107e6SIvan Khoronzhuk 
6028feb0a19SIvan Khoronzhuk 		txv = &cpsw->txv[ch];
6038feb0a19SIvan Khoronzhuk 		if (unlikely(txv->budget > budget - num_tx))
6048feb0a19SIvan Khoronzhuk 			cur_budget = budget - num_tx;
6058feb0a19SIvan Khoronzhuk 		else
6068feb0a19SIvan Khoronzhuk 			cur_budget = txv->budget;
6078feb0a19SIvan Khoronzhuk 
6088feb0a19SIvan Khoronzhuk 		num_tx += cpdma_chan_process(txv->ch, cur_budget);
609342934a5SIvan Khoronzhuk 		if (num_tx >= budget)
610342934a5SIvan Khoronzhuk 			break;
611e05107e6SIvan Khoronzhuk 	}
612e05107e6SIvan Khoronzhuk 
61332a7432cSMugunthan V N 	if (num_tx < budget) {
61432a7432cSMugunthan V N 		napi_complete(napi_tx);
6155d8d0d4dSIvan Khoronzhuk 		writel(0xff, &cpsw->wr_regs->tx_en);
6169611d6d6SIvan Khoronzhuk 	}
6179611d6d6SIvan Khoronzhuk 
6189611d6d6SIvan Khoronzhuk 	return num_tx;
6199611d6d6SIvan Khoronzhuk }
6209611d6d6SIvan Khoronzhuk 
6219611d6d6SIvan Khoronzhuk static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget)
6229611d6d6SIvan Khoronzhuk {
6239611d6d6SIvan Khoronzhuk 	struct cpsw_common *cpsw = napi_to_cpsw(napi_tx);
6249611d6d6SIvan Khoronzhuk 	int num_tx;
6259611d6d6SIvan Khoronzhuk 
6269611d6d6SIvan Khoronzhuk 	num_tx = cpdma_chan_process(cpsw->txv[0].ch, budget);
6279611d6d6SIvan Khoronzhuk 	if (num_tx < budget) {
6289611d6d6SIvan Khoronzhuk 		napi_complete(napi_tx);
6299611d6d6SIvan Khoronzhuk 		writel(0xff, &cpsw->wr_regs->tx_en);
6309611d6d6SIvan Khoronzhuk 		if (cpsw->tx_irq_disabled) {
631e38b5a3dSIvan Khoronzhuk 			cpsw->tx_irq_disabled = false;
632e38b5a3dSIvan Khoronzhuk 			enable_irq(cpsw->irqs_table[1]);
6337da11600SMugunthan V N 		}
63432a7432cSMugunthan V N 	}
63532a7432cSMugunthan V N 
63632a7432cSMugunthan V N 	return num_tx;
63732a7432cSMugunthan V N }
63832a7432cSMugunthan V N 
6399611d6d6SIvan Khoronzhuk static int cpsw_rx_mq_poll(struct napi_struct *napi_rx, int budget)
64032a7432cSMugunthan V N {
641e05107e6SIvan Khoronzhuk 	u32			ch_map;
6428feb0a19SIvan Khoronzhuk 	int			num_rx, cur_budget, ch;
643dbc4ec52SIvan Khoronzhuk 	struct cpsw_common	*cpsw = napi_to_cpsw(napi_rx);
6448feb0a19SIvan Khoronzhuk 	struct cpsw_vector	*rxv;
645510a1e72SMugunthan V N 
646e05107e6SIvan Khoronzhuk 	/* process every unprocessed channel */
647e05107e6SIvan Khoronzhuk 	ch_map = cpdma_ctrl_rxchs_state(cpsw->dma);
648342934a5SIvan Khoronzhuk 	for (ch = 0, num_rx = 0; ch_map; ch_map >>= 1, ch++) {
649e05107e6SIvan Khoronzhuk 		if (!(ch_map & 0x01))
650e05107e6SIvan Khoronzhuk 			continue;
651e05107e6SIvan Khoronzhuk 
6528feb0a19SIvan Khoronzhuk 		rxv = &cpsw->rxv[ch];
6538feb0a19SIvan Khoronzhuk 		if (unlikely(rxv->budget > budget - num_rx))
6548feb0a19SIvan Khoronzhuk 			cur_budget = budget - num_rx;
6558feb0a19SIvan Khoronzhuk 		else
6568feb0a19SIvan Khoronzhuk 			cur_budget = rxv->budget;
6578feb0a19SIvan Khoronzhuk 
6588feb0a19SIvan Khoronzhuk 		num_rx += cpdma_chan_process(rxv->ch, cur_budget);
659342934a5SIvan Khoronzhuk 		if (num_rx >= budget)
660342934a5SIvan Khoronzhuk 			break;
661e05107e6SIvan Khoronzhuk 	}
662e05107e6SIvan Khoronzhuk 
663510a1e72SMugunthan V N 	if (num_rx < budget) {
6646ad20165SEric Dumazet 		napi_complete_done(napi_rx, num_rx);
6655d8d0d4dSIvan Khoronzhuk 		writel(0xff, &cpsw->wr_regs->rx_en);
6669611d6d6SIvan Khoronzhuk 	}
6679611d6d6SIvan Khoronzhuk 
6689611d6d6SIvan Khoronzhuk 	return num_rx;
6699611d6d6SIvan Khoronzhuk }
6709611d6d6SIvan Khoronzhuk 
6719611d6d6SIvan Khoronzhuk static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget)
6729611d6d6SIvan Khoronzhuk {
6739611d6d6SIvan Khoronzhuk 	struct cpsw_common *cpsw = napi_to_cpsw(napi_rx);
6749611d6d6SIvan Khoronzhuk 	int num_rx;
6759611d6d6SIvan Khoronzhuk 
6769611d6d6SIvan Khoronzhuk 	num_rx = cpdma_chan_process(cpsw->rxv[0].ch, budget);
6779611d6d6SIvan Khoronzhuk 	if (num_rx < budget) {
6789611d6d6SIvan Khoronzhuk 		napi_complete_done(napi_rx, num_rx);
6799611d6d6SIvan Khoronzhuk 		writel(0xff, &cpsw->wr_regs->rx_en);
6809611d6d6SIvan Khoronzhuk 		if (cpsw->rx_irq_disabled) {
681e38b5a3dSIvan Khoronzhuk 			cpsw->rx_irq_disabled = false;
682e38b5a3dSIvan Khoronzhuk 			enable_irq(cpsw->irqs_table[0]);
6837da11600SMugunthan V N 		}
684510a1e72SMugunthan V N 	}
685df828598SMugunthan V N 
686df828598SMugunthan V N 	return num_rx;
687df828598SMugunthan V N }
688df828598SMugunthan V N 
689df828598SMugunthan V N static inline void soft_reset(const char *module, void __iomem *reg)
690df828598SMugunthan V N {
691df828598SMugunthan V N 	unsigned long timeout = jiffies + HZ;
692df828598SMugunthan V N 
693dda5f5feSGrygorii Strashko 	writel_relaxed(1, reg);
694df828598SMugunthan V N 	do {
695df828598SMugunthan V N 		cpu_relax();
696dda5f5feSGrygorii Strashko 	} while ((readl_relaxed(reg) & 1) && time_after(timeout, jiffies));
697df828598SMugunthan V N 
698dda5f5feSGrygorii Strashko 	WARN(readl_relaxed(reg) & 1, "failed to soft-reset %s\n", module);
699df828598SMugunthan V N }
700df828598SMugunthan V N 
701df828598SMugunthan V N static void cpsw_set_slave_mac(struct cpsw_slave *slave,
702df828598SMugunthan V N 			       struct cpsw_priv *priv)
703df828598SMugunthan V N {
7049750a3adSRichard Cochran 	slave_write(slave, mac_hi(priv->mac_addr), SA_HI);
7059750a3adSRichard Cochran 	slave_write(slave, mac_lo(priv->mac_addr), SA_LO);
706df828598SMugunthan V N }
707df828598SMugunthan V N 
70857d90148SIvan Khoronzhuk static bool cpsw_shp_is_off(struct cpsw_priv *priv)
70957d90148SIvan Khoronzhuk {
71057d90148SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
71157d90148SIvan Khoronzhuk 	struct cpsw_slave *slave;
71257d90148SIvan Khoronzhuk 	u32 shift, mask, val;
71357d90148SIvan Khoronzhuk 
71457d90148SIvan Khoronzhuk 	val = readl_relaxed(&cpsw->regs->ptype);
71557d90148SIvan Khoronzhuk 
71657d90148SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
71757d90148SIvan Khoronzhuk 	shift = CPSW_FIFO_SHAPE_EN_SHIFT + 3 * slave->slave_num;
71857d90148SIvan Khoronzhuk 	mask = 7 << shift;
71957d90148SIvan Khoronzhuk 	val = val & mask;
72057d90148SIvan Khoronzhuk 
72157d90148SIvan Khoronzhuk 	return !val;
72257d90148SIvan Khoronzhuk }
72357d90148SIvan Khoronzhuk 
72457d90148SIvan Khoronzhuk static void cpsw_fifo_shp_on(struct cpsw_priv *priv, int fifo, int on)
72557d90148SIvan Khoronzhuk {
72657d90148SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
72757d90148SIvan Khoronzhuk 	struct cpsw_slave *slave;
72857d90148SIvan Khoronzhuk 	u32 shift, mask, val;
72957d90148SIvan Khoronzhuk 
73057d90148SIvan Khoronzhuk 	val = readl_relaxed(&cpsw->regs->ptype);
73157d90148SIvan Khoronzhuk 
73257d90148SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
73357d90148SIvan Khoronzhuk 	shift = CPSW_FIFO_SHAPE_EN_SHIFT + 3 * slave->slave_num;
73457d90148SIvan Khoronzhuk 	mask = (1 << --fifo) << shift;
73557d90148SIvan Khoronzhuk 	val = on ? val | mask : val & ~mask;
73657d90148SIvan Khoronzhuk 
73757d90148SIvan Khoronzhuk 	writel_relaxed(val, &cpsw->regs->ptype);
73857d90148SIvan Khoronzhuk }
73957d90148SIvan Khoronzhuk 
740df828598SMugunthan V N static void _cpsw_adjust_link(struct cpsw_slave *slave,
741df828598SMugunthan V N 			      struct cpsw_priv *priv, bool *link)
742df828598SMugunthan V N {
743df828598SMugunthan V N 	struct phy_device	*phy = slave->phy;
744df828598SMugunthan V N 	u32			mac_control = 0;
745df828598SMugunthan V N 	u32			slave_port;
746606f3993SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
747df828598SMugunthan V N 
748df828598SMugunthan V N 	if (!phy)
749df828598SMugunthan V N 		return;
750df828598SMugunthan V N 
7516f1f5836SIvan Khoronzhuk 	slave_port = cpsw_get_slave_port(slave->slave_num);
752df828598SMugunthan V N 
753df828598SMugunthan V N 	if (phy->link) {
754cfc08345SGrygorii Strashko 		mac_control = CPSW_SL_CTL_GMII_EN;
755cfc08345SGrygorii Strashko 
756cfc08345SGrygorii Strashko 		if (phy->speed == 1000)
757cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_GIG;
758cfc08345SGrygorii Strashko 		if (phy->duplex)
759cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_FULLDUPLEX;
760cfc08345SGrygorii Strashko 
761cfc08345SGrygorii Strashko 		/* set speed_in input in case RMII mode is used in 100Mbps */
762cfc08345SGrygorii Strashko 		if (phy->speed == 100)
763cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_IFCTL_A;
764cfc08345SGrygorii Strashko 		/* in band mode only works in 10Mbps RGMII mode */
765cfc08345SGrygorii Strashko 		else if ((phy->speed == 10) && phy_interface_is_rgmii(phy))
766cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_EXT_EN; /* In Band mode */
767cfc08345SGrygorii Strashko 
768cfc08345SGrygorii Strashko 		if (priv->rx_pause)
769cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_RX_FLOW_EN;
770cfc08345SGrygorii Strashko 
771cfc08345SGrygorii Strashko 		if (priv->tx_pause)
772cfc08345SGrygorii Strashko 			mac_control |= CPSW_SL_CTL_TX_FLOW_EN;
773cfc08345SGrygorii Strashko 
774cfc08345SGrygorii Strashko 		if (mac_control != slave->mac_control)
775cfc08345SGrygorii Strashko 			cpsw_sl_ctl_set(slave->mac_sl, mac_control);
776df828598SMugunthan V N 
777df828598SMugunthan V N 		/* enable forwarding */
7782a05a622SIvan Khoronzhuk 		cpsw_ale_control_set(cpsw->ale, slave_port,
779df828598SMugunthan V N 				     ALE_PORT_STATE, ALE_PORT_STATE_FORWARD);
780df828598SMugunthan V N 
781df828598SMugunthan V N 		*link = true;
78257d90148SIvan Khoronzhuk 
78357d90148SIvan Khoronzhuk 		if (priv->shp_cfg_speed &&
78457d90148SIvan Khoronzhuk 		    priv->shp_cfg_speed != slave->phy->speed &&
78557d90148SIvan Khoronzhuk 		    !cpsw_shp_is_off(priv))
78657d90148SIvan Khoronzhuk 			dev_warn(priv->dev,
78757d90148SIvan Khoronzhuk 				 "Speed was changed, CBS shaper speeds are changed!");
788df828598SMugunthan V N 	} else {
789df828598SMugunthan V N 		mac_control = 0;
790df828598SMugunthan V N 		/* disable forwarding */
7912a05a622SIvan Khoronzhuk 		cpsw_ale_control_set(cpsw->ale, slave_port,
792df828598SMugunthan V N 				     ALE_PORT_STATE, ALE_PORT_STATE_DISABLE);
793cfc08345SGrygorii Strashko 
794cfc08345SGrygorii Strashko 		cpsw_sl_wait_for_idle(slave->mac_sl, 100);
795cfc08345SGrygorii Strashko 
796cfc08345SGrygorii Strashko 		cpsw_sl_ctl_reset(slave->mac_sl);
797df828598SMugunthan V N 	}
798df828598SMugunthan V N 
799cfc08345SGrygorii Strashko 	if (mac_control != slave->mac_control)
800df828598SMugunthan V N 		phy_print_status(phy);
801df828598SMugunthan V N 
802df828598SMugunthan V N 	slave->mac_control = mac_control;
803df828598SMugunthan V N }
804df828598SMugunthan V N 
8050be01b8eSIvan Khoronzhuk static int cpsw_get_common_speed(struct cpsw_common *cpsw)
8060be01b8eSIvan Khoronzhuk {
8070be01b8eSIvan Khoronzhuk 	int i, speed;
8080be01b8eSIvan Khoronzhuk 
8090be01b8eSIvan Khoronzhuk 	for (i = 0, speed = 0; i < cpsw->data.slaves; i++)
8100be01b8eSIvan Khoronzhuk 		if (cpsw->slaves[i].phy && cpsw->slaves[i].phy->link)
8110be01b8eSIvan Khoronzhuk 			speed += cpsw->slaves[i].phy->speed;
8120be01b8eSIvan Khoronzhuk 
8130be01b8eSIvan Khoronzhuk 	return speed;
8140be01b8eSIvan Khoronzhuk }
8150be01b8eSIvan Khoronzhuk 
8160be01b8eSIvan Khoronzhuk static int cpsw_need_resplit(struct cpsw_common *cpsw)
8170be01b8eSIvan Khoronzhuk {
8180be01b8eSIvan Khoronzhuk 	int i, rlim_ch_num;
8190be01b8eSIvan Khoronzhuk 	int speed, ch_rate;
8200be01b8eSIvan Khoronzhuk 
8210be01b8eSIvan Khoronzhuk 	/* re-split resources only in case speed was changed */
8220be01b8eSIvan Khoronzhuk 	speed = cpsw_get_common_speed(cpsw);
8230be01b8eSIvan Khoronzhuk 	if (speed == cpsw->speed || !speed)
8240be01b8eSIvan Khoronzhuk 		return 0;
8250be01b8eSIvan Khoronzhuk 
8260be01b8eSIvan Khoronzhuk 	cpsw->speed = speed;
8270be01b8eSIvan Khoronzhuk 
8280be01b8eSIvan Khoronzhuk 	for (i = 0, rlim_ch_num = 0; i < cpsw->tx_ch_num; i++) {
8290be01b8eSIvan Khoronzhuk 		ch_rate = cpdma_chan_get_rate(cpsw->txv[i].ch);
8300be01b8eSIvan Khoronzhuk 		if (!ch_rate)
8310be01b8eSIvan Khoronzhuk 			break;
8320be01b8eSIvan Khoronzhuk 
8330be01b8eSIvan Khoronzhuk 		rlim_ch_num++;
8340be01b8eSIvan Khoronzhuk 	}
8350be01b8eSIvan Khoronzhuk 
8360be01b8eSIvan Khoronzhuk 	/* cases not dependent on speed */
8370be01b8eSIvan Khoronzhuk 	if (!rlim_ch_num || rlim_ch_num == cpsw->tx_ch_num)
8380be01b8eSIvan Khoronzhuk 		return 0;
8390be01b8eSIvan Khoronzhuk 
8400be01b8eSIvan Khoronzhuk 	return 1;
8410be01b8eSIvan Khoronzhuk }
8420be01b8eSIvan Khoronzhuk 
843df828598SMugunthan V N static void cpsw_adjust_link(struct net_device *ndev)
844df828598SMugunthan V N {
845df828598SMugunthan V N 	struct cpsw_priv	*priv = netdev_priv(ndev);
8460be01b8eSIvan Khoronzhuk 	struct cpsw_common	*cpsw = priv->cpsw;
847df828598SMugunthan V N 	bool			link = false;
848df828598SMugunthan V N 
849df828598SMugunthan V N 	for_each_slave(priv, _cpsw_adjust_link, priv, &link);
850df828598SMugunthan V N 
851df828598SMugunthan V N 	if (link) {
8520be01b8eSIvan Khoronzhuk 		if (cpsw_need_resplit(cpsw))
8539763a891SGrygorii Strashko 			cpsw_split_res(cpsw);
8540be01b8eSIvan Khoronzhuk 
855df828598SMugunthan V N 		netif_carrier_on(ndev);
856df828598SMugunthan V N 		if (netif_running(ndev))
857e05107e6SIvan Khoronzhuk 			netif_tx_wake_all_queues(ndev);
858df828598SMugunthan V N 	} else {
859df828598SMugunthan V N 		netif_carrier_off(ndev);
860e05107e6SIvan Khoronzhuk 		netif_tx_stop_all_queues(ndev);
861df828598SMugunthan V N 	}
862df828598SMugunthan V N }
863df828598SMugunthan V N 
864d9ba8f9eSMugunthan V N static inline void cpsw_add_dual_emac_def_ale_entries(
865d9ba8f9eSMugunthan V N 		struct cpsw_priv *priv, struct cpsw_slave *slave,
866d9ba8f9eSMugunthan V N 		u32 slave_port)
867d9ba8f9eSMugunthan V N {
8682a05a622SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
86971a2cbb7SGrygorii Strashko 	u32 port_mask = 1 << slave_port | ALE_PORT_HOST;
870d9ba8f9eSMugunthan V N 
8712a05a622SIvan Khoronzhuk 	if (cpsw->version == CPSW_VERSION_1)
872d9ba8f9eSMugunthan V N 		slave_write(slave, slave->port_vlan, CPSW1_PORT_VLAN);
873d9ba8f9eSMugunthan V N 	else
874d9ba8f9eSMugunthan V N 		slave_write(slave, slave->port_vlan, CPSW2_PORT_VLAN);
8752a05a622SIvan Khoronzhuk 	cpsw_ale_add_vlan(cpsw->ale, slave->port_vlan, port_mask,
876d9ba8f9eSMugunthan V N 			  port_mask, port_mask, 0);
8772a05a622SIvan Khoronzhuk 	cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast,
8785b3a5a14SIvan Khoronzhuk 			   ALE_PORT_HOST, ALE_VLAN, slave->port_vlan, 0);
8792a05a622SIvan Khoronzhuk 	cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr,
8802a05a622SIvan Khoronzhuk 			   HOST_PORT_NUM, ALE_VLAN |
8812a05a622SIvan Khoronzhuk 			   ALE_SECURE, slave->port_vlan);
8825e5add17SGrygorii Strashko 	cpsw_ale_control_set(cpsw->ale, slave_port,
8835e5add17SGrygorii Strashko 			     ALE_PORT_DROP_UNKNOWN_VLAN, 1);
884d9ba8f9eSMugunthan V N }
885d9ba8f9eSMugunthan V N 
8861e7a2e21SDaniel Mack static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
8871e7a2e21SDaniel Mack {
888df828598SMugunthan V N 	u32 slave_port;
88930c57f07SSekhar Nori 	struct phy_device *phy;
890649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
891df828598SMugunthan V N 
892cfc08345SGrygorii Strashko 	cpsw_sl_reset(slave->mac_sl, 100);
893cfc08345SGrygorii Strashko 	cpsw_sl_ctl_reset(slave->mac_sl);
894df828598SMugunthan V N 
895df828598SMugunthan V N 	/* setup priority mapping */
896cfc08345SGrygorii Strashko 	cpsw_sl_reg_write(slave->mac_sl, CPSW_SL_RX_PRI_MAP,
897cfc08345SGrygorii Strashko 			  RX_PRIORITY_MAPPING);
8989750a3adSRichard Cochran 
8992a05a622SIvan Khoronzhuk 	switch (cpsw->version) {
9009750a3adSRichard Cochran 	case CPSW_VERSION_1:
9019750a3adSRichard Cochran 		slave_write(slave, TX_PRIORITY_MAPPING, CPSW1_TX_PRI_MAP);
90248f5bcccSGrygorii Strashko 		/* Increase RX FIFO size to 5 for supporting fullduplex
90348f5bcccSGrygorii Strashko 		 * flow control mode
90448f5bcccSGrygorii Strashko 		 */
90548f5bcccSGrygorii Strashko 		slave_write(slave,
90648f5bcccSGrygorii Strashko 			    (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) |
90748f5bcccSGrygorii Strashko 			    CPSW_MAX_BLKS_RX, CPSW1_MAX_BLKS);
9089750a3adSRichard Cochran 		break;
9099750a3adSRichard Cochran 	case CPSW_VERSION_2:
910c193f365SMugunthan V N 	case CPSW_VERSION_3:
911926489beSMugunthan V N 	case CPSW_VERSION_4:
9129750a3adSRichard Cochran 		slave_write(slave, TX_PRIORITY_MAPPING, CPSW2_TX_PRI_MAP);
91348f5bcccSGrygorii Strashko 		/* Increase RX FIFO size to 5 for supporting fullduplex
91448f5bcccSGrygorii Strashko 		 * flow control mode
91548f5bcccSGrygorii Strashko 		 */
91648f5bcccSGrygorii Strashko 		slave_write(slave,
91748f5bcccSGrygorii Strashko 			    (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) |
91848f5bcccSGrygorii Strashko 			    CPSW_MAX_BLKS_RX, CPSW2_MAX_BLKS);
9199750a3adSRichard Cochran 		break;
9209750a3adSRichard Cochran 	}
921df828598SMugunthan V N 
922df828598SMugunthan V N 	/* setup max packet size, and mac address */
923cfc08345SGrygorii Strashko 	cpsw_sl_reg_write(slave->mac_sl, CPSW_SL_RX_MAXLEN,
924cfc08345SGrygorii Strashko 			  cpsw->rx_packet_max);
925df828598SMugunthan V N 	cpsw_set_slave_mac(slave, priv);
926df828598SMugunthan V N 
927df828598SMugunthan V N 	slave->mac_control = 0;	/* no link yet */
928df828598SMugunthan V N 
9296f1f5836SIvan Khoronzhuk 	slave_port = cpsw_get_slave_port(slave->slave_num);
930df828598SMugunthan V N 
931606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac)
932d9ba8f9eSMugunthan V N 		cpsw_add_dual_emac_def_ale_entries(priv, slave, slave_port);
933d9ba8f9eSMugunthan V N 	else
9342a05a622SIvan Khoronzhuk 		cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast,
935e11b220fSMugunthan V N 				   1 << slave_port, 0, 0, ALE_MCAST_FWD_2);
936df828598SMugunthan V N 
937d733f754SDavid Rivshin 	if (slave->data->phy_node) {
93830c57f07SSekhar Nori 		phy = of_phy_connect(priv->ndev, slave->data->phy_node,
9399e42f715SHeiko Schocher 				 &cpsw_adjust_link, 0, slave->data->phy_if);
94030c57f07SSekhar Nori 		if (!phy) {
941f7ce9103SRob Herring 			dev_err(priv->dev, "phy \"%pOF\" not found on slave %d\n",
942f7ce9103SRob Herring 				slave->data->phy_node,
943d733f754SDavid Rivshin 				slave->slave_num);
944d733f754SDavid Rivshin 			return;
945d733f754SDavid Rivshin 		}
946d733f754SDavid Rivshin 	} else {
94730c57f07SSekhar Nori 		phy = phy_connect(priv->ndev, slave->data->phy_id,
948f9a8f83bSFlorian Fainelli 				 &cpsw_adjust_link, slave->data->phy_if);
94930c57f07SSekhar Nori 		if (IS_ERR(phy)) {
950d733f754SDavid Rivshin 			dev_err(priv->dev,
951d733f754SDavid Rivshin 				"phy \"%s\" not found on slave %d, err %ld\n",
952d733f754SDavid Rivshin 				slave->data->phy_id, slave->slave_num,
95330c57f07SSekhar Nori 				PTR_ERR(phy));
954d733f754SDavid Rivshin 			return;
955d733f754SDavid Rivshin 		}
956d733f754SDavid Rivshin 	}
957d733f754SDavid Rivshin 
95830c57f07SSekhar Nori 	slave->phy = phy;
95930c57f07SSekhar Nori 
9602220943aSAndrew Lunn 	phy_attached_info(slave->phy);
9612220943aSAndrew Lunn 
962df828598SMugunthan V N 	phy_start(slave->phy);
963388367a5SMugunthan V N 
964388367a5SMugunthan V N 	/* Configure GMII_SEL register */
9653ff18849SGrygorii Strashko 	if (!IS_ERR(slave->data->ifphy))
9663ff18849SGrygorii Strashko 		phy_set_mode_ext(slave->data->ifphy, PHY_MODE_ETHERNET,
9673ff18849SGrygorii Strashko 				 slave->data->phy_if);
9683ff18849SGrygorii Strashko 	else
9693ff18849SGrygorii Strashko 		cpsw_phy_sel(cpsw->dev, slave->phy->interface,
9703ff18849SGrygorii Strashko 			     slave->slave_num);
971df828598SMugunthan V N }
972df828598SMugunthan V N 
9733b72c2feSMugunthan V N static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
9743b72c2feSMugunthan V N {
975606f3993SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
976606f3993SIvan Khoronzhuk 	const int vlan = cpsw->data.default_vlan;
9773b72c2feSMugunthan V N 	u32 reg;
9783b72c2feSMugunthan V N 	int i;
9791e5c4bc4SLennart Sorensen 	int unreg_mcast_mask;
9803b72c2feSMugunthan V N 
9812a05a622SIvan Khoronzhuk 	reg = (cpsw->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN :
9823b72c2feSMugunthan V N 	       CPSW2_PORT_VLAN;
9833b72c2feSMugunthan V N 
9845d8d0d4dSIvan Khoronzhuk 	writel(vlan, &cpsw->host_port_regs->port_vlan);
9853b72c2feSMugunthan V N 
986606f3993SIvan Khoronzhuk 	for (i = 0; i < cpsw->data.slaves; i++)
987606f3993SIvan Khoronzhuk 		slave_write(cpsw->slaves + i, vlan, reg);
9883b72c2feSMugunthan V N 
9891e5c4bc4SLennart Sorensen 	if (priv->ndev->flags & IFF_ALLMULTI)
9901e5c4bc4SLennart Sorensen 		unreg_mcast_mask = ALE_ALL_PORTS;
9911e5c4bc4SLennart Sorensen 	else
9921e5c4bc4SLennart Sorensen 		unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2;
9931e5c4bc4SLennart Sorensen 
9942a05a622SIvan Khoronzhuk 	cpsw_ale_add_vlan(cpsw->ale, vlan, ALE_ALL_PORTS,
99561f1cef9SGrygorii Strashko 			  ALE_ALL_PORTS, ALE_ALL_PORTS,
99661f1cef9SGrygorii Strashko 			  unreg_mcast_mask);
9973b72c2feSMugunthan V N }
9983b72c2feSMugunthan V N 
999df828598SMugunthan V N static void cpsw_init_host_port(struct cpsw_priv *priv)
1000df828598SMugunthan V N {
1001d9ba8f9eSMugunthan V N 	u32 fifo_mode;
10025d8d0d4dSIvan Khoronzhuk 	u32 control_reg;
10035d8d0d4dSIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
10043b72c2feSMugunthan V N 
1005df828598SMugunthan V N 	/* soft reset the controller and initialize ale */
10065d8d0d4dSIvan Khoronzhuk 	soft_reset("cpsw", &cpsw->regs->soft_reset);
10072a05a622SIvan Khoronzhuk 	cpsw_ale_start(cpsw->ale);
1008df828598SMugunthan V N 
1009df828598SMugunthan V N 	/* switch to vlan unaware mode */
10102a05a622SIvan Khoronzhuk 	cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_VLAN_AWARE,
10113b72c2feSMugunthan V N 			     CPSW_ALE_VLAN_AWARE);
10125d8d0d4dSIvan Khoronzhuk 	control_reg = readl(&cpsw->regs->control);
1013a3a41d2fSGrygorii Strashko 	control_reg |= CPSW_VLAN_AWARE | CPSW_RX_VLAN_ENCAP;
10145d8d0d4dSIvan Khoronzhuk 	writel(control_reg, &cpsw->regs->control);
1015606f3993SIvan Khoronzhuk 	fifo_mode = (cpsw->data.dual_emac) ? CPSW_FIFO_DUAL_MAC_MODE :
1016d9ba8f9eSMugunthan V N 		     CPSW_FIFO_NORMAL_MODE;
10175d8d0d4dSIvan Khoronzhuk 	writel(fifo_mode, &cpsw->host_port_regs->tx_in_ctl);
1018df828598SMugunthan V N 
1019df828598SMugunthan V N 	/* setup host port priority mapping */
1020dda5f5feSGrygorii Strashko 	writel_relaxed(CPDMA_TX_PRIORITY_MAP,
10215d8d0d4dSIvan Khoronzhuk 		       &cpsw->host_port_regs->cpdma_tx_pri_map);
1022dda5f5feSGrygorii Strashko 	writel_relaxed(0, &cpsw->host_port_regs->cpdma_rx_chan_map);
1023df828598SMugunthan V N 
10242a05a622SIvan Khoronzhuk 	cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM,
1025df828598SMugunthan V N 			     ALE_PORT_STATE, ALE_PORT_STATE_FORWARD);
1026df828598SMugunthan V N 
1027606f3993SIvan Khoronzhuk 	if (!cpsw->data.dual_emac) {
10282a05a622SIvan Khoronzhuk 		cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr, HOST_PORT_NUM,
1029d9ba8f9eSMugunthan V N 				   0, 0);
10302a05a622SIvan Khoronzhuk 		cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast,
103171a2cbb7SGrygorii Strashko 				   ALE_PORT_HOST, 0, 0, ALE_MCAST_FWD_2);
1032df828598SMugunthan V N 	}
1033d9ba8f9eSMugunthan V N }
1034df828598SMugunthan V N 
1035c24eef28SGrygorii Strashko int cpsw_fill_rx_channels(struct cpsw_priv *priv)
10363802dce1SIvan Khoronzhuk {
10373802dce1SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
10383802dce1SIvan Khoronzhuk 	struct sk_buff *skb;
10393802dce1SIvan Khoronzhuk 	int ch_buf_num;
1040e05107e6SIvan Khoronzhuk 	int ch, i, ret;
10413802dce1SIvan Khoronzhuk 
1042e05107e6SIvan Khoronzhuk 	for (ch = 0; ch < cpsw->rx_ch_num; ch++) {
10438feb0a19SIvan Khoronzhuk 		ch_buf_num = cpdma_chan_get_rx_buf_num(cpsw->rxv[ch].ch);
10443802dce1SIvan Khoronzhuk 		for (i = 0; i < ch_buf_num; i++) {
10453802dce1SIvan Khoronzhuk 			skb = __netdev_alloc_skb_ip_align(priv->ndev,
10463802dce1SIvan Khoronzhuk 							  cpsw->rx_packet_max,
10473802dce1SIvan Khoronzhuk 							  GFP_KERNEL);
10483802dce1SIvan Khoronzhuk 			if (!skb) {
10493802dce1SIvan Khoronzhuk 				cpsw_err(priv, ifup, "cannot allocate skb\n");
10503802dce1SIvan Khoronzhuk 				return -ENOMEM;
10513802dce1SIvan Khoronzhuk 			}
10523802dce1SIvan Khoronzhuk 
1053e05107e6SIvan Khoronzhuk 			skb_set_queue_mapping(skb, ch);
10548feb0a19SIvan Khoronzhuk 			ret = cpdma_chan_submit(cpsw->rxv[ch].ch, skb,
10558feb0a19SIvan Khoronzhuk 						skb->data, skb_tailroom(skb),
10568feb0a19SIvan Khoronzhuk 						0);
10573802dce1SIvan Khoronzhuk 			if (ret < 0) {
10583802dce1SIvan Khoronzhuk 				cpsw_err(priv, ifup,
1059e05107e6SIvan Khoronzhuk 					 "cannot submit skb to channel %d rx, error %d\n",
1060e05107e6SIvan Khoronzhuk 					 ch, ret);
10613802dce1SIvan Khoronzhuk 				kfree_skb(skb);
10623802dce1SIvan Khoronzhuk 				return ret;
10633802dce1SIvan Khoronzhuk 			}
10643802dce1SIvan Khoronzhuk 			kmemleak_not_leak(skb);
10653802dce1SIvan Khoronzhuk 		}
10663802dce1SIvan Khoronzhuk 
1067e05107e6SIvan Khoronzhuk 		cpsw_info(priv, ifup, "ch %d rx, submitted %d descriptors\n",
1068e05107e6SIvan Khoronzhuk 			  ch, ch_buf_num);
1069e05107e6SIvan Khoronzhuk 	}
10703802dce1SIvan Khoronzhuk 
1071e05107e6SIvan Khoronzhuk 	return 0;
10723802dce1SIvan Khoronzhuk }
10733802dce1SIvan Khoronzhuk 
10742a05a622SIvan Khoronzhuk static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_common *cpsw)
1075aacebbf8SSebastian Siewior {
10763995d265SSchuyler Patton 	u32 slave_port;
10773995d265SSchuyler Patton 
10786f1f5836SIvan Khoronzhuk 	slave_port = cpsw_get_slave_port(slave->slave_num);
10793995d265SSchuyler Patton 
1080aacebbf8SSebastian Siewior 	if (!slave->phy)
1081aacebbf8SSebastian Siewior 		return;
1082aacebbf8SSebastian Siewior 	phy_stop(slave->phy);
1083aacebbf8SSebastian Siewior 	phy_disconnect(slave->phy);
1084aacebbf8SSebastian Siewior 	slave->phy = NULL;
10852a05a622SIvan Khoronzhuk 	cpsw_ale_control_set(cpsw->ale, slave_port,
10863995d265SSchuyler Patton 			     ALE_PORT_STATE, ALE_PORT_STATE_DISABLE);
1087cfc08345SGrygorii Strashko 	cpsw_sl_reset(slave->mac_sl, 100);
1088cfc08345SGrygorii Strashko 	cpsw_sl_ctl_reset(slave->mac_sl);
1089aacebbf8SSebastian Siewior }
1090aacebbf8SSebastian Siewior 
10917929a668SIvan Khoronzhuk static int cpsw_tc_to_fifo(int tc, int num_tc)
10927929a668SIvan Khoronzhuk {
10937929a668SIvan Khoronzhuk 	if (tc == num_tc - 1)
10947929a668SIvan Khoronzhuk 		return 0;
10957929a668SIvan Khoronzhuk 
10967929a668SIvan Khoronzhuk 	return CPSW_FIFO_SHAPERS_NUM - tc;
10977929a668SIvan Khoronzhuk }
10987929a668SIvan Khoronzhuk 
109957d90148SIvan Khoronzhuk static int cpsw_set_fifo_bw(struct cpsw_priv *priv, int fifo, int bw)
110057d90148SIvan Khoronzhuk {
110157d90148SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
110257d90148SIvan Khoronzhuk 	u32 val = 0, send_pct, shift;
110357d90148SIvan Khoronzhuk 	struct cpsw_slave *slave;
110457d90148SIvan Khoronzhuk 	int pct = 0, i;
110557d90148SIvan Khoronzhuk 
110657d90148SIvan Khoronzhuk 	if (bw > priv->shp_cfg_speed * 1000)
110757d90148SIvan Khoronzhuk 		goto err;
110857d90148SIvan Khoronzhuk 
110957d90148SIvan Khoronzhuk 	/* shaping has to stay enabled for highest fifos linearly
111057d90148SIvan Khoronzhuk 	 * and fifo bw no more then interface can allow
111157d90148SIvan Khoronzhuk 	 */
111257d90148SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
111357d90148SIvan Khoronzhuk 	send_pct = slave_read(slave, SEND_PERCENT);
111457d90148SIvan Khoronzhuk 	for (i = CPSW_FIFO_SHAPERS_NUM; i > 0; i--) {
111557d90148SIvan Khoronzhuk 		if (!bw) {
111657d90148SIvan Khoronzhuk 			if (i >= fifo || !priv->fifo_bw[i])
111757d90148SIvan Khoronzhuk 				continue;
111857d90148SIvan Khoronzhuk 
111957d90148SIvan Khoronzhuk 			dev_warn(priv->dev, "Prev FIFO%d is shaped", i);
112057d90148SIvan Khoronzhuk 			continue;
112157d90148SIvan Khoronzhuk 		}
112257d90148SIvan Khoronzhuk 
112357d90148SIvan Khoronzhuk 		if (!priv->fifo_bw[i] && i > fifo) {
112457d90148SIvan Khoronzhuk 			dev_err(priv->dev, "Upper FIFO%d is not shaped", i);
112557d90148SIvan Khoronzhuk 			return -EINVAL;
112657d90148SIvan Khoronzhuk 		}
112757d90148SIvan Khoronzhuk 
112857d90148SIvan Khoronzhuk 		shift = (i - 1) * 8;
112957d90148SIvan Khoronzhuk 		if (i == fifo) {
113057d90148SIvan Khoronzhuk 			send_pct &= ~(CPSW_PCT_MASK << shift);
113157d90148SIvan Khoronzhuk 			val = DIV_ROUND_UP(bw, priv->shp_cfg_speed * 10);
113257d90148SIvan Khoronzhuk 			if (!val)
113357d90148SIvan Khoronzhuk 				val = 1;
113457d90148SIvan Khoronzhuk 
113557d90148SIvan Khoronzhuk 			send_pct |= val << shift;
113657d90148SIvan Khoronzhuk 			pct += val;
113757d90148SIvan Khoronzhuk 			continue;
113857d90148SIvan Khoronzhuk 		}
113957d90148SIvan Khoronzhuk 
114057d90148SIvan Khoronzhuk 		if (priv->fifo_bw[i])
114157d90148SIvan Khoronzhuk 			pct += (send_pct >> shift) & CPSW_PCT_MASK;
114257d90148SIvan Khoronzhuk 	}
114357d90148SIvan Khoronzhuk 
114457d90148SIvan Khoronzhuk 	if (pct >= 100)
114557d90148SIvan Khoronzhuk 		goto err;
114657d90148SIvan Khoronzhuk 
114757d90148SIvan Khoronzhuk 	slave_write(slave, send_pct, SEND_PERCENT);
114857d90148SIvan Khoronzhuk 	priv->fifo_bw[fifo] = bw;
114957d90148SIvan Khoronzhuk 
115057d90148SIvan Khoronzhuk 	dev_warn(priv->dev, "set FIFO%d bw = %d\n", fifo,
115157d90148SIvan Khoronzhuk 		 DIV_ROUND_CLOSEST(val * priv->shp_cfg_speed, 100));
115257d90148SIvan Khoronzhuk 
115357d90148SIvan Khoronzhuk 	return 0;
115457d90148SIvan Khoronzhuk err:
115557d90148SIvan Khoronzhuk 	dev_err(priv->dev, "Bandwidth doesn't fit in tc configuration");
115657d90148SIvan Khoronzhuk 	return -EINVAL;
115757d90148SIvan Khoronzhuk }
115857d90148SIvan Khoronzhuk 
115957d90148SIvan Khoronzhuk static int cpsw_set_fifo_rlimit(struct cpsw_priv *priv, int fifo, int bw)
116057d90148SIvan Khoronzhuk {
116157d90148SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
116257d90148SIvan Khoronzhuk 	struct cpsw_slave *slave;
116357d90148SIvan Khoronzhuk 	u32 tx_in_ctl_rg, val;
116457d90148SIvan Khoronzhuk 	int ret;
116557d90148SIvan Khoronzhuk 
116657d90148SIvan Khoronzhuk 	ret = cpsw_set_fifo_bw(priv, fifo, bw);
116757d90148SIvan Khoronzhuk 	if (ret)
116857d90148SIvan Khoronzhuk 		return ret;
116957d90148SIvan Khoronzhuk 
117057d90148SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
117157d90148SIvan Khoronzhuk 	tx_in_ctl_rg = cpsw->version == CPSW_VERSION_1 ?
117257d90148SIvan Khoronzhuk 		       CPSW1_TX_IN_CTL : CPSW2_TX_IN_CTL;
117357d90148SIvan Khoronzhuk 
117457d90148SIvan Khoronzhuk 	if (!bw)
117557d90148SIvan Khoronzhuk 		cpsw_fifo_shp_on(priv, fifo, bw);
117657d90148SIvan Khoronzhuk 
117757d90148SIvan Khoronzhuk 	val = slave_read(slave, tx_in_ctl_rg);
117857d90148SIvan Khoronzhuk 	if (cpsw_shp_is_off(priv)) {
117957d90148SIvan Khoronzhuk 		/* disable FIFOs rate limited queues */
118057d90148SIvan Khoronzhuk 		val &= ~(0xf << CPSW_FIFO_RATE_EN_SHIFT);
118157d90148SIvan Khoronzhuk 
118257d90148SIvan Khoronzhuk 		/* set type of FIFO queues to normal priority mode */
118357d90148SIvan Khoronzhuk 		val &= ~(3 << CPSW_FIFO_QUEUE_TYPE_SHIFT);
118457d90148SIvan Khoronzhuk 
118557d90148SIvan Khoronzhuk 		/* set type of FIFO queues to be rate limited */
118657d90148SIvan Khoronzhuk 		if (bw)
118757d90148SIvan Khoronzhuk 			val |= 2 << CPSW_FIFO_QUEUE_TYPE_SHIFT;
118857d90148SIvan Khoronzhuk 		else
118957d90148SIvan Khoronzhuk 			priv->shp_cfg_speed = 0;
119057d90148SIvan Khoronzhuk 	}
119157d90148SIvan Khoronzhuk 
119257d90148SIvan Khoronzhuk 	/* toggle a FIFO rate limited queue */
119357d90148SIvan Khoronzhuk 	if (bw)
119457d90148SIvan Khoronzhuk 		val |= BIT(fifo + CPSW_FIFO_RATE_EN_SHIFT);
119557d90148SIvan Khoronzhuk 	else
119657d90148SIvan Khoronzhuk 		val &= ~BIT(fifo + CPSW_FIFO_RATE_EN_SHIFT);
119757d90148SIvan Khoronzhuk 	slave_write(slave, val, tx_in_ctl_rg);
119857d90148SIvan Khoronzhuk 
119957d90148SIvan Khoronzhuk 	/* FIFO transmit shape enable */
120057d90148SIvan Khoronzhuk 	cpsw_fifo_shp_on(priv, fifo, bw);
120157d90148SIvan Khoronzhuk 	return 0;
120257d90148SIvan Khoronzhuk }
120357d90148SIvan Khoronzhuk 
120457d90148SIvan Khoronzhuk /* Defaults:
120557d90148SIvan Khoronzhuk  * class A - prio 3
120657d90148SIvan Khoronzhuk  * class B - prio 2
120757d90148SIvan Khoronzhuk  * shaping for class A should be set first
120857d90148SIvan Khoronzhuk  */
120957d90148SIvan Khoronzhuk static int cpsw_set_cbs(struct net_device *ndev,
121057d90148SIvan Khoronzhuk 			struct tc_cbs_qopt_offload *qopt)
121157d90148SIvan Khoronzhuk {
121257d90148SIvan Khoronzhuk 	struct cpsw_priv *priv = netdev_priv(ndev);
121357d90148SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
121457d90148SIvan Khoronzhuk 	struct cpsw_slave *slave;
121557d90148SIvan Khoronzhuk 	int prev_speed = 0;
121657d90148SIvan Khoronzhuk 	int tc, ret, fifo;
121757d90148SIvan Khoronzhuk 	u32 bw = 0;
121857d90148SIvan Khoronzhuk 
121957d90148SIvan Khoronzhuk 	tc = netdev_txq_to_tc(priv->ndev, qopt->queue);
122057d90148SIvan Khoronzhuk 
122157d90148SIvan Khoronzhuk 	/* enable channels in backward order, as highest FIFOs must be rate
122257d90148SIvan Khoronzhuk 	 * limited first and for compliance with CPDMA rate limited channels
122357d90148SIvan Khoronzhuk 	 * that also used in bacward order. FIFO0 cannot be rate limited.
122457d90148SIvan Khoronzhuk 	 */
122557d90148SIvan Khoronzhuk 	fifo = cpsw_tc_to_fifo(tc, ndev->num_tc);
122657d90148SIvan Khoronzhuk 	if (!fifo) {
122757d90148SIvan Khoronzhuk 		dev_err(priv->dev, "Last tc%d can't be rate limited", tc);
122857d90148SIvan Khoronzhuk 		return -EINVAL;
122957d90148SIvan Khoronzhuk 	}
123057d90148SIvan Khoronzhuk 
123157d90148SIvan Khoronzhuk 	/* do nothing, it's disabled anyway */
123257d90148SIvan Khoronzhuk 	if (!qopt->enable && !priv->fifo_bw[fifo])
123357d90148SIvan Khoronzhuk 		return 0;
123457d90148SIvan Khoronzhuk 
123557d90148SIvan Khoronzhuk 	/* shapers can be set if link speed is known */
123657d90148SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
123757d90148SIvan Khoronzhuk 	if (slave->phy && slave->phy->link) {
123857d90148SIvan Khoronzhuk 		if (priv->shp_cfg_speed &&
123957d90148SIvan Khoronzhuk 		    priv->shp_cfg_speed != slave->phy->speed)
124057d90148SIvan Khoronzhuk 			prev_speed = priv->shp_cfg_speed;
124157d90148SIvan Khoronzhuk 
124257d90148SIvan Khoronzhuk 		priv->shp_cfg_speed = slave->phy->speed;
124357d90148SIvan Khoronzhuk 	}
124457d90148SIvan Khoronzhuk 
124557d90148SIvan Khoronzhuk 	if (!priv->shp_cfg_speed) {
124657d90148SIvan Khoronzhuk 		dev_err(priv->dev, "Link speed is not known");
124757d90148SIvan Khoronzhuk 		return -1;
124857d90148SIvan Khoronzhuk 	}
124957d90148SIvan Khoronzhuk 
125057d90148SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
125157d90148SIvan Khoronzhuk 	if (ret < 0) {
125257d90148SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
125357d90148SIvan Khoronzhuk 		return ret;
125457d90148SIvan Khoronzhuk 	}
125557d90148SIvan Khoronzhuk 
125657d90148SIvan Khoronzhuk 	bw = qopt->enable ? qopt->idleslope : 0;
125757d90148SIvan Khoronzhuk 	ret = cpsw_set_fifo_rlimit(priv, fifo, bw);
125857d90148SIvan Khoronzhuk 	if (ret) {
125957d90148SIvan Khoronzhuk 		priv->shp_cfg_speed = prev_speed;
126057d90148SIvan Khoronzhuk 		prev_speed = 0;
126157d90148SIvan Khoronzhuk 	}
126257d90148SIvan Khoronzhuk 
126357d90148SIvan Khoronzhuk 	if (bw && prev_speed)
126457d90148SIvan Khoronzhuk 		dev_warn(priv->dev,
126557d90148SIvan Khoronzhuk 			 "Speed was changed, CBS shaper speeds are changed!");
126657d90148SIvan Khoronzhuk 
126757d90148SIvan Khoronzhuk 	pm_runtime_put_sync(cpsw->dev);
126857d90148SIvan Khoronzhuk 	return ret;
126957d90148SIvan Khoronzhuk }
127057d90148SIvan Khoronzhuk 
12714b4255edSIvan Khoronzhuk static void cpsw_cbs_resume(struct cpsw_slave *slave, struct cpsw_priv *priv)
12724b4255edSIvan Khoronzhuk {
12734b4255edSIvan Khoronzhuk 	int fifo, bw;
12744b4255edSIvan Khoronzhuk 
12754b4255edSIvan Khoronzhuk 	for (fifo = CPSW_FIFO_SHAPERS_NUM; fifo > 0; fifo--) {
12764b4255edSIvan Khoronzhuk 		bw = priv->fifo_bw[fifo];
12774b4255edSIvan Khoronzhuk 		if (!bw)
12784b4255edSIvan Khoronzhuk 			continue;
12794b4255edSIvan Khoronzhuk 
12804b4255edSIvan Khoronzhuk 		cpsw_set_fifo_rlimit(priv, fifo, bw);
12814b4255edSIvan Khoronzhuk 	}
12824b4255edSIvan Khoronzhuk }
12834b4255edSIvan Khoronzhuk 
12844b4255edSIvan Khoronzhuk static void cpsw_mqprio_resume(struct cpsw_slave *slave, struct cpsw_priv *priv)
12854b4255edSIvan Khoronzhuk {
12864b4255edSIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
12874b4255edSIvan Khoronzhuk 	u32 tx_prio_map = 0;
12884b4255edSIvan Khoronzhuk 	int i, tc, fifo;
12894b4255edSIvan Khoronzhuk 	u32 tx_prio_rg;
12904b4255edSIvan Khoronzhuk 
12914b4255edSIvan Khoronzhuk 	if (!priv->mqprio_hw)
12924b4255edSIvan Khoronzhuk 		return;
12934b4255edSIvan Khoronzhuk 
12944b4255edSIvan Khoronzhuk 	for (i = 0; i < 8; i++) {
12954b4255edSIvan Khoronzhuk 		tc = netdev_get_prio_tc_map(priv->ndev, i);
12964b4255edSIvan Khoronzhuk 		fifo = CPSW_FIFO_SHAPERS_NUM - tc;
12974b4255edSIvan Khoronzhuk 		tx_prio_map |= fifo << (4 * i);
12984b4255edSIvan Khoronzhuk 	}
12994b4255edSIvan Khoronzhuk 
13004b4255edSIvan Khoronzhuk 	tx_prio_rg = cpsw->version == CPSW_VERSION_1 ?
13014b4255edSIvan Khoronzhuk 		     CPSW1_TX_PRI_MAP : CPSW2_TX_PRI_MAP;
13024b4255edSIvan Khoronzhuk 
13034b4255edSIvan Khoronzhuk 	slave_write(slave, tx_prio_map, tx_prio_rg);
13044b4255edSIvan Khoronzhuk }
13054b4255edSIvan Khoronzhuk 
130600fe4712SIvan Khoronzhuk static int cpsw_restore_vlans(struct net_device *vdev, int vid, void *arg)
130700fe4712SIvan Khoronzhuk {
130800fe4712SIvan Khoronzhuk 	struct cpsw_priv *priv = arg;
130900fe4712SIvan Khoronzhuk 
131000fe4712SIvan Khoronzhuk 	if (!vdev)
131100fe4712SIvan Khoronzhuk 		return 0;
131200fe4712SIvan Khoronzhuk 
131300fe4712SIvan Khoronzhuk 	cpsw_ndo_vlan_rx_add_vid(priv->ndev, 0, vid);
131400fe4712SIvan Khoronzhuk 	return 0;
131500fe4712SIvan Khoronzhuk }
131600fe4712SIvan Khoronzhuk 
13174b4255edSIvan Khoronzhuk /* restore resources after port reset */
13184b4255edSIvan Khoronzhuk static void cpsw_restore(struct cpsw_priv *priv)
13194b4255edSIvan Khoronzhuk {
132000fe4712SIvan Khoronzhuk 	/* restore vlan configurations */
132100fe4712SIvan Khoronzhuk 	vlan_for_each(priv->ndev, cpsw_restore_vlans, priv);
132200fe4712SIvan Khoronzhuk 
13234b4255edSIvan Khoronzhuk 	/* restore MQPRIO offload */
13244b4255edSIvan Khoronzhuk 	for_each_slave(priv, cpsw_mqprio_resume, priv);
13254b4255edSIvan Khoronzhuk 
13264b4255edSIvan Khoronzhuk 	/* restore CBS offload */
13274b4255edSIvan Khoronzhuk 	for_each_slave(priv, cpsw_cbs_resume, priv);
13284b4255edSIvan Khoronzhuk }
13294b4255edSIvan Khoronzhuk 
1330df828598SMugunthan V N static int cpsw_ndo_open(struct net_device *ndev)
1331df828598SMugunthan V N {
1332df828598SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
1333649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
13343802dce1SIvan Khoronzhuk 	int ret;
1335df828598SMugunthan V N 	u32 reg;
1336df828598SMugunthan V N 
133756e31bd8SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
1338108a6537SGrygorii Strashko 	if (ret < 0) {
133956e31bd8SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
1340108a6537SGrygorii Strashko 		return ret;
1341108a6537SGrygorii Strashko 	}
13423fa88c51SGrygorii Strashko 
1343df828598SMugunthan V N 	netif_carrier_off(ndev);
1344df828598SMugunthan V N 
1345e05107e6SIvan Khoronzhuk 	/* Notify the stack of the actual queue counts. */
1346e05107e6SIvan Khoronzhuk 	ret = netif_set_real_num_tx_queues(ndev, cpsw->tx_ch_num);
1347e05107e6SIvan Khoronzhuk 	if (ret) {
1348e05107e6SIvan Khoronzhuk 		dev_err(priv->dev, "cannot set real number of tx queues\n");
1349e05107e6SIvan Khoronzhuk 		goto err_cleanup;
1350e05107e6SIvan Khoronzhuk 	}
1351e05107e6SIvan Khoronzhuk 
1352e05107e6SIvan Khoronzhuk 	ret = netif_set_real_num_rx_queues(ndev, cpsw->rx_ch_num);
1353e05107e6SIvan Khoronzhuk 	if (ret) {
1354e05107e6SIvan Khoronzhuk 		dev_err(priv->dev, "cannot set real number of rx queues\n");
1355e05107e6SIvan Khoronzhuk 		goto err_cleanup;
1356e05107e6SIvan Khoronzhuk 	}
1357e05107e6SIvan Khoronzhuk 
13582a05a622SIvan Khoronzhuk 	reg = cpsw->version;
1359df828598SMugunthan V N 
1360df828598SMugunthan V N 	dev_info(priv->dev, "initializing cpsw version %d.%d (%d)\n",
1361df828598SMugunthan V N 		 CPSW_MAJOR_VERSION(reg), CPSW_MINOR_VERSION(reg),
1362df828598SMugunthan V N 		 CPSW_RTL_VERSION(reg));
1363df828598SMugunthan V N 
1364d5bc1613SIvan Khoronzhuk 	/* Initialize host and slave ports */
1365d5bc1613SIvan Khoronzhuk 	if (!cpsw->usage_count)
1366df828598SMugunthan V N 		cpsw_init_host_port(priv);
1367df828598SMugunthan V N 	for_each_slave(priv, cpsw_slave_open, priv);
1368df828598SMugunthan V N 
13693b72c2feSMugunthan V N 	/* Add default VLAN */
1370606f3993SIvan Khoronzhuk 	if (!cpsw->data.dual_emac)
13713b72c2feSMugunthan V N 		cpsw_add_default_vlan(priv);
1372e6afea0bSMugunthan V N 	else
13732a05a622SIvan Khoronzhuk 		cpsw_ale_add_vlan(cpsw->ale, cpsw->data.default_vlan,
137461f1cef9SGrygorii Strashko 				  ALE_ALL_PORTS, ALE_ALL_PORTS, 0, 0);
13753b72c2feSMugunthan V N 
1376d5bc1613SIvan Khoronzhuk 	/* initialize shared resources for every ndev */
1377d5bc1613SIvan Khoronzhuk 	if (!cpsw->usage_count) {
1378d9ba8f9eSMugunthan V N 		/* disable priority elevation */
1379dda5f5feSGrygorii Strashko 		writel_relaxed(0, &cpsw->regs->ptype);
1380df828598SMugunthan V N 
1381d9ba8f9eSMugunthan V N 		/* enable statistics collection only on all ports */
1382dda5f5feSGrygorii Strashko 		writel_relaxed(0x7, &cpsw->regs->stat_port_en);
1383df828598SMugunthan V N 
13841923d6e4SMugunthan V N 		/* Enable internal fifo flow control */
13855d8d0d4dSIvan Khoronzhuk 		writel(0x7, &cpsw->regs->flow_control);
13861923d6e4SMugunthan V N 
1387dbc4ec52SIvan Khoronzhuk 		napi_enable(&cpsw->napi_rx);
1388dbc4ec52SIvan Khoronzhuk 		napi_enable(&cpsw->napi_tx);
1389d354eb85SMugunthan V N 
1390e38b5a3dSIvan Khoronzhuk 		if (cpsw->tx_irq_disabled) {
1391e38b5a3dSIvan Khoronzhuk 			cpsw->tx_irq_disabled = false;
1392e38b5a3dSIvan Khoronzhuk 			enable_irq(cpsw->irqs_table[1]);
13937da11600SMugunthan V N 		}
13947da11600SMugunthan V N 
1395e38b5a3dSIvan Khoronzhuk 		if (cpsw->rx_irq_disabled) {
1396e38b5a3dSIvan Khoronzhuk 			cpsw->rx_irq_disabled = false;
1397e38b5a3dSIvan Khoronzhuk 			enable_irq(cpsw->irqs_table[0]);
13987da11600SMugunthan V N 		}
13997da11600SMugunthan V N 
14003802dce1SIvan Khoronzhuk 		ret = cpsw_fill_rx_channels(priv);
14013802dce1SIvan Khoronzhuk 		if (ret < 0)
1402aacebbf8SSebastian Siewior 			goto err_cleanup;
1403f280e89aSMugunthan V N 
14048a2c9a5aSGrygorii Strashko 		if (cpts_register(cpsw->cpts))
1405f280e89aSMugunthan V N 			dev_err(priv->dev, "error registering cpts device\n");
1406f280e89aSMugunthan V N 
1407d9ba8f9eSMugunthan V N 	}
1408df828598SMugunthan V N 
14094b4255edSIvan Khoronzhuk 	cpsw_restore(priv);
14104b4255edSIvan Khoronzhuk 
1411ff5b8ef2SMugunthan V N 	/* Enable Interrupt pacing if configured */
14122a05a622SIvan Khoronzhuk 	if (cpsw->coal_intvl != 0) {
1413ff5b8ef2SMugunthan V N 		struct ethtool_coalesce coal;
1414ff5b8ef2SMugunthan V N 
14152a05a622SIvan Khoronzhuk 		coal.rx_coalesce_usecs = cpsw->coal_intvl;
1416ff5b8ef2SMugunthan V N 		cpsw_set_coalesce(ndev, &coal);
1417ff5b8ef2SMugunthan V N 	}
1418ff5b8ef2SMugunthan V N 
14192c836bd9SIvan Khoronzhuk 	cpdma_ctlr_start(cpsw->dma);
14202c836bd9SIvan Khoronzhuk 	cpsw_intr_enable(cpsw);
1421d5bc1613SIvan Khoronzhuk 	cpsw->usage_count++;
1422f63a975eSMugunthan V N 
1423df828598SMugunthan V N 	return 0;
1424df828598SMugunthan V N 
1425aacebbf8SSebastian Siewior err_cleanup:
142602cacedeSIvan Khoronzhuk 	if (!cpsw->usage_count) {
14272c836bd9SIvan Khoronzhuk 		cpdma_ctlr_stop(cpsw->dma);
14282a05a622SIvan Khoronzhuk 		for_each_slave(priv, cpsw_slave_stop, cpsw);
142902cacedeSIvan Khoronzhuk 	}
143002cacedeSIvan Khoronzhuk 
143156e31bd8SIvan Khoronzhuk 	pm_runtime_put_sync(cpsw->dev);
1432aacebbf8SSebastian Siewior 	netif_carrier_off(priv->ndev);
1433aacebbf8SSebastian Siewior 	return ret;
1434df828598SMugunthan V N }
1435df828598SMugunthan V N 
1436df828598SMugunthan V N static int cpsw_ndo_stop(struct net_device *ndev)
1437df828598SMugunthan V N {
1438df828598SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
1439649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1440df828598SMugunthan V N 
1441df828598SMugunthan V N 	cpsw_info(priv, ifdown, "shutting down cpsw device\n");
144215180ecaSIvan Khoronzhuk 	__hw_addr_ref_unsync_dev(&ndev->mc, ndev, cpsw_purge_all_mc);
1443e05107e6SIvan Khoronzhuk 	netif_tx_stop_all_queues(priv->ndev);
1444df828598SMugunthan V N 	netif_carrier_off(priv->ndev);
1445d9ba8f9eSMugunthan V N 
1446d5bc1613SIvan Khoronzhuk 	if (cpsw->usage_count <= 1) {
1447dbc4ec52SIvan Khoronzhuk 		napi_disable(&cpsw->napi_rx);
1448dbc4ec52SIvan Khoronzhuk 		napi_disable(&cpsw->napi_tx);
14492a05a622SIvan Khoronzhuk 		cpts_unregister(cpsw->cpts);
14502c836bd9SIvan Khoronzhuk 		cpsw_intr_disable(cpsw);
14512c836bd9SIvan Khoronzhuk 		cpdma_ctlr_stop(cpsw->dma);
14522a05a622SIvan Khoronzhuk 		cpsw_ale_stop(cpsw->ale);
1453d9ba8f9eSMugunthan V N 	}
14542a05a622SIvan Khoronzhuk 	for_each_slave(priv, cpsw_slave_stop, cpsw);
14550be01b8eSIvan Khoronzhuk 
14560be01b8eSIvan Khoronzhuk 	if (cpsw_need_resplit(cpsw))
14579763a891SGrygorii Strashko 		cpsw_split_res(cpsw);
14580be01b8eSIvan Khoronzhuk 
1459d5bc1613SIvan Khoronzhuk 	cpsw->usage_count--;
146056e31bd8SIvan Khoronzhuk 	pm_runtime_put_sync(cpsw->dev);
1461df828598SMugunthan V N 	return 0;
1462df828598SMugunthan V N }
1463df828598SMugunthan V N 
1464df828598SMugunthan V N static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
1465df828598SMugunthan V N 				       struct net_device *ndev)
1466df828598SMugunthan V N {
1467df828598SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
14682c836bd9SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1469f44f8417SIvan Khoronzhuk 	struct cpts *cpts = cpsw->cpts;
1470e05107e6SIvan Khoronzhuk 	struct netdev_queue *txq;
1471e05107e6SIvan Khoronzhuk 	struct cpdma_chan *txch;
1472e05107e6SIvan Khoronzhuk 	int ret, q_idx;
1473df828598SMugunthan V N 
1474df828598SMugunthan V N 	if (skb_padto(skb, CPSW_MIN_PACKET_SIZE)) {
1475df828598SMugunthan V N 		cpsw_err(priv, tx_err, "packet pad failed\n");
14768dc43ddcSTobias Klauser 		ndev->stats.tx_dropped++;
14771bf96050SIvan Khoronzhuk 		return NET_XMIT_DROP;
1478df828598SMugunthan V N 	}
1479df828598SMugunthan V N 
14809232b16dSMugunthan V N 	if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
1481a9423120SIvan Khoronzhuk 	    priv->tx_ts_enabled && cpts_can_timestamp(cpts, skb))
14822e5b38abSRichard Cochran 		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
14832e5b38abSRichard Cochran 
1484e05107e6SIvan Khoronzhuk 	q_idx = skb_get_queue_mapping(skb);
1485e05107e6SIvan Khoronzhuk 	if (q_idx >= cpsw->tx_ch_num)
1486e05107e6SIvan Khoronzhuk 		q_idx = q_idx % cpsw->tx_ch_num;
1487e05107e6SIvan Khoronzhuk 
14888feb0a19SIvan Khoronzhuk 	txch = cpsw->txv[q_idx].ch;
148962f94c21SGrygorii Strashko 	txq = netdev_get_tx_queue(ndev, q_idx);
149010ae8054SGrygorii Strashko 	skb_tx_timestamp(skb);
149110ae8054SGrygorii Strashko 	ret = cpdma_chan_submit(txch, skb, skb->data, skb->len,
149210ae8054SGrygorii Strashko 				priv->emac_port + cpsw->data.dual_emac);
1493df828598SMugunthan V N 	if (unlikely(ret != 0)) {
1494df828598SMugunthan V N 		cpsw_err(priv, tx_err, "desc submit failed\n");
1495df828598SMugunthan V N 		goto fail;
1496df828598SMugunthan V N 	}
1497df828598SMugunthan V N 
1498fae50823SMugunthan V N 	/* If there is no more tx desc left free then we need to
1499fae50823SMugunthan V N 	 * tell the kernel to stop sending us tx frames.
1500fae50823SMugunthan V N 	 */
1501e05107e6SIvan Khoronzhuk 	if (unlikely(!cpdma_check_free_tx_desc(txch))) {
1502e05107e6SIvan Khoronzhuk 		netif_tx_stop_queue(txq);
150362f94c21SGrygorii Strashko 
150462f94c21SGrygorii Strashko 		/* Barrier, so that stop_queue visible to other cpus */
150562f94c21SGrygorii Strashko 		smp_mb__after_atomic();
150662f94c21SGrygorii Strashko 
150762f94c21SGrygorii Strashko 		if (cpdma_check_free_tx_desc(txch))
150862f94c21SGrygorii Strashko 			netif_tx_wake_queue(txq);
1509e05107e6SIvan Khoronzhuk 	}
1510fae50823SMugunthan V N 
1511df828598SMugunthan V N 	return NETDEV_TX_OK;
1512df828598SMugunthan V N fail:
15138dc43ddcSTobias Klauser 	ndev->stats.tx_dropped++;
1514e05107e6SIvan Khoronzhuk 	netif_tx_stop_queue(txq);
151562f94c21SGrygorii Strashko 
151662f94c21SGrygorii Strashko 	/* Barrier, so that stop_queue visible to other cpus */
151762f94c21SGrygorii Strashko 	smp_mb__after_atomic();
151862f94c21SGrygorii Strashko 
151962f94c21SGrygorii Strashko 	if (cpdma_check_free_tx_desc(txch))
152062f94c21SGrygorii Strashko 		netif_tx_wake_queue(txq);
152162f94c21SGrygorii Strashko 
1522df828598SMugunthan V N 	return NETDEV_TX_BUSY;
1523df828598SMugunthan V N }
1524df828598SMugunthan V N 
1525c8395d4eSGrygorii Strashko #if IS_ENABLED(CONFIG_TI_CPTS)
15262e5b38abSRichard Cochran 
1527a9423120SIvan Khoronzhuk static void cpsw_hwtstamp_v1(struct cpsw_priv *priv)
15282e5b38abSRichard Cochran {
1529a9423120SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1530606f3993SIvan Khoronzhuk 	struct cpsw_slave *slave = &cpsw->slaves[cpsw->data.active_slave];
15312e5b38abSRichard Cochran 	u32 ts_en, seq_id;
15322e5b38abSRichard Cochran 
1533a9423120SIvan Khoronzhuk 	if (!priv->tx_ts_enabled && !priv->rx_ts_enabled) {
15342e5b38abSRichard Cochran 		slave_write(slave, 0, CPSW1_TS_CTL);
15352e5b38abSRichard Cochran 		return;
15362e5b38abSRichard Cochran 	}
15372e5b38abSRichard Cochran 
15382e5b38abSRichard Cochran 	seq_id = (30 << CPSW_V1_SEQ_ID_OFS_SHIFT) | ETH_P_1588;
15392e5b38abSRichard Cochran 	ts_en = EVENT_MSG_BITS << CPSW_V1_MSG_TYPE_OFS;
15402e5b38abSRichard Cochran 
1541a9423120SIvan Khoronzhuk 	if (priv->tx_ts_enabled)
15422e5b38abSRichard Cochran 		ts_en |= CPSW_V1_TS_TX_EN;
15432e5b38abSRichard Cochran 
1544a9423120SIvan Khoronzhuk 	if (priv->rx_ts_enabled)
15452e5b38abSRichard Cochran 		ts_en |= CPSW_V1_TS_RX_EN;
15462e5b38abSRichard Cochran 
15472e5b38abSRichard Cochran 	slave_write(slave, ts_en, CPSW1_TS_CTL);
15482e5b38abSRichard Cochran 	slave_write(slave, seq_id, CPSW1_TS_SEQ_LTYPE);
15492e5b38abSRichard Cochran }
15502e5b38abSRichard Cochran 
15512e5b38abSRichard Cochran static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
15522e5b38abSRichard Cochran {
1553d9ba8f9eSMugunthan V N 	struct cpsw_slave *slave;
15545d8d0d4dSIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
15552e5b38abSRichard Cochran 	u32 ctrl, mtype;
15562e5b38abSRichard Cochran 
1557cb7d78d0SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
1558d9ba8f9eSMugunthan V N 
15592e5b38abSRichard Cochran 	ctrl = slave_read(slave, CPSW2_CONTROL);
15602a05a622SIvan Khoronzhuk 	switch (cpsw->version) {
156109c55372SGeorge Cherian 	case CPSW_VERSION_2:
156209c55372SGeorge Cherian 		ctrl &= ~CTRL_V2_ALL_TS_MASK;
15632e5b38abSRichard Cochran 
1564a9423120SIvan Khoronzhuk 		if (priv->tx_ts_enabled)
156509c55372SGeorge Cherian 			ctrl |= CTRL_V2_TX_TS_BITS;
15662e5b38abSRichard Cochran 
1567a9423120SIvan Khoronzhuk 		if (priv->rx_ts_enabled)
156809c55372SGeorge Cherian 			ctrl |= CTRL_V2_RX_TS_BITS;
156909c55372SGeorge Cherian 		break;
157009c55372SGeorge Cherian 	case CPSW_VERSION_3:
157109c55372SGeorge Cherian 	default:
157209c55372SGeorge Cherian 		ctrl &= ~CTRL_V3_ALL_TS_MASK;
157309c55372SGeorge Cherian 
1574a9423120SIvan Khoronzhuk 		if (priv->tx_ts_enabled)
157509c55372SGeorge Cherian 			ctrl |= CTRL_V3_TX_TS_BITS;
157609c55372SGeorge Cherian 
1577a9423120SIvan Khoronzhuk 		if (priv->rx_ts_enabled)
157809c55372SGeorge Cherian 			ctrl |= CTRL_V3_RX_TS_BITS;
157909c55372SGeorge Cherian 		break;
158009c55372SGeorge Cherian 	}
15812e5b38abSRichard Cochran 
15822e5b38abSRichard Cochran 	mtype = (30 << TS_SEQ_ID_OFFSET_SHIFT) | EVENT_MSG_BITS;
15832e5b38abSRichard Cochran 
15842e5b38abSRichard Cochran 	slave_write(slave, mtype, CPSW2_TS_SEQ_MTYPE);
15852e5b38abSRichard Cochran 	slave_write(slave, ctrl, CPSW2_CONTROL);
1586dda5f5feSGrygorii Strashko 	writel_relaxed(ETH_P_1588, &cpsw->regs->ts_ltype);
15871ebb2446SIvan Khoronzhuk 	writel_relaxed(ETH_P_8021Q, &cpsw->regs->vlan_ltype);
15882e5b38abSRichard Cochran }
15892e5b38abSRichard Cochran 
1590a5b4145bSBen Hutchings static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
15912e5b38abSRichard Cochran {
15923177bf6fSMugunthan V N 	struct cpsw_priv *priv = netdev_priv(dev);
15932e5b38abSRichard Cochran 	struct hwtstamp_config cfg;
15942a05a622SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
15952e5b38abSRichard Cochran 
15962a05a622SIvan Khoronzhuk 	if (cpsw->version != CPSW_VERSION_1 &&
15972a05a622SIvan Khoronzhuk 	    cpsw->version != CPSW_VERSION_2 &&
15982a05a622SIvan Khoronzhuk 	    cpsw->version != CPSW_VERSION_3)
15992ee91e54SBen Hutchings 		return -EOPNOTSUPP;
16002ee91e54SBen Hutchings 
16012e5b38abSRichard Cochran 	if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
16022e5b38abSRichard Cochran 		return -EFAULT;
16032e5b38abSRichard Cochran 
16042e5b38abSRichard Cochran 	/* reserved for future extensions */
16052e5b38abSRichard Cochran 	if (cfg.flags)
16062e5b38abSRichard Cochran 		return -EINVAL;
16072e5b38abSRichard Cochran 
16082ee91e54SBen Hutchings 	if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON)
16092e5b38abSRichard Cochran 		return -ERANGE;
16102e5b38abSRichard Cochran 
16112e5b38abSRichard Cochran 	switch (cfg.rx_filter) {
16122e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_NONE:
1613a9423120SIvan Khoronzhuk 		priv->rx_ts_enabled = 0;
16142e5b38abSRichard Cochran 		break;
16152e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_ALL:
1616e9523a5aSGrygorii Strashko 	case HWTSTAMP_FILTER_NTP_ALL:
1617e9523a5aSGrygorii Strashko 		return -ERANGE;
16182e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
16192e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
16202e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
1621a9423120SIvan Khoronzhuk 		priv->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
1622e9523a5aSGrygorii Strashko 		cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
1623e9523a5aSGrygorii Strashko 		break;
16242e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
16252e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
16262e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
16272e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
16282e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
16292e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
16302e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_EVENT:
16312e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_SYNC:
16322e5b38abSRichard Cochran 	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
1633a9423120SIvan Khoronzhuk 		priv->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V2_EVENT;
16342e5b38abSRichard Cochran 		cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
16352e5b38abSRichard Cochran 		break;
16362e5b38abSRichard Cochran 	default:
16372e5b38abSRichard Cochran 		return -ERANGE;
16382e5b38abSRichard Cochran 	}
16392e5b38abSRichard Cochran 
1640a9423120SIvan Khoronzhuk 	priv->tx_ts_enabled = cfg.tx_type == HWTSTAMP_TX_ON;
16412ee91e54SBen Hutchings 
16422a05a622SIvan Khoronzhuk 	switch (cpsw->version) {
16432e5b38abSRichard Cochran 	case CPSW_VERSION_1:
1644a9423120SIvan Khoronzhuk 		cpsw_hwtstamp_v1(priv);
16452e5b38abSRichard Cochran 		break;
16462e5b38abSRichard Cochran 	case CPSW_VERSION_2:
1647f7d403cbSGeorge Cherian 	case CPSW_VERSION_3:
16482e5b38abSRichard Cochran 		cpsw_hwtstamp_v2(priv);
16492e5b38abSRichard Cochran 		break;
16502e5b38abSRichard Cochran 	default:
16512ee91e54SBen Hutchings 		WARN_ON(1);
16522e5b38abSRichard Cochran 	}
16532e5b38abSRichard Cochran 
16542e5b38abSRichard Cochran 	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
16552e5b38abSRichard Cochran }
16562e5b38abSRichard Cochran 
1657a5b4145bSBen Hutchings static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
1658a5b4145bSBen Hutchings {
16592a05a622SIvan Khoronzhuk 	struct cpsw_common *cpsw = ndev_to_cpsw(dev);
1660a9423120SIvan Khoronzhuk 	struct cpsw_priv *priv = netdev_priv(dev);
1661a5b4145bSBen Hutchings 	struct hwtstamp_config cfg;
1662a5b4145bSBen Hutchings 
16632a05a622SIvan Khoronzhuk 	if (cpsw->version != CPSW_VERSION_1 &&
16642a05a622SIvan Khoronzhuk 	    cpsw->version != CPSW_VERSION_2 &&
16652a05a622SIvan Khoronzhuk 	    cpsw->version != CPSW_VERSION_3)
1666a5b4145bSBen Hutchings 		return -EOPNOTSUPP;
1667a5b4145bSBen Hutchings 
1668a5b4145bSBen Hutchings 	cfg.flags = 0;
1669a9423120SIvan Khoronzhuk 	cfg.tx_type = priv->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
1670a9423120SIvan Khoronzhuk 	cfg.rx_filter = priv->rx_ts_enabled;
1671a5b4145bSBen Hutchings 
1672a5b4145bSBen Hutchings 	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
1673a5b4145bSBen Hutchings }
1674c8395d4eSGrygorii Strashko #else
1675c8395d4eSGrygorii Strashko static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
1676c8395d4eSGrygorii Strashko {
1677c8395d4eSGrygorii Strashko 	return -EOPNOTSUPP;
1678c8395d4eSGrygorii Strashko }
1679a5b4145bSBen Hutchings 
1680c8395d4eSGrygorii Strashko static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
1681c8395d4eSGrygorii Strashko {
1682c8395d4eSGrygorii Strashko 	return -EOPNOTSUPP;
1683c8395d4eSGrygorii Strashko }
16842e5b38abSRichard Cochran #endif /*CONFIG_TI_CPTS*/
16852e5b38abSRichard Cochran 
16862e5b38abSRichard Cochran static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
16872e5b38abSRichard Cochran {
168811f2c988SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(dev);
1689606f3993SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1690606f3993SIvan Khoronzhuk 	int slave_no = cpsw_slave_index(cpsw, priv);
169111f2c988SMugunthan V N 
16922e5b38abSRichard Cochran 	if (!netif_running(dev))
16932e5b38abSRichard Cochran 		return -EINVAL;
16942e5b38abSRichard Cochran 
169511f2c988SMugunthan V N 	switch (cmd) {
169611f2c988SMugunthan V N 	case SIOCSHWTSTAMP:
1697a5b4145bSBen Hutchings 		return cpsw_hwtstamp_set(dev, req);
1698a5b4145bSBen Hutchings 	case SIOCGHWTSTAMP:
1699a5b4145bSBen Hutchings 		return cpsw_hwtstamp_get(dev, req);
17002e5b38abSRichard Cochran 	}
17012e5b38abSRichard Cochran 
1702606f3993SIvan Khoronzhuk 	if (!cpsw->slaves[slave_no].phy)
1703c1b59947SStefan Sørensen 		return -EOPNOTSUPP;
1704606f3993SIvan Khoronzhuk 	return phy_mii_ioctl(cpsw->slaves[slave_no].phy, req, cmd);
170511f2c988SMugunthan V N }
170611f2c988SMugunthan V N 
1707df828598SMugunthan V N static void cpsw_ndo_tx_timeout(struct net_device *ndev)
1708df828598SMugunthan V N {
1709df828598SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
17102c836bd9SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1711e05107e6SIvan Khoronzhuk 	int ch;
1712df828598SMugunthan V N 
1713df828598SMugunthan V N 	cpsw_err(priv, tx_err, "transmit timeout, restarting dma\n");
17148dc43ddcSTobias Klauser 	ndev->stats.tx_errors++;
17152c836bd9SIvan Khoronzhuk 	cpsw_intr_disable(cpsw);
1716e05107e6SIvan Khoronzhuk 	for (ch = 0; ch < cpsw->tx_ch_num; ch++) {
17178feb0a19SIvan Khoronzhuk 		cpdma_chan_stop(cpsw->txv[ch].ch);
17188feb0a19SIvan Khoronzhuk 		cpdma_chan_start(cpsw->txv[ch].ch);
1719e05107e6SIvan Khoronzhuk 	}
1720e05107e6SIvan Khoronzhuk 
17212c836bd9SIvan Khoronzhuk 	cpsw_intr_enable(cpsw);
172275514b66SGrygorii Strashko 	netif_trans_update(ndev);
172375514b66SGrygorii Strashko 	netif_tx_wake_all_queues(ndev);
1724df828598SMugunthan V N }
1725df828598SMugunthan V N 
1726dcfd8d58SMugunthan V N static int cpsw_ndo_set_mac_address(struct net_device *ndev, void *p)
1727dcfd8d58SMugunthan V N {
1728dcfd8d58SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
1729dcfd8d58SMugunthan V N 	struct sockaddr *addr = (struct sockaddr *)p;
1730649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1731dcfd8d58SMugunthan V N 	int flags = 0;
1732dcfd8d58SMugunthan V N 	u16 vid = 0;
1733a6c5d14fSGrygorii Strashko 	int ret;
1734dcfd8d58SMugunthan V N 
1735dcfd8d58SMugunthan V N 	if (!is_valid_ether_addr(addr->sa_data))
1736dcfd8d58SMugunthan V N 		return -EADDRNOTAVAIL;
1737dcfd8d58SMugunthan V N 
173856e31bd8SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
1739a6c5d14fSGrygorii Strashko 	if (ret < 0) {
174056e31bd8SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
1741a6c5d14fSGrygorii Strashko 		return ret;
1742a6c5d14fSGrygorii Strashko 	}
1743a6c5d14fSGrygorii Strashko 
1744606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
1745606f3993SIvan Khoronzhuk 		vid = cpsw->slaves[priv->emac_port].port_vlan;
1746dcfd8d58SMugunthan V N 		flags = ALE_VLAN;
1747dcfd8d58SMugunthan V N 	}
1748dcfd8d58SMugunthan V N 
17492a05a622SIvan Khoronzhuk 	cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr, HOST_PORT_NUM,
1750dcfd8d58SMugunthan V N 			   flags, vid);
17512a05a622SIvan Khoronzhuk 	cpsw_ale_add_ucast(cpsw->ale, addr->sa_data, HOST_PORT_NUM,
1752dcfd8d58SMugunthan V N 			   flags, vid);
1753dcfd8d58SMugunthan V N 
1754dcfd8d58SMugunthan V N 	memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
1755dcfd8d58SMugunthan V N 	memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
1756dcfd8d58SMugunthan V N 	for_each_slave(priv, cpsw_set_slave_mac, priv);
1757dcfd8d58SMugunthan V N 
175856e31bd8SIvan Khoronzhuk 	pm_runtime_put(cpsw->dev);
1759a6c5d14fSGrygorii Strashko 
1760dcfd8d58SMugunthan V N 	return 0;
1761dcfd8d58SMugunthan V N }
1762dcfd8d58SMugunthan V N 
17633b72c2feSMugunthan V N static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv,
17643b72c2feSMugunthan V N 				unsigned short vid)
17653b72c2feSMugunthan V N {
17663b72c2feSMugunthan V N 	int ret;
17679f6bd8faSMugunthan V N 	int unreg_mcast_mask = 0;
17685b3a5a14SIvan Khoronzhuk 	int mcast_mask;
17699f6bd8faSMugunthan V N 	u32 port_mask;
1770606f3993SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
17719f6bd8faSMugunthan V N 
1772606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
17739f6bd8faSMugunthan V N 		port_mask = (1 << (priv->emac_port + 1)) | ALE_PORT_HOST;
17749f6bd8faSMugunthan V N 
17755b3a5a14SIvan Khoronzhuk 		mcast_mask = ALE_PORT_HOST;
17769f6bd8faSMugunthan V N 		if (priv->ndev->flags & IFF_ALLMULTI)
17775b3a5a14SIvan Khoronzhuk 			unreg_mcast_mask = mcast_mask;
17789f6bd8faSMugunthan V N 	} else {
17799f6bd8faSMugunthan V N 		port_mask = ALE_ALL_PORTS;
17805b3a5a14SIvan Khoronzhuk 		mcast_mask = port_mask;
17811e5c4bc4SLennart Sorensen 
17821e5c4bc4SLennart Sorensen 		if (priv->ndev->flags & IFF_ALLMULTI)
17831e5c4bc4SLennart Sorensen 			unreg_mcast_mask = ALE_ALL_PORTS;
17841e5c4bc4SLennart Sorensen 		else
17851e5c4bc4SLennart Sorensen 			unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2;
17869f6bd8faSMugunthan V N 	}
17873b72c2feSMugunthan V N 
17882a05a622SIvan Khoronzhuk 	ret = cpsw_ale_add_vlan(cpsw->ale, vid, port_mask, 0, port_mask,
178961f1cef9SGrygorii Strashko 				unreg_mcast_mask);
17903b72c2feSMugunthan V N 	if (ret != 0)
17913b72c2feSMugunthan V N 		return ret;
17923b72c2feSMugunthan V N 
17932a05a622SIvan Khoronzhuk 	ret = cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr,
179471a2cbb7SGrygorii Strashko 				 HOST_PORT_NUM, ALE_VLAN, vid);
17953b72c2feSMugunthan V N 	if (ret != 0)
17963b72c2feSMugunthan V N 		goto clean_vid;
17973b72c2feSMugunthan V N 
17982a05a622SIvan Khoronzhuk 	ret = cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast,
17995b3a5a14SIvan Khoronzhuk 				 mcast_mask, ALE_VLAN, vid, 0);
18003b72c2feSMugunthan V N 	if (ret != 0)
18013b72c2feSMugunthan V N 		goto clean_vlan_ucast;
18023b72c2feSMugunthan V N 	return 0;
18033b72c2feSMugunthan V N 
18043b72c2feSMugunthan V N clean_vlan_ucast:
18052a05a622SIvan Khoronzhuk 	cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr,
180671a2cbb7SGrygorii Strashko 			   HOST_PORT_NUM, ALE_VLAN, vid);
18073b72c2feSMugunthan V N clean_vid:
18082a05a622SIvan Khoronzhuk 	cpsw_ale_del_vlan(cpsw->ale, vid, 0);
18093b72c2feSMugunthan V N 	return ret;
18103b72c2feSMugunthan V N }
18113b72c2feSMugunthan V N 
18123b72c2feSMugunthan V N static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev,
181380d5c368SPatrick McHardy 				    __be16 proto, u16 vid)
18143b72c2feSMugunthan V N {
18153b72c2feSMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
1816649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
1817a6c5d14fSGrygorii Strashko 	int ret;
18183b72c2feSMugunthan V N 
1819606f3993SIvan Khoronzhuk 	if (vid == cpsw->data.default_vlan)
18203b72c2feSMugunthan V N 		return 0;
18213b72c2feSMugunthan V N 
182256e31bd8SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
1823a6c5d14fSGrygorii Strashko 	if (ret < 0) {
182456e31bd8SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
1825a6c5d14fSGrygorii Strashko 		return ret;
1826a6c5d14fSGrygorii Strashko 	}
1827a6c5d14fSGrygorii Strashko 
1828606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
182902a54164SMugunthan V N 		/* In dual EMAC, reserved VLAN id should not be used for
183002a54164SMugunthan V N 		 * creating VLAN interfaces as this can break the dual
183102a54164SMugunthan V N 		 * EMAC port separation
183202a54164SMugunthan V N 		 */
183302a54164SMugunthan V N 		int i;
183402a54164SMugunthan V N 
1835606f3993SIvan Khoronzhuk 		for (i = 0; i < cpsw->data.slaves; i++) {
1836803c4f64SIvan Khoronzhuk 			if (vid == cpsw->slaves[i].port_vlan) {
1837803c4f64SIvan Khoronzhuk 				ret = -EINVAL;
1838803c4f64SIvan Khoronzhuk 				goto err;
1839803c4f64SIvan Khoronzhuk 			}
184002a54164SMugunthan V N 		}
184102a54164SMugunthan V N 	}
184202a54164SMugunthan V N 
18433b72c2feSMugunthan V N 	dev_info(priv->dev, "Adding vlanid %d to vlan filter\n", vid);
1844a6c5d14fSGrygorii Strashko 	ret = cpsw_add_vlan_ale_entry(priv, vid);
1845803c4f64SIvan Khoronzhuk err:
184656e31bd8SIvan Khoronzhuk 	pm_runtime_put(cpsw->dev);
1847a6c5d14fSGrygorii Strashko 	return ret;
18483b72c2feSMugunthan V N }
18493b72c2feSMugunthan V N 
18503b72c2feSMugunthan V N static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev,
185180d5c368SPatrick McHardy 				     __be16 proto, u16 vid)
18523b72c2feSMugunthan V N {
18533b72c2feSMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
1854649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
18553b72c2feSMugunthan V N 	int ret;
18563b72c2feSMugunthan V N 
1857606f3993SIvan Khoronzhuk 	if (vid == cpsw->data.default_vlan)
18583b72c2feSMugunthan V N 		return 0;
18593b72c2feSMugunthan V N 
186056e31bd8SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
1861a6c5d14fSGrygorii Strashko 	if (ret < 0) {
186256e31bd8SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
1863a6c5d14fSGrygorii Strashko 		return ret;
1864a6c5d14fSGrygorii Strashko 	}
1865a6c5d14fSGrygorii Strashko 
1866606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
186702a54164SMugunthan V N 		int i;
186802a54164SMugunthan V N 
1869606f3993SIvan Khoronzhuk 		for (i = 0; i < cpsw->data.slaves; i++) {
1870606f3993SIvan Khoronzhuk 			if (vid == cpsw->slaves[i].port_vlan)
1871803c4f64SIvan Khoronzhuk 				goto err;
187202a54164SMugunthan V N 		}
187302a54164SMugunthan V N 	}
187402a54164SMugunthan V N 
18753b72c2feSMugunthan V N 	dev_info(priv->dev, "removing vlanid %d from vlan filter\n", vid);
18762a05a622SIvan Khoronzhuk 	ret = cpsw_ale_del_vlan(cpsw->ale, vid, 0);
1877be35b982SIvan Khoronzhuk 	ret |= cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr,
187861f1cef9SGrygorii Strashko 				  HOST_PORT_NUM, ALE_VLAN, vid);
1879be35b982SIvan Khoronzhuk 	ret |= cpsw_ale_del_mcast(cpsw->ale, priv->ndev->broadcast,
18803b72c2feSMugunthan V N 				  0, ALE_VLAN, vid);
188115180ecaSIvan Khoronzhuk 	ret |= cpsw_ale_flush_multicast(cpsw->ale, 0, vid);
1882803c4f64SIvan Khoronzhuk err:
188356e31bd8SIvan Khoronzhuk 	pm_runtime_put(cpsw->dev);
1884a6c5d14fSGrygorii Strashko 	return ret;
18853b72c2feSMugunthan V N }
18863b72c2feSMugunthan V N 
188783fcad0cSIvan Khoronzhuk static int cpsw_ndo_set_tx_maxrate(struct net_device *ndev, int queue, u32 rate)
188883fcad0cSIvan Khoronzhuk {
188983fcad0cSIvan Khoronzhuk 	struct cpsw_priv *priv = netdev_priv(ndev);
189083fcad0cSIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
189152986a2fSIvan Khoronzhuk 	struct cpsw_slave *slave;
189232b78d85SIvan Khoronzhuk 	u32 min_rate;
189383fcad0cSIvan Khoronzhuk 	u32 ch_rate;
189452986a2fSIvan Khoronzhuk 	int i, ret;
189583fcad0cSIvan Khoronzhuk 
189683fcad0cSIvan Khoronzhuk 	ch_rate = netdev_get_tx_queue(ndev, queue)->tx_maxrate;
189783fcad0cSIvan Khoronzhuk 	if (ch_rate == rate)
189883fcad0cSIvan Khoronzhuk 		return 0;
189983fcad0cSIvan Khoronzhuk 
190032b78d85SIvan Khoronzhuk 	ch_rate = rate * 1000;
190183fcad0cSIvan Khoronzhuk 	min_rate = cpdma_chan_get_min_rate(cpsw->dma);
190232b78d85SIvan Khoronzhuk 	if ((ch_rate < min_rate && ch_rate)) {
190332b78d85SIvan Khoronzhuk 		dev_err(priv->dev, "The channel rate cannot be less than %dMbps",
190483fcad0cSIvan Khoronzhuk 			min_rate);
190583fcad0cSIvan Khoronzhuk 		return -EINVAL;
190683fcad0cSIvan Khoronzhuk 	}
190783fcad0cSIvan Khoronzhuk 
19080be01b8eSIvan Khoronzhuk 	if (rate > cpsw->speed) {
190932b78d85SIvan Khoronzhuk 		dev_err(priv->dev, "The channel rate cannot be more than 2Gbps");
191032b78d85SIvan Khoronzhuk 		return -EINVAL;
191132b78d85SIvan Khoronzhuk 	}
191232b78d85SIvan Khoronzhuk 
191383fcad0cSIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
191483fcad0cSIvan Khoronzhuk 	if (ret < 0) {
191583fcad0cSIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
191683fcad0cSIvan Khoronzhuk 		return ret;
191783fcad0cSIvan Khoronzhuk 	}
191883fcad0cSIvan Khoronzhuk 
191932b78d85SIvan Khoronzhuk 	ret = cpdma_chan_set_rate(cpsw->txv[queue].ch, ch_rate);
192083fcad0cSIvan Khoronzhuk 	pm_runtime_put(cpsw->dev);
192132b78d85SIvan Khoronzhuk 
192232b78d85SIvan Khoronzhuk 	if (ret)
192332b78d85SIvan Khoronzhuk 		return ret;
192432b78d85SIvan Khoronzhuk 
192552986a2fSIvan Khoronzhuk 	/* update rates for slaves tx queues */
192652986a2fSIvan Khoronzhuk 	for (i = 0; i < cpsw->data.slaves; i++) {
192752986a2fSIvan Khoronzhuk 		slave = &cpsw->slaves[i];
192852986a2fSIvan Khoronzhuk 		if (!slave->ndev)
192952986a2fSIvan Khoronzhuk 			continue;
193052986a2fSIvan Khoronzhuk 
193152986a2fSIvan Khoronzhuk 		netdev_get_tx_queue(slave->ndev, queue)->tx_maxrate = rate;
193252986a2fSIvan Khoronzhuk 	}
193352986a2fSIvan Khoronzhuk 
19349763a891SGrygorii Strashko 	cpsw_split_res(cpsw);
193583fcad0cSIvan Khoronzhuk 	return ret;
193683fcad0cSIvan Khoronzhuk }
193783fcad0cSIvan Khoronzhuk 
19387929a668SIvan Khoronzhuk static int cpsw_set_mqprio(struct net_device *ndev, void *type_data)
19397929a668SIvan Khoronzhuk {
19407929a668SIvan Khoronzhuk 	struct tc_mqprio_qopt_offload *mqprio = type_data;
19417929a668SIvan Khoronzhuk 	struct cpsw_priv *priv = netdev_priv(ndev);
19427929a668SIvan Khoronzhuk 	struct cpsw_common *cpsw = priv->cpsw;
19437929a668SIvan Khoronzhuk 	int fifo, num_tc, count, offset;
19447929a668SIvan Khoronzhuk 	struct cpsw_slave *slave;
19457929a668SIvan Khoronzhuk 	u32 tx_prio_map = 0;
19467929a668SIvan Khoronzhuk 	int i, tc, ret;
19477929a668SIvan Khoronzhuk 
19487929a668SIvan Khoronzhuk 	num_tc = mqprio->qopt.num_tc;
19497929a668SIvan Khoronzhuk 	if (num_tc > CPSW_TC_NUM)
19507929a668SIvan Khoronzhuk 		return -EINVAL;
19517929a668SIvan Khoronzhuk 
19527929a668SIvan Khoronzhuk 	if (mqprio->mode != TC_MQPRIO_MODE_DCB)
19537929a668SIvan Khoronzhuk 		return -EINVAL;
19547929a668SIvan Khoronzhuk 
19557929a668SIvan Khoronzhuk 	ret = pm_runtime_get_sync(cpsw->dev);
19567929a668SIvan Khoronzhuk 	if (ret < 0) {
19577929a668SIvan Khoronzhuk 		pm_runtime_put_noidle(cpsw->dev);
19587929a668SIvan Khoronzhuk 		return ret;
19597929a668SIvan Khoronzhuk 	}
19607929a668SIvan Khoronzhuk 
19617929a668SIvan Khoronzhuk 	if (num_tc) {
19627929a668SIvan Khoronzhuk 		for (i = 0; i < 8; i++) {
19637929a668SIvan Khoronzhuk 			tc = mqprio->qopt.prio_tc_map[i];
19647929a668SIvan Khoronzhuk 			fifo = cpsw_tc_to_fifo(tc, num_tc);
19657929a668SIvan Khoronzhuk 			tx_prio_map |= fifo << (4 * i);
19667929a668SIvan Khoronzhuk 		}
19677929a668SIvan Khoronzhuk 
19687929a668SIvan Khoronzhuk 		netdev_set_num_tc(ndev, num_tc);
19697929a668SIvan Khoronzhuk 		for (i = 0; i < num_tc; i++) {
19707929a668SIvan Khoronzhuk 			count = mqprio->qopt.count[i];
19717929a668SIvan Khoronzhuk 			offset = mqprio->qopt.offset[i];
19727929a668SIvan Khoronzhuk 			netdev_set_tc_queue(ndev, i, count, offset);
19737929a668SIvan Khoronzhuk 		}
19747929a668SIvan Khoronzhuk 	}
19757929a668SIvan Khoronzhuk 
19767929a668SIvan Khoronzhuk 	if (!mqprio->qopt.hw) {
19777929a668SIvan Khoronzhuk 		/* restore default configuration */
19787929a668SIvan Khoronzhuk 		netdev_reset_tc(ndev);
19797929a668SIvan Khoronzhuk 		tx_prio_map = TX_PRIORITY_MAPPING;
19807929a668SIvan Khoronzhuk 	}
19817929a668SIvan Khoronzhuk 
19827929a668SIvan Khoronzhuk 	priv->mqprio_hw = mqprio->qopt.hw;
19837929a668SIvan Khoronzhuk 
19847929a668SIvan Khoronzhuk 	offset = cpsw->version == CPSW_VERSION_1 ?
19857929a668SIvan Khoronzhuk 		 CPSW1_TX_PRI_MAP : CPSW2_TX_PRI_MAP;
19867929a668SIvan Khoronzhuk 
19877929a668SIvan Khoronzhuk 	slave = &cpsw->slaves[cpsw_slave_index(cpsw, priv)];
19887929a668SIvan Khoronzhuk 	slave_write(slave, tx_prio_map, offset);
19897929a668SIvan Khoronzhuk 
19907929a668SIvan Khoronzhuk 	pm_runtime_put_sync(cpsw->dev);
19917929a668SIvan Khoronzhuk 
19927929a668SIvan Khoronzhuk 	return 0;
19937929a668SIvan Khoronzhuk }
19947929a668SIvan Khoronzhuk 
19957929a668SIvan Khoronzhuk static int cpsw_ndo_setup_tc(struct net_device *ndev, enum tc_setup_type type,
19967929a668SIvan Khoronzhuk 			     void *type_data)
19977929a668SIvan Khoronzhuk {
19987929a668SIvan Khoronzhuk 	switch (type) {
199957d90148SIvan Khoronzhuk 	case TC_SETUP_QDISC_CBS:
200057d90148SIvan Khoronzhuk 		return cpsw_set_cbs(ndev, type_data);
200157d90148SIvan Khoronzhuk 
20027929a668SIvan Khoronzhuk 	case TC_SETUP_QDISC_MQPRIO:
20037929a668SIvan Khoronzhuk 		return cpsw_set_mqprio(ndev, type_data);
20047929a668SIvan Khoronzhuk 
20057929a668SIvan Khoronzhuk 	default:
20067929a668SIvan Khoronzhuk 		return -EOPNOTSUPP;
20077929a668SIvan Khoronzhuk 	}
20087929a668SIvan Khoronzhuk }
20097929a668SIvan Khoronzhuk 
2010026cc9c3SDavid S. Miller #ifdef CONFIG_NET_POLL_CONTROLLER
2011026cc9c3SDavid S. Miller static void cpsw_ndo_poll_controller(struct net_device *ndev)
2012026cc9c3SDavid S. Miller {
2013026cc9c3SDavid S. Miller 	struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
2014026cc9c3SDavid S. Miller 
2015026cc9c3SDavid S. Miller 	cpsw_intr_disable(cpsw);
2016026cc9c3SDavid S. Miller 	cpsw_rx_interrupt(cpsw->irqs_table[0], cpsw);
2017026cc9c3SDavid S. Miller 	cpsw_tx_interrupt(cpsw->irqs_table[1], cpsw);
2018026cc9c3SDavid S. Miller 	cpsw_intr_enable(cpsw);
2019026cc9c3SDavid S. Miller }
2020026cc9c3SDavid S. Miller #endif
2021026cc9c3SDavid S. Miller 
2022df828598SMugunthan V N static const struct net_device_ops cpsw_netdev_ops = {
2023df828598SMugunthan V N 	.ndo_open		= cpsw_ndo_open,
2024df828598SMugunthan V N 	.ndo_stop		= cpsw_ndo_stop,
2025df828598SMugunthan V N 	.ndo_start_xmit		= cpsw_ndo_start_xmit,
2026dcfd8d58SMugunthan V N 	.ndo_set_mac_address	= cpsw_ndo_set_mac_address,
20272e5b38abSRichard Cochran 	.ndo_do_ioctl		= cpsw_ndo_ioctl,
2028df828598SMugunthan V N 	.ndo_validate_addr	= eth_validate_addr,
2029df828598SMugunthan V N 	.ndo_tx_timeout		= cpsw_ndo_tx_timeout,
20305c50a856SMugunthan V N 	.ndo_set_rx_mode	= cpsw_ndo_set_rx_mode,
203183fcad0cSIvan Khoronzhuk 	.ndo_set_tx_maxrate	= cpsw_ndo_set_tx_maxrate,
2032df828598SMugunthan V N #ifdef CONFIG_NET_POLL_CONTROLLER
2033df828598SMugunthan V N 	.ndo_poll_controller	= cpsw_ndo_poll_controller,
2034df828598SMugunthan V N #endif
20353b72c2feSMugunthan V N 	.ndo_vlan_rx_add_vid	= cpsw_ndo_vlan_rx_add_vid,
20363b72c2feSMugunthan V N 	.ndo_vlan_rx_kill_vid	= cpsw_ndo_vlan_rx_kill_vid,
20377929a668SIvan Khoronzhuk 	.ndo_setup_tc           = cpsw_ndo_setup_tc,
2038df828598SMugunthan V N };
2039df828598SMugunthan V N 
2040df828598SMugunthan V N static void cpsw_get_drvinfo(struct net_device *ndev,
2041df828598SMugunthan V N 			     struct ethtool_drvinfo *info)
2042df828598SMugunthan V N {
2043649a1688SIvan Khoronzhuk 	struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
204456e31bd8SIvan Khoronzhuk 	struct platform_device	*pdev = to_platform_device(cpsw->dev);
20457826d43fSJiri Pirko 
204652c4f0ecSMugunthan V N 	strlcpy(info->driver, "cpsw", sizeof(info->driver));
20477826d43fSJiri Pirko 	strlcpy(info->version, "1.0", sizeof(info->version));
204856e31bd8SIvan Khoronzhuk 	strlcpy(info->bus_info, pdev->name, sizeof(info->bus_info));
2049df828598SMugunthan V N }
2050df828598SMugunthan V N 
20511923d6e4SMugunthan V N static int cpsw_set_pauseparam(struct net_device *ndev,
20521923d6e4SMugunthan V N 			       struct ethtool_pauseparam *pause)
20531923d6e4SMugunthan V N {
20541923d6e4SMugunthan V N 	struct cpsw_priv *priv = netdev_priv(ndev);
20551923d6e4SMugunthan V N 	bool link;
20561923d6e4SMugunthan V N 
20571923d6e4SMugunthan V N 	priv->rx_pause = pause->rx_pause ? true : false;
20581923d6e4SMugunthan V N 	priv->tx_pause = pause->tx_pause ? true : false;
20591923d6e4SMugunthan V N 
20601923d6e4SMugunthan V N 	for_each_slave(priv, _cpsw_adjust_link, priv, &link);
20611923d6e4SMugunthan V N 	return 0;
20621923d6e4SMugunthan V N }
20631923d6e4SMugunthan V N 
2064022d7ad7SIvan Khoronzhuk static int cpsw_set_channels(struct net_device *ndev,
2065022d7ad7SIvan Khoronzhuk 			     struct ethtool_channels *chs)
2066022d7ad7SIvan Khoronzhuk {
2067c24eef28SGrygorii Strashko 	return cpsw_set_channels_common(ndev, chs, cpsw_rx_handler);
2068be034fc1SGrygorii Strashko }
2069be034fc1SGrygorii Strashko 
2070df828598SMugunthan V N static const struct ethtool_ops cpsw_ethtool_ops = {
2071df828598SMugunthan V N 	.get_drvinfo	= cpsw_get_drvinfo,
2072df828598SMugunthan V N 	.get_msglevel	= cpsw_get_msglevel,
2073df828598SMugunthan V N 	.set_msglevel	= cpsw_set_msglevel,
2074df828598SMugunthan V N 	.get_link	= ethtool_op_get_link,
20752e5b38abSRichard Cochran 	.get_ts_info	= cpsw_get_ts_info,
2076ff5b8ef2SMugunthan V N 	.get_coalesce	= cpsw_get_coalesce,
2077ff5b8ef2SMugunthan V N 	.set_coalesce	= cpsw_set_coalesce,
2078d9718546SMugunthan V N 	.get_sset_count		= cpsw_get_sset_count,
2079d9718546SMugunthan V N 	.get_strings		= cpsw_get_strings,
2080d9718546SMugunthan V N 	.get_ethtool_stats	= cpsw_get_ethtool_stats,
20811923d6e4SMugunthan V N 	.get_pauseparam		= cpsw_get_pauseparam,
20821923d6e4SMugunthan V N 	.set_pauseparam		= cpsw_set_pauseparam,
2083d8a64420SMatus Ujhelyi 	.get_wol	= cpsw_get_wol,
2084d8a64420SMatus Ujhelyi 	.set_wol	= cpsw_set_wol,
208552c4f0ecSMugunthan V N 	.get_regs_len	= cpsw_get_regs_len,
208652c4f0ecSMugunthan V N 	.get_regs	= cpsw_get_regs,
20877898b1daSGrygorii Strashko 	.begin		= cpsw_ethtool_op_begin,
20887898b1daSGrygorii Strashko 	.complete	= cpsw_ethtool_op_complete,
2089ce52c744SIvan Khoronzhuk 	.get_channels	= cpsw_get_channels,
2090ce52c744SIvan Khoronzhuk 	.set_channels	= cpsw_set_channels,
20912479876dSPhilippe Reynes 	.get_link_ksettings	= cpsw_get_link_ksettings,
20922479876dSPhilippe Reynes 	.set_link_ksettings	= cpsw_set_link_ksettings,
2093a0909949SYegor Yefremov 	.get_eee	= cpsw_get_eee,
2094a0909949SYegor Yefremov 	.set_eee	= cpsw_set_eee,
20956bb10c2bSYegor Yefremov 	.nway_reset	= cpsw_nway_reset,
2096be034fc1SGrygorii Strashko 	.get_ringparam = cpsw_get_ringparam,
2097be034fc1SGrygorii Strashko 	.set_ringparam = cpsw_set_ringparam,
2098df828598SMugunthan V N };
2099df828598SMugunthan V N 
2100552165bcSDavid Rivshin static int cpsw_probe_dt(struct cpsw_platform_data *data,
21012eb32b0aSMugunthan V N 			 struct platform_device *pdev)
21022eb32b0aSMugunthan V N {
21032eb32b0aSMugunthan V N 	struct device_node *node = pdev->dev.of_node;
21042eb32b0aSMugunthan V N 	struct device_node *slave_node;
21052eb32b0aSMugunthan V N 	int i = 0, ret;
21062eb32b0aSMugunthan V N 	u32 prop;
21072eb32b0aSMugunthan V N 
21082eb32b0aSMugunthan V N 	if (!node)
21092eb32b0aSMugunthan V N 		return -EINVAL;
21102eb32b0aSMugunthan V N 
21112eb32b0aSMugunthan V N 	if (of_property_read_u32(node, "slaves", &prop)) {
211288c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing slaves property in the DT.\n");
21132eb32b0aSMugunthan V N 		return -EINVAL;
21142eb32b0aSMugunthan V N 	}
21152eb32b0aSMugunthan V N 	data->slaves = prop;
21162eb32b0aSMugunthan V N 
2117e86ac13bSMugunthan V N 	if (of_property_read_u32(node, "active_slave", &prop)) {
211888c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing active_slave property in the DT.\n");
2119aa1a15e2SDaniel Mack 		return -EINVAL;
212078ca0b28SRichard Cochran 	}
2121e86ac13bSMugunthan V N 	data->active_slave = prop;
212278ca0b28SRichard Cochran 
2123a86854d0SKees Cook 	data->slave_data = devm_kcalloc(&pdev->dev,
2124a86854d0SKees Cook 					data->slaves,
2125a86854d0SKees Cook 					sizeof(struct cpsw_slave_data),
2126b2adaca9SJoe Perches 					GFP_KERNEL);
2127b2adaca9SJoe Perches 	if (!data->slave_data)
2128aa1a15e2SDaniel Mack 		return -ENOMEM;
21292eb32b0aSMugunthan V N 
21302eb32b0aSMugunthan V N 	if (of_property_read_u32(node, "cpdma_channels", &prop)) {
213188c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing cpdma_channels property in the DT.\n");
2132aa1a15e2SDaniel Mack 		return -EINVAL;
21332eb32b0aSMugunthan V N 	}
21342eb32b0aSMugunthan V N 	data->channels = prop;
21352eb32b0aSMugunthan V N 
21362eb32b0aSMugunthan V N 	if (of_property_read_u32(node, "ale_entries", &prop)) {
213788c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing ale_entries property in the DT.\n");
2138aa1a15e2SDaniel Mack 		return -EINVAL;
21392eb32b0aSMugunthan V N 	}
21402eb32b0aSMugunthan V N 	data->ale_entries = prop;
21412eb32b0aSMugunthan V N 
21422eb32b0aSMugunthan V N 	if (of_property_read_u32(node, "bd_ram_size", &prop)) {
214388c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing bd_ram_size property in the DT.\n");
2144aa1a15e2SDaniel Mack 		return -EINVAL;
21452eb32b0aSMugunthan V N 	}
21462eb32b0aSMugunthan V N 	data->bd_ram_size = prop;
21472eb32b0aSMugunthan V N 
21482eb32b0aSMugunthan V N 	if (of_property_read_u32(node, "mac_control", &prop)) {
214988c99ff6SGeorge Cherian 		dev_err(&pdev->dev, "Missing mac_control property in the DT.\n");
2150aa1a15e2SDaniel Mack 		return -EINVAL;
21512eb32b0aSMugunthan V N 	}
21522eb32b0aSMugunthan V N 	data->mac_control = prop;
21532eb32b0aSMugunthan V N 
2154281abd96SMarkus Pargmann 	if (of_property_read_bool(node, "dual_emac"))
2155281abd96SMarkus Pargmann 		data->dual_emac = 1;
2156d9ba8f9eSMugunthan V N 
21571fb19aa7SVaibhav Hiremath 	/*
21581fb19aa7SVaibhav Hiremath 	 * Populate all the child nodes here...
21591fb19aa7SVaibhav Hiremath 	 */
21601fb19aa7SVaibhav Hiremath 	ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
21611fb19aa7SVaibhav Hiremath 	/* We do not want to force this, as in some cases may not have child */
21621fb19aa7SVaibhav Hiremath 	if (ret)
216388c99ff6SGeorge Cherian 		dev_warn(&pdev->dev, "Doesn't have any child node\n");
21641fb19aa7SVaibhav Hiremath 
21658658aaf2SBen Hutchings 	for_each_available_child_of_node(node, slave_node) {
2166549985eeSRichard Cochran 		struct cpsw_slave_data *slave_data = data->slave_data + i;
2167549985eeSRichard Cochran 		const void *mac_addr = NULL;
2168549985eeSRichard Cochran 		int lenp;
2169549985eeSRichard Cochran 		const __be32 *parp;
2170549985eeSRichard Cochran 
2171f468b10eSMarkus Pargmann 		/* This is no slave child node, continue */
2172bf5849f1SRob Herring 		if (!of_node_name_eq(slave_node, "slave"))
2173f468b10eSMarkus Pargmann 			continue;
2174f468b10eSMarkus Pargmann 
21753ff18849SGrygorii Strashko 		slave_data->ifphy = devm_of_phy_get(&pdev->dev, slave_node,
21763ff18849SGrygorii Strashko 						    NULL);
21773ff18849SGrygorii Strashko 		if (!IS_ENABLED(CONFIG_TI_CPSW_PHY_SEL) &&
21783ff18849SGrygorii Strashko 		    IS_ERR(slave_data->ifphy)) {
21793ff18849SGrygorii Strashko 			ret = PTR_ERR(slave_data->ifphy);
21803ff18849SGrygorii Strashko 			dev_err(&pdev->dev,
21813ff18849SGrygorii Strashko 				"%d: Error retrieving port phy: %d\n", i, ret);
21823ff18849SGrygorii Strashko 			return ret;
21833ff18849SGrygorii Strashko 		}
21843ff18849SGrygorii Strashko 
2185552165bcSDavid Rivshin 		slave_data->phy_node = of_parse_phandle(slave_node,
2186552165bcSDavid Rivshin 							"phy-handle", 0);
2187f1eea5c1SDavid Rivshin 		parp = of_get_property(slave_node, "phy_id", &lenp);
2188ae092b5bSDavid Rivshin 		if (slave_data->phy_node) {
2189ae092b5bSDavid Rivshin 			dev_dbg(&pdev->dev,
2190f7ce9103SRob Herring 				"slave[%d] using phy-handle=\"%pOF\"\n",
2191f7ce9103SRob Herring 				i, slave_data->phy_node);
2192ae092b5bSDavid Rivshin 		} else if (of_phy_is_fixed_link(slave_node)) {
2193dfc0a6d3SDavid Rivshin 			/* In the case of a fixed PHY, the DT node associated
2194dfc0a6d3SDavid Rivshin 			 * to the PHY is the Ethernet MAC DT node.
2195dfc0a6d3SDavid Rivshin 			 */
21961f71e8c9SMarkus Brunner 			ret = of_phy_register_fixed_link(slave_node);
219723a09873SJohan Hovold 			if (ret) {
219823a09873SJohan Hovold 				if (ret != -EPROBE_DEFER)
219923a09873SJohan Hovold 					dev_err(&pdev->dev, "failed to register fixed-link phy: %d\n", ret);
22001f71e8c9SMarkus Brunner 				return ret;
220123a09873SJohan Hovold 			}
220206cd6d6eSDavid Rivshin 			slave_data->phy_node = of_node_get(slave_node);
2203f1eea5c1SDavid Rivshin 		} else if (parp) {
2204f1eea5c1SDavid Rivshin 			u32 phyid;
2205f1eea5c1SDavid Rivshin 			struct device_node *mdio_node;
2206f1eea5c1SDavid Rivshin 			struct platform_device *mdio;
2207f1eea5c1SDavid Rivshin 
2208f1eea5c1SDavid Rivshin 			if (lenp != (sizeof(__be32) * 2)) {
2209f1eea5c1SDavid Rivshin 				dev_err(&pdev->dev, "Invalid slave[%d] phy_id property\n", i);
221047276fccSMugunthan V N 				goto no_phy_slave;
2211549985eeSRichard Cochran 			}
2212549985eeSRichard Cochran 			mdio_node = of_find_node_by_phandle(be32_to_cpup(parp));
2213549985eeSRichard Cochran 			phyid = be32_to_cpup(parp+1);
2214549985eeSRichard Cochran 			mdio = of_find_device_by_node(mdio_node);
221560e71ab5SJohan Hovold 			of_node_put(mdio_node);
22166954cc1fSJohan Hovold 			if (!mdio) {
221756fdb2e0SMarkus Pargmann 				dev_err(&pdev->dev, "Missing mdio platform device\n");
22186954cc1fSJohan Hovold 				return -EINVAL;
22196954cc1fSJohan Hovold 			}
2220549985eeSRichard Cochran 			snprintf(slave_data->phy_id, sizeof(slave_data->phy_id),
2221549985eeSRichard Cochran 				 PHY_ID_FMT, mdio->name, phyid);
222286e1d5adSJohan Hovold 			put_device(&mdio->dev);
2223f1eea5c1SDavid Rivshin 		} else {
2224ae092b5bSDavid Rivshin 			dev_err(&pdev->dev,
2225ae092b5bSDavid Rivshin 				"No slave[%d] phy_id, phy-handle, or fixed-link property\n",
2226ae092b5bSDavid Rivshin 				i);
2227f1eea5c1SDavid Rivshin 			goto no_phy_slave;
2228f1eea5c1SDavid Rivshin 		}
222947276fccSMugunthan V N 		slave_data->phy_if = of_get_phy_mode(slave_node);
223047276fccSMugunthan V N 		if (slave_data->phy_if < 0) {
223147276fccSMugunthan V N 			dev_err(&pdev->dev, "Missing or malformed slave[%d] phy-mode property\n",
223247276fccSMugunthan V N 				i);
223347276fccSMugunthan V N 			return slave_data->phy_if;
223447276fccSMugunthan V N 		}
223547276fccSMugunthan V N 
223647276fccSMugunthan V N no_phy_slave:
2237549985eeSRichard Cochran 		mac_addr = of_get_mac_address(slave_node);
2238a51645f7SPetr Štetiar 		if (!IS_ERR(mac_addr)) {
22392d2924afSPetr Štetiar 			ether_addr_copy(slave_data->mac_addr, mac_addr);
22400ba517b1SMarkus Pargmann 		} else {
2241b6745f6eSMugunthan V N 			ret = ti_cm_get_macid(&pdev->dev, i,
22420ba517b1SMarkus Pargmann 					      slave_data->mac_addr);
22430ba517b1SMarkus Pargmann 			if (ret)
22440ba517b1SMarkus Pargmann 				return ret;
22450ba517b1SMarkus Pargmann 		}
2246d9ba8f9eSMugunthan V N 		if (data->dual_emac) {
224791c4166cSMugunthan V N 			if (of_property_read_u32(slave_node, "dual_emac_res_vlan",
2248d9ba8f9eSMugunthan V N 						 &prop)) {
224988c99ff6SGeorge Cherian 				dev_err(&pdev->dev, "Missing dual_emac_res_vlan in DT.\n");
2250d9ba8f9eSMugunthan V N 				slave_data->dual_emac_res_vlan = i+1;
225188c99ff6SGeorge Cherian 				dev_err(&pdev->dev, "Using %d as Reserved VLAN for %d slave\n",
2252d9ba8f9eSMugunthan V N 					slave_data->dual_emac_res_vlan, i);
2253d9ba8f9eSMugunthan V N 			} else {
2254d9ba8f9eSMugunthan V N 				slave_data->dual_emac_res_vlan = prop;
2255d9ba8f9eSMugunthan V N 			}
2256d9ba8f9eSMugunthan V N 		}
2257d9ba8f9eSMugunthan V N 
2258549985eeSRichard Cochran 		i++;
22593a27bfacSMugunthan V N 		if (i == data->slaves)
22603a27bfacSMugunthan V N 			break;
2261549985eeSRichard Cochran 	}
2262549985eeSRichard Cochran 
22632eb32b0aSMugunthan V N 	return 0;
22642eb32b0aSMugunthan V N }
22652eb32b0aSMugunthan V N 
2266a4e32b0dSJohan Hovold static void cpsw_remove_dt(struct platform_device *pdev)
2267a4e32b0dSJohan Hovold {
2268bfe59032SIvan Khoronzhuk 	struct cpsw_common *cpsw = platform_get_drvdata(pdev);
22698cbcc466SJohan Hovold 	struct cpsw_platform_data *data = &cpsw->data;
22708cbcc466SJohan Hovold 	struct device_node *node = pdev->dev.of_node;
22718cbcc466SJohan Hovold 	struct device_node *slave_node;
22728cbcc466SJohan Hovold 	int i = 0;
22738cbcc466SJohan Hovold 
22748cbcc466SJohan Hovold 	for_each_available_child_of_node(node, slave_node) {
22758cbcc466SJohan Hovold 		struct cpsw_slave_data *slave_data = &data->slave_data[i];
22768cbcc466SJohan Hovold 
2277bf5849f1SRob Herring 		if (!of_node_name_eq(slave_node, "slave"))
22788cbcc466SJohan Hovold 			continue;
22798cbcc466SJohan Hovold 
22803f65047cSJohan Hovold 		if (of_phy_is_fixed_link(slave_node))
22813f65047cSJohan Hovold 			of_phy_deregister_fixed_link(slave_node);
22828cbcc466SJohan Hovold 
22838cbcc466SJohan Hovold 		of_node_put(slave_data->phy_node);
22848cbcc466SJohan Hovold 
22858cbcc466SJohan Hovold 		i++;
22868cbcc466SJohan Hovold 		if (i == data->slaves)
22878cbcc466SJohan Hovold 			break;
22888cbcc466SJohan Hovold 	}
22898cbcc466SJohan Hovold 
2290a4e32b0dSJohan Hovold 	of_platform_depopulate(&pdev->dev);
2291a4e32b0dSJohan Hovold }
2292a4e32b0dSJohan Hovold 
229356e31bd8SIvan Khoronzhuk static int cpsw_probe_dual_emac(struct cpsw_priv *priv)
2294d9ba8f9eSMugunthan V N {
2295606f3993SIvan Khoronzhuk 	struct cpsw_common		*cpsw = priv->cpsw;
2296606f3993SIvan Khoronzhuk 	struct cpsw_platform_data	*data = &cpsw->data;
2297d9ba8f9eSMugunthan V N 	struct net_device		*ndev;
2298d9ba8f9eSMugunthan V N 	struct cpsw_priv		*priv_sl2;
2299e38b5a3dSIvan Khoronzhuk 	int ret = 0;
2300d9ba8f9eSMugunthan V N 
2301d183a942SGrygorii Strashko 	ndev = devm_alloc_etherdev_mqs(cpsw->dev, sizeof(struct cpsw_priv),
2302d183a942SGrygorii Strashko 				       CPSW_MAX_QUEUES, CPSW_MAX_QUEUES);
2303d9ba8f9eSMugunthan V N 	if (!ndev) {
230456e31bd8SIvan Khoronzhuk 		dev_err(cpsw->dev, "cpsw: error allocating net_device\n");
2305d9ba8f9eSMugunthan V N 		return -ENOMEM;
2306d9ba8f9eSMugunthan V N 	}
2307d9ba8f9eSMugunthan V N 
2308d9ba8f9eSMugunthan V N 	priv_sl2 = netdev_priv(ndev);
2309606f3993SIvan Khoronzhuk 	priv_sl2->cpsw = cpsw;
2310d9ba8f9eSMugunthan V N 	priv_sl2->ndev = ndev;
2311d9ba8f9eSMugunthan V N 	priv_sl2->dev  = &ndev->dev;
2312d9ba8f9eSMugunthan V N 	priv_sl2->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
2313d9ba8f9eSMugunthan V N 
2314d9ba8f9eSMugunthan V N 	if (is_valid_ether_addr(data->slave_data[1].mac_addr)) {
2315d9ba8f9eSMugunthan V N 		memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr,
2316d9ba8f9eSMugunthan V N 			ETH_ALEN);
231756e31bd8SIvan Khoronzhuk 		dev_info(cpsw->dev, "cpsw: Detected MACID = %pM\n",
231856e31bd8SIvan Khoronzhuk 			 priv_sl2->mac_addr);
2319d9ba8f9eSMugunthan V N 	} else {
23206c1f0a1fSJoe Perches 		eth_random_addr(priv_sl2->mac_addr);
232156e31bd8SIvan Khoronzhuk 		dev_info(cpsw->dev, "cpsw: Random MACID = %pM\n",
232256e31bd8SIvan Khoronzhuk 			 priv_sl2->mac_addr);
2323d9ba8f9eSMugunthan V N 	}
2324d9ba8f9eSMugunthan V N 	memcpy(ndev->dev_addr, priv_sl2->mac_addr, ETH_ALEN);
2325d9ba8f9eSMugunthan V N 
2326d9ba8f9eSMugunthan V N 	priv_sl2->emac_port = 1;
2327606f3993SIvan Khoronzhuk 	cpsw->slaves[1].ndev = ndev;
2328193736c8SIvan Khoronzhuk 	ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX;
2329d9ba8f9eSMugunthan V N 
2330d9ba8f9eSMugunthan V N 	ndev->netdev_ops = &cpsw_netdev_ops;
23317ad24ea4SWilfried Klaebe 	ndev->ethtool_ops = &cpsw_ethtool_ops;
2332d9ba8f9eSMugunthan V N 
2333d9ba8f9eSMugunthan V N 	/* register the network device */
233456e31bd8SIvan Khoronzhuk 	SET_NETDEV_DEV(ndev, cpsw->dev);
2335d9ba8f9eSMugunthan V N 	ret = register_netdev(ndev);
2336d183a942SGrygorii Strashko 	if (ret)
233756e31bd8SIvan Khoronzhuk 		dev_err(cpsw->dev, "cpsw: error registering net device\n");
2338d9ba8f9eSMugunthan V N 
2339d9ba8f9eSMugunthan V N 	return ret;
2340d9ba8f9eSMugunthan V N }
2341d9ba8f9eSMugunthan V N 
23427da11600SMugunthan V N static const struct of_device_id cpsw_of_mtable[] = {
23439611d6d6SIvan Khoronzhuk 	{ .compatible = "ti,cpsw"},
23449611d6d6SIvan Khoronzhuk 	{ .compatible = "ti,am335x-cpsw"},
23459611d6d6SIvan Khoronzhuk 	{ .compatible = "ti,am4372-cpsw"},
23469611d6d6SIvan Khoronzhuk 	{ .compatible = "ti,dra7-cpsw"},
23477da11600SMugunthan V N 	{ /* sentinel */ },
23487da11600SMugunthan V N };
23497da11600SMugunthan V N MODULE_DEVICE_TABLE(of, cpsw_of_mtable);
23507da11600SMugunthan V N 
23519611d6d6SIvan Khoronzhuk static const struct soc_device_attribute cpsw_soc_devices[] = {
23529611d6d6SIvan Khoronzhuk 	{ .family = "AM33xx", .revision = "ES1.0"},
23539611d6d6SIvan Khoronzhuk 	{ /* sentinel */ }
23549611d6d6SIvan Khoronzhuk };
23559611d6d6SIvan Khoronzhuk 
2356663e12e6SBill Pemberton static int cpsw_probe(struct platform_device *pdev)
2357df828598SMugunthan V N {
2358c8fb5668SGrygorii Strashko 	struct device			*dev = &pdev->dev;
2359ef4183a1SIvan Khoronzhuk 	struct clk			*clk;
2360d1bd9acfSSebastian Siewior 	struct cpsw_platform_data	*data;
2361df828598SMugunthan V N 	struct net_device		*ndev;
2362df828598SMugunthan V N 	struct cpsw_priv		*priv;
2363aa1a15e2SDaniel Mack 	void __iomem			*ss_regs;
2364aa1a15e2SDaniel Mack 	struct resource			*res, *ss_res;
23651d147ccbSMugunthan V N 	struct gpio_descs		*mode;
23669611d6d6SIvan Khoronzhuk 	const struct soc_device_attribute *soc;
2367649a1688SIvan Khoronzhuk 	struct cpsw_common		*cpsw;
2368e6a84624SGrygorii Strashko 	int ret = 0, ch;
23695087b915SFelipe Balbi 	int irq;
2370df828598SMugunthan V N 
2371c8fb5668SGrygorii Strashko 	cpsw = devm_kzalloc(dev, sizeof(struct cpsw_common), GFP_KERNEL);
23723420ea88SJohan Hovold 	if (!cpsw)
23733420ea88SJohan Hovold 		return -ENOMEM;
23743420ea88SJohan Hovold 
2375c8fb5668SGrygorii Strashko 	cpsw->dev = dev;
2376649a1688SIvan Khoronzhuk 
2377c8fb5668SGrygorii Strashko 	mode = devm_gpiod_get_array_optional(dev, "mode", GPIOD_OUT_LOW);
23781d147ccbSMugunthan V N 	if (IS_ERR(mode)) {
23791d147ccbSMugunthan V N 		ret = PTR_ERR(mode);
2380c8fb5668SGrygorii Strashko 		dev_err(dev, "gpio request failed, ret %d\n", ret);
2381d183a942SGrygorii Strashko 		return ret;
23821d147ccbSMugunthan V N 	}
23831d147ccbSMugunthan V N 
238483a8471bSGrygorii Strashko 	clk = devm_clk_get(dev, "fck");
238583a8471bSGrygorii Strashko 	if (IS_ERR(clk)) {
2386ac97a359SYueHaibing 		ret = PTR_ERR(clk);
238783a8471bSGrygorii Strashko 		dev_err(dev, "fck is not found %d\n", ret);
238883a8471bSGrygorii Strashko 		return ret;
238983a8471bSGrygorii Strashko 	}
239083a8471bSGrygorii Strashko 	cpsw->bus_freq_mhz = clk_get_rate(clk) / 1000000;
239183a8471bSGrygorii Strashko 
239283a8471bSGrygorii Strashko 	ss_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
239383a8471bSGrygorii Strashko 	ss_regs = devm_ioremap_resource(dev, ss_res);
239483a8471bSGrygorii Strashko 	if (IS_ERR(ss_regs))
239583a8471bSGrygorii Strashko 		return PTR_ERR(ss_regs);
239683a8471bSGrygorii Strashko 	cpsw->regs = ss_regs;
239783a8471bSGrygorii Strashko 
239883a8471bSGrygorii Strashko 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
239983a8471bSGrygorii Strashko 	cpsw->wr_regs = devm_ioremap_resource(dev, res);
240083a8471bSGrygorii Strashko 	if (IS_ERR(cpsw->wr_regs))
240183a8471bSGrygorii Strashko 		return PTR_ERR(cpsw->wr_regs);
240283a8471bSGrygorii Strashko 
240383a8471bSGrygorii Strashko 	/* RX IRQ */
240483a8471bSGrygorii Strashko 	irq = platform_get_irq(pdev, 1);
240583a8471bSGrygorii Strashko 	if (irq < 0)
240683a8471bSGrygorii Strashko 		return irq;
240783a8471bSGrygorii Strashko 	cpsw->irqs_table[0] = irq;
240883a8471bSGrygorii Strashko 
240983a8471bSGrygorii Strashko 	/* TX IRQ */
241083a8471bSGrygorii Strashko 	irq = platform_get_irq(pdev, 2);
241183a8471bSGrygorii Strashko 	if (irq < 0)
241283a8471bSGrygorii Strashko 		return irq;
241383a8471bSGrygorii Strashko 	cpsw->irqs_table[1] = irq;
241483a8471bSGrygorii Strashko 
24151fb19aa7SVaibhav Hiremath 	/*
24161fb19aa7SVaibhav Hiremath 	 * This may be required here for child devices.
24171fb19aa7SVaibhav Hiremath 	 */
2418c8fb5668SGrygorii Strashko 	pm_runtime_enable(dev);
24191fb19aa7SVaibhav Hiremath 
2420a4e32b0dSJohan Hovold 	/* Need to enable clocks with runtime PM api to access module
2421a4e32b0dSJohan Hovold 	 * registers
2422a4e32b0dSJohan Hovold 	 */
2423c8fb5668SGrygorii Strashko 	ret = pm_runtime_get_sync(dev);
2424a4e32b0dSJohan Hovold 	if (ret < 0) {
2425c8fb5668SGrygorii Strashko 		pm_runtime_put_noidle(dev);
2426aa1a15e2SDaniel Mack 		goto clean_runtime_disable_ret;
24272eb32b0aSMugunthan V N 	}
2428a4e32b0dSJohan Hovold 
242923a09873SJohan Hovold 	ret = cpsw_probe_dt(&cpsw->data, pdev);
243023a09873SJohan Hovold 	if (ret)
2431a4e32b0dSJohan Hovold 		goto clean_dt_ret;
243223a09873SJohan Hovold 
243383a8471bSGrygorii Strashko 	soc = soc_device_match(cpsw_soc_devices);
243483a8471bSGrygorii Strashko 	if (soc)
243583a8471bSGrygorii Strashko 		cpsw->quirk_irq = 1;
243683a8471bSGrygorii Strashko 
2437606f3993SIvan Khoronzhuk 	data = &cpsw->data;
2438c8fb5668SGrygorii Strashko 	cpsw->slaves = devm_kcalloc(dev,
2439a86854d0SKees Cook 				    data->slaves, sizeof(struct cpsw_slave),
2440df828598SMugunthan V N 				    GFP_KERNEL);
2441606f3993SIvan Khoronzhuk 	if (!cpsw->slaves) {
2442aa1a15e2SDaniel Mack 		ret = -ENOMEM;
2443a4e32b0dSJohan Hovold 		goto clean_dt_ret;
2444df828598SMugunthan V N 	}
2445df828598SMugunthan V N 
244683a8471bSGrygorii Strashko 	cpsw->rx_packet_max = max(rx_packet_max, CPSW_MAX_PACKET_SIZE);
2447c24eef28SGrygorii Strashko 	cpsw->descs_pool_size = descs_pool_size;
2448df828598SMugunthan V N 
2449e6a84624SGrygorii Strashko 	ret = cpsw_init_common(cpsw, ss_regs, ale_ageout,
2450e6a84624SGrygorii Strashko 			       ss_res->start + CPSW2_BD_OFFSET,
2451e6a84624SGrygorii Strashko 			       descs_pool_size);
2452e6a84624SGrygorii Strashko 	if (ret)
2453a4e32b0dSJohan Hovold 		goto clean_dt_ret;
24548a2c9a5aSGrygorii Strashko 
245583a8471bSGrygorii Strashko 	ch = cpsw->quirk_irq ? 0 : 7;
245683a8471bSGrygorii Strashko 	cpsw->txv[0].ch = cpdma_chan_create(cpsw->dma, ch, cpsw_tx_handler, 0);
245783a8471bSGrygorii Strashko 	if (IS_ERR(cpsw->txv[0].ch)) {
245883a8471bSGrygorii Strashko 		dev_err(dev, "error initializing tx dma channel\n");
245983a8471bSGrygorii Strashko 		ret = PTR_ERR(cpsw->txv[0].ch);
246083a8471bSGrygorii Strashko 		goto clean_cpts;
2461df828598SMugunthan V N 	}
2462df828598SMugunthan V N 
246383a8471bSGrygorii Strashko 	cpsw->rxv[0].ch = cpdma_chan_create(cpsw->dma, 0, cpsw_rx_handler, 1);
246483a8471bSGrygorii Strashko 	if (IS_ERR(cpsw->rxv[0].ch)) {
246583a8471bSGrygorii Strashko 		dev_err(dev, "error initializing rx dma channel\n");
246683a8471bSGrygorii Strashko 		ret = PTR_ERR(cpsw->rxv[0].ch);
246783a8471bSGrygorii Strashko 		goto clean_cpts;
246883a8471bSGrygorii Strashko 	}
246983a8471bSGrygorii Strashko 	cpsw_split_res(cpsw);
247083a8471bSGrygorii Strashko 
247183a8471bSGrygorii Strashko 	/* setup netdev */
247283a8471bSGrygorii Strashko 	ndev = devm_alloc_etherdev_mqs(dev, sizeof(struct cpsw_priv),
247383a8471bSGrygorii Strashko 				       CPSW_MAX_QUEUES, CPSW_MAX_QUEUES);
247483a8471bSGrygorii Strashko 	if (!ndev) {
247583a8471bSGrygorii Strashko 		dev_err(dev, "error allocating net_device\n");
247683a8471bSGrygorii Strashko 		goto clean_cpts;
247783a8471bSGrygorii Strashko 	}
247883a8471bSGrygorii Strashko 
2479bfe59032SIvan Khoronzhuk 	platform_set_drvdata(pdev, cpsw);
248083a8471bSGrygorii Strashko 	priv = netdev_priv(ndev);
248183a8471bSGrygorii Strashko 	priv->cpsw = cpsw;
248283a8471bSGrygorii Strashko 	priv->ndev = ndev;
248383a8471bSGrygorii Strashko 	priv->dev  = dev;
248483a8471bSGrygorii Strashko 	priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
248583a8471bSGrygorii Strashko 	priv->emac_port = 0;
248683a8471bSGrygorii Strashko 
248783a8471bSGrygorii Strashko 	if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
248883a8471bSGrygorii Strashko 		memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN);
248983a8471bSGrygorii Strashko 		dev_info(dev, "Detected MACID = %pM\n", priv->mac_addr);
249083a8471bSGrygorii Strashko 	} else {
249183a8471bSGrygorii Strashko 		eth_random_addr(priv->mac_addr);
249283a8471bSGrygorii Strashko 		dev_info(dev, "Random MACID = %pM\n", priv->mac_addr);
249383a8471bSGrygorii Strashko 	}
249483a8471bSGrygorii Strashko 
249583a8471bSGrygorii Strashko 	memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
249683a8471bSGrygorii Strashko 
249783a8471bSGrygorii Strashko 	cpsw->slaves[0].ndev = ndev;
249883a8471bSGrygorii Strashko 
2499a3a41d2fSGrygorii Strashko 	ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX;
2500070f9c65SKeerthy 
2501070f9c65SKeerthy 	ndev->netdev_ops = &cpsw_netdev_ops;
2502070f9c65SKeerthy 	ndev->ethtool_ops = &cpsw_ethtool_ops;
25039611d6d6SIvan Khoronzhuk 	netif_napi_add(ndev, &cpsw->napi_rx,
25049611d6d6SIvan Khoronzhuk 		       cpsw->quirk_irq ? cpsw_rx_poll : cpsw_rx_mq_poll,
25059611d6d6SIvan Khoronzhuk 		       CPSW_POLL_WEIGHT);
25069611d6d6SIvan Khoronzhuk 	netif_tx_napi_add(ndev, &cpsw->napi_tx,
25079611d6d6SIvan Khoronzhuk 			  cpsw->quirk_irq ? cpsw_tx_poll : cpsw_tx_mq_poll,
25089611d6d6SIvan Khoronzhuk 			  CPSW_POLL_WEIGHT);
2509070f9c65SKeerthy 
2510070f9c65SKeerthy 	/* register the network device */
2511c8fb5668SGrygorii Strashko 	SET_NETDEV_DEV(ndev, dev);
2512070f9c65SKeerthy 	ret = register_netdev(ndev);
2513070f9c65SKeerthy 	if (ret) {
2514c8fb5668SGrygorii Strashko 		dev_err(dev, "error registering net device\n");
2515070f9c65SKeerthy 		ret = -ENODEV;
251683a8471bSGrygorii Strashko 		goto clean_cpts;
2517070f9c65SKeerthy 	}
2518070f9c65SKeerthy 
2519070f9c65SKeerthy 	if (cpsw->data.dual_emac) {
2520070f9c65SKeerthy 		ret = cpsw_probe_dual_emac(priv);
2521070f9c65SKeerthy 		if (ret) {
2522070f9c65SKeerthy 			cpsw_err(priv, probe, "error probe slave 2 emac interface\n");
2523070f9c65SKeerthy 			goto clean_unregister_netdev_ret;
2524070f9c65SKeerthy 		}
2525070f9c65SKeerthy 	}
2526070f9c65SKeerthy 
2527c03abd84SFelipe Balbi 	/* Grab RX and TX IRQs. Note that we also have RX_THRESHOLD and
2528c03abd84SFelipe Balbi 	 * MISC IRQs which are always kept disabled with this driver so
2529c03abd84SFelipe Balbi 	 * we will not request them.
2530c03abd84SFelipe Balbi 	 *
2531c03abd84SFelipe Balbi 	 * If anyone wants to implement support for those, make sure to
2532c03abd84SFelipe Balbi 	 * first request and append them to irqs_table array.
2533c03abd84SFelipe Balbi 	 */
253483a8471bSGrygorii Strashko 	ret = devm_request_irq(dev, cpsw->irqs_table[0], cpsw_rx_interrupt,
2535c8fb5668SGrygorii Strashko 			       0, dev_name(dev), cpsw);
25365087b915SFelipe Balbi 	if (ret < 0) {
2537c8fb5668SGrygorii Strashko 		dev_err(dev, "error attaching irq (%d)\n", ret);
253883a8471bSGrygorii Strashko 		goto clean_unregister_netdev_ret;
2539df828598SMugunthan V N 	}
2540df828598SMugunthan V N 
25415087b915SFelipe Balbi 
254283a8471bSGrygorii Strashko 	ret = devm_request_irq(dev, cpsw->irqs_table[1], cpsw_tx_interrupt,
2543dbc4ec52SIvan Khoronzhuk 			       0, dev_name(&pdev->dev), cpsw);
25445087b915SFelipe Balbi 	if (ret < 0) {
2545c8fb5668SGrygorii Strashko 		dev_err(dev, "error attaching irq (%d)\n", ret);
254683a8471bSGrygorii Strashko 		goto clean_unregister_netdev_ret;
25475087b915SFelipe Balbi 	}
2548c2b32e58SDaniel Mack 
254990225bf0SGrygorii Strashko 	cpsw_notice(priv, probe,
255090225bf0SGrygorii Strashko 		    "initialized device (regs %pa, irq %d, pool size %d)\n",
255183a8471bSGrygorii Strashko 		    &ss_res->start, cpsw->irqs_table[0], descs_pool_size);
2552d9ba8f9eSMugunthan V N 
2553c46ab7e0SJohan Hovold 	pm_runtime_put(&pdev->dev);
2554c46ab7e0SJohan Hovold 
2555df828598SMugunthan V N 	return 0;
2556df828598SMugunthan V N 
2557a7fe9d46SJohan Hovold clean_unregister_netdev_ret:
2558a7fe9d46SJohan Hovold 	unregister_netdev(ndev);
255983a8471bSGrygorii Strashko clean_cpts:
256083a8471bSGrygorii Strashko 	cpts_release(cpsw->cpts);
25612c836bd9SIvan Khoronzhuk 	cpdma_ctlr_destroy(cpsw->dma);
2562a4e32b0dSJohan Hovold clean_dt_ret:
2563a4e32b0dSJohan Hovold 	cpsw_remove_dt(pdev);
2564c46ab7e0SJohan Hovold 	pm_runtime_put_sync(&pdev->dev);
2565aa1a15e2SDaniel Mack clean_runtime_disable_ret:
2566f150bd7fSMugunthan V N 	pm_runtime_disable(&pdev->dev);
2567df828598SMugunthan V N 	return ret;
2568df828598SMugunthan V N }
2569df828598SMugunthan V N 
2570663e12e6SBill Pemberton static int cpsw_remove(struct platform_device *pdev)
2571df828598SMugunthan V N {
2572bfe59032SIvan Khoronzhuk 	struct cpsw_common *cpsw = platform_get_drvdata(pdev);
2573bfe59032SIvan Khoronzhuk 	int i, ret;
25748a0b6dc9SGrygorii Strashko 
25758a0b6dc9SGrygorii Strashko 	ret = pm_runtime_get_sync(&pdev->dev);
25768a0b6dc9SGrygorii Strashko 	if (ret < 0) {
25778a0b6dc9SGrygorii Strashko 		pm_runtime_put_noidle(&pdev->dev);
25788a0b6dc9SGrygorii Strashko 		return ret;
25798a0b6dc9SGrygorii Strashko 	}
2580df828598SMugunthan V N 
2581bfe59032SIvan Khoronzhuk 	for (i = 0; i < cpsw->data.slaves; i++)
2582bfe59032SIvan Khoronzhuk 		if (cpsw->slaves[i].ndev)
2583bfe59032SIvan Khoronzhuk 			unregister_netdev(cpsw->slaves[i].ndev);
2584df828598SMugunthan V N 
25858a2c9a5aSGrygorii Strashko 	cpts_release(cpsw->cpts);
25862c836bd9SIvan Khoronzhuk 	cpdma_ctlr_destroy(cpsw->dma);
2587a4e32b0dSJohan Hovold 	cpsw_remove_dt(pdev);
25888a0b6dc9SGrygorii Strashko 	pm_runtime_put_sync(&pdev->dev);
25898a0b6dc9SGrygorii Strashko 	pm_runtime_disable(&pdev->dev);
2590df828598SMugunthan V N 	return 0;
2591df828598SMugunthan V N }
2592df828598SMugunthan V N 
25938963a504SGrygorii Strashko #ifdef CONFIG_PM_SLEEP
2594df828598SMugunthan V N static int cpsw_suspend(struct device *dev)
2595df828598SMugunthan V N {
25964e13c252SWolfram Sang 	struct net_device	*ndev = dev_get_drvdata(dev);
2597606f3993SIvan Khoronzhuk 	struct cpsw_common	*cpsw = ndev_to_cpsw(ndev);
2598df828598SMugunthan V N 
2599606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
2600618073e3SMugunthan V N 		int i;
2601618073e3SMugunthan V N 
2602606f3993SIvan Khoronzhuk 		for (i = 0; i < cpsw->data.slaves; i++) {
2603606f3993SIvan Khoronzhuk 			if (netif_running(cpsw->slaves[i].ndev))
2604606f3993SIvan Khoronzhuk 				cpsw_ndo_stop(cpsw->slaves[i].ndev);
2605618073e3SMugunthan V N 		}
2606618073e3SMugunthan V N 	} else {
2607df828598SMugunthan V N 		if (netif_running(ndev))
2608df828598SMugunthan V N 			cpsw_ndo_stop(ndev);
2609618073e3SMugunthan V N 	}
26101e7a2e21SDaniel Mack 
2611739683b4SMugunthan V N 	/* Select sleep pin state */
261256e31bd8SIvan Khoronzhuk 	pinctrl_pm_select_sleep_state(dev);
2613739683b4SMugunthan V N 
2614df828598SMugunthan V N 	return 0;
2615df828598SMugunthan V N }
2616df828598SMugunthan V N 
2617df828598SMugunthan V N static int cpsw_resume(struct device *dev)
2618df828598SMugunthan V N {
26194e13c252SWolfram Sang 	struct net_device	*ndev = dev_get_drvdata(dev);
2620a60ced99SIvan Khoronzhuk 	struct cpsw_common	*cpsw = ndev_to_cpsw(ndev);
2621df828598SMugunthan V N 
2622739683b4SMugunthan V N 	/* Select default pin state */
262356e31bd8SIvan Khoronzhuk 	pinctrl_pm_select_default_state(dev);
2624739683b4SMugunthan V N 
26254ccfd638SGrygorii Strashko 	/* shut up ASSERT_RTNL() warning in netif_set_real_num_tx/rx_queues */
26264ccfd638SGrygorii Strashko 	rtnl_lock();
2627606f3993SIvan Khoronzhuk 	if (cpsw->data.dual_emac) {
2628618073e3SMugunthan V N 		int i;
2629618073e3SMugunthan V N 
2630606f3993SIvan Khoronzhuk 		for (i = 0; i < cpsw->data.slaves; i++) {
2631606f3993SIvan Khoronzhuk 			if (netif_running(cpsw->slaves[i].ndev))
2632606f3993SIvan Khoronzhuk 				cpsw_ndo_open(cpsw->slaves[i].ndev);
2633618073e3SMugunthan V N 		}
2634618073e3SMugunthan V N 	} else {
2635df828598SMugunthan V N 		if (netif_running(ndev))
2636df828598SMugunthan V N 			cpsw_ndo_open(ndev);
2637618073e3SMugunthan V N 	}
26384ccfd638SGrygorii Strashko 	rtnl_unlock();
26394ccfd638SGrygorii Strashko 
2640df828598SMugunthan V N 	return 0;
2641df828598SMugunthan V N }
26428963a504SGrygorii Strashko #endif
2643df828598SMugunthan V N 
26448963a504SGrygorii Strashko static SIMPLE_DEV_PM_OPS(cpsw_pm_ops, cpsw_suspend, cpsw_resume);
2645df828598SMugunthan V N 
2646df828598SMugunthan V N static struct platform_driver cpsw_driver = {
2647df828598SMugunthan V N 	.driver = {
2648df828598SMugunthan V N 		.name	 = "cpsw",
2649df828598SMugunthan V N 		.pm	 = &cpsw_pm_ops,
26501e5c76d4SSachin Kamat 		.of_match_table = cpsw_of_mtable,
2651df828598SMugunthan V N 	},
2652df828598SMugunthan V N 	.probe = cpsw_probe,
2653663e12e6SBill Pemberton 	.remove = cpsw_remove,
2654df828598SMugunthan V N };
2655df828598SMugunthan V N 
26566fb3b6b5SGrygorii Strashko module_platform_driver(cpsw_driver);
2657df828598SMugunthan V N 
2658df828598SMugunthan V N MODULE_LICENSE("GPL");
2659df828598SMugunthan V N MODULE_AUTHOR("Cyril Chemparathy <cyril@ti.com>");
2660df828598SMugunthan V N MODULE_AUTHOR("Mugunthan V N <mugunthanvnm@ti.com>");
2661df828598SMugunthan V N MODULE_DESCRIPTION("TI CPSW Ethernet driver");
2662