168cf027fSGrygorii Strashko // SPDX-License-Identifier: GPL-2.0 2df828598SMugunthan V N /* 3df828598SMugunthan V N * Texas Instruments Ethernet Switch Driver 4df828598SMugunthan V N * 5df828598SMugunthan V N * Copyright (C) 2012 Texas Instruments 6df828598SMugunthan V N * 7df828598SMugunthan V N */ 8df828598SMugunthan V N 9df828598SMugunthan V N #include <linux/kernel.h> 10df828598SMugunthan V N #include <linux/io.h> 11df828598SMugunthan V N #include <linux/clk.h> 12df828598SMugunthan V N #include <linux/timer.h> 13df828598SMugunthan V N #include <linux/module.h> 14df828598SMugunthan V N #include <linux/platform_device.h> 15df828598SMugunthan V N #include <linux/irqreturn.h> 16df828598SMugunthan V N #include <linux/interrupt.h> 17df828598SMugunthan V N #include <linux/if_ether.h> 18df828598SMugunthan V N #include <linux/etherdevice.h> 19df828598SMugunthan V N #include <linux/netdevice.h> 202e5b38abSRichard Cochran #include <linux/net_tstamp.h> 21df828598SMugunthan V N #include <linux/phy.h> 223ff18849SGrygorii Strashko #include <linux/phy/phy.h> 23df828598SMugunthan V N #include <linux/workqueue.h> 24df828598SMugunthan V N #include <linux/delay.h> 25f150bd7fSMugunthan V N #include <linux/pm_runtime.h> 26e2b3e493SArnd Bergmann #include <linux/gpio/consumer.h> 272eb32b0aSMugunthan V N #include <linux/of.h> 289e42f715SHeiko Schocher #include <linux/of_mdio.h> 292eb32b0aSMugunthan V N #include <linux/of_net.h> 302eb32b0aSMugunthan V N #include <linux/of_device.h> 313b72c2feSMugunthan V N #include <linux/if_vlan.h> 32514c6032SRandy Dunlap #include <linux/kmemleak.h> 339611d6d6SIvan Khoronzhuk #include <linux/sys_soc.h> 349ed4050cSIvan Khoronzhuk #include <net/page_pool.h> 359ed4050cSIvan Khoronzhuk #include <linux/bpf.h> 369ed4050cSIvan Khoronzhuk #include <linux/bpf_trace.h> 37df828598SMugunthan V N 38739683b4SMugunthan V N #include <linux/pinctrl/consumer.h> 397929a668SIvan Khoronzhuk #include <net/pkt_cls.h> 40df828598SMugunthan V N 41dbe34724SMugunthan V N #include "cpsw.h" 42df828598SMugunthan V N #include "cpsw_ale.h" 43814b4a67SGrygorii Strashko #include "cpsw_priv.h" 44cfc08345SGrygorii Strashko #include "cpsw_sl.h" 452e5b38abSRichard Cochran #include "cpts.h" 46df828598SMugunthan V N #include "davinci_cpdma.h" 47df828598SMugunthan V N 4857d90148SIvan Khoronzhuk #include <net/pkt_sched.h> 4957d90148SIvan Khoronzhuk 50df828598SMugunthan V N static int debug_level; 51df828598SMugunthan V N module_param(debug_level, int, 0); 52df828598SMugunthan V N MODULE_PARM_DESC(debug_level, "cpsw debug level (NETIF_MSG bits)"); 53df828598SMugunthan V N 54df828598SMugunthan V N static int ale_ageout = 10; 55df828598SMugunthan V N module_param(ale_ageout, int, 0); 56df828598SMugunthan V N MODULE_PARM_DESC(ale_ageout, "cpsw ale ageout interval (seconds)"); 57df828598SMugunthan V N 58df828598SMugunthan V N static int rx_packet_max = CPSW_MAX_PACKET_SIZE; 59df828598SMugunthan V N module_param(rx_packet_max, int, 0); 60df828598SMugunthan V N MODULE_PARM_DESC(rx_packet_max, "maximum receive packet size (bytes)"); 61df828598SMugunthan V N 6290225bf0SGrygorii Strashko static int descs_pool_size = CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT; 6390225bf0SGrygorii Strashko module_param(descs_pool_size, int, 0444); 6490225bf0SGrygorii Strashko MODULE_PARM_DESC(descs_pool_size, "Number of CPDMA CPPI descriptors in pool"); 6590225bf0SGrygorii Strashko 66df828598SMugunthan V N #define for_each_slave(priv, func, arg...) \ 67df828598SMugunthan V N do { \ 686e6ceaedSSebastian Siewior struct cpsw_slave *slave; \ 69606f3993SIvan Khoronzhuk struct cpsw_common *cpsw = (priv)->cpsw; \ 706e6ceaedSSebastian Siewior int n; \ 71606f3993SIvan Khoronzhuk if (cpsw->data.dual_emac) \ 72606f3993SIvan Khoronzhuk (func)((cpsw)->slaves + priv->emac_port, ##arg);\ 73d9ba8f9eSMugunthan V N else \ 74606f3993SIvan Khoronzhuk for (n = cpsw->data.slaves, \ 75606f3993SIvan Khoronzhuk slave = cpsw->slaves; \ 766e6ceaedSSebastian Siewior n; n--) \ 776e6ceaedSSebastian Siewior (func)(slave++, ##arg); \ 78df828598SMugunthan V N } while (0) 79d9ba8f9eSMugunthan V N 8051a95337SGrygorii Strashko static int cpsw_slave_index_priv(struct cpsw_common *cpsw, 8151a95337SGrygorii Strashko struct cpsw_priv *priv) 8251a95337SGrygorii Strashko { 8351a95337SGrygorii Strashko return cpsw->data.dual_emac ? priv->emac_port : cpsw->data.active_slave; 8451a95337SGrygorii Strashko } 8551a95337SGrygorii Strashko 8651a95337SGrygorii Strashko static int cpsw_get_slave_port(u32 slave_num) 8751a95337SGrygorii Strashko { 8851a95337SGrygorii Strashko return slave_num + 1; 8951a95337SGrygorii Strashko } 9051a95337SGrygorii Strashko 9100fe4712SIvan Khoronzhuk static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev, 9200fe4712SIvan Khoronzhuk __be16 proto, u16 vid); 9300fe4712SIvan Khoronzhuk 940cd8f9ccSMugunthan V N static void cpsw_set_promiscious(struct net_device *ndev, bool enable) 950cd8f9ccSMugunthan V N { 962a05a622SIvan Khoronzhuk struct cpsw_common *cpsw = ndev_to_cpsw(ndev); 972a05a622SIvan Khoronzhuk struct cpsw_ale *ale = cpsw->ale; 980cd8f9ccSMugunthan V N int i; 990cd8f9ccSMugunthan V N 100606f3993SIvan Khoronzhuk if (cpsw->data.dual_emac) { 1010cd8f9ccSMugunthan V N bool flag = false; 1020cd8f9ccSMugunthan V N 1030cd8f9ccSMugunthan V N /* Enabling promiscuous mode for one interface will be 1040cd8f9ccSMugunthan V N * common for both the interface as the interface shares 1050cd8f9ccSMugunthan V N * the same hardware resource. 1060cd8f9ccSMugunthan V N */ 107606f3993SIvan Khoronzhuk for (i = 0; i < cpsw->data.slaves; i++) 108606f3993SIvan Khoronzhuk if (cpsw->slaves[i].ndev->flags & IFF_PROMISC) 1090cd8f9ccSMugunthan V N flag = true; 1100cd8f9ccSMugunthan V N 1110cd8f9ccSMugunthan V N if (!enable && flag) { 1120cd8f9ccSMugunthan V N enable = true; 1130cd8f9ccSMugunthan V N dev_err(&ndev->dev, "promiscuity not disabled as the other interface is still in promiscuity mode\n"); 1140cd8f9ccSMugunthan V N } 1150cd8f9ccSMugunthan V N 1160cd8f9ccSMugunthan V N if (enable) { 1170cd8f9ccSMugunthan V N /* Enable Bypass */ 1180cd8f9ccSMugunthan V N cpsw_ale_control_set(ale, 0, ALE_BYPASS, 1); 1190cd8f9ccSMugunthan V N 1200cd8f9ccSMugunthan V N dev_dbg(&ndev->dev, "promiscuity enabled\n"); 1210cd8f9ccSMugunthan V N } else { 1220cd8f9ccSMugunthan V N /* Disable Bypass */ 1230cd8f9ccSMugunthan V N cpsw_ale_control_set(ale, 0, ALE_BYPASS, 0); 1240cd8f9ccSMugunthan V N dev_dbg(&ndev->dev, "promiscuity disabled\n"); 1250cd8f9ccSMugunthan V N } 1260cd8f9ccSMugunthan V N } else { 1270cd8f9ccSMugunthan V N if (enable) { 1280cd8f9ccSMugunthan V N unsigned long timeout = jiffies + HZ; 1290cd8f9ccSMugunthan V N 1306f979eb3SLennart Sorensen /* Disable Learn for all ports (host is port 0 and slaves are port 1 and up */ 131606f3993SIvan Khoronzhuk for (i = 0; i <= cpsw->data.slaves; i++) { 1320cd8f9ccSMugunthan V N cpsw_ale_control_set(ale, i, 1330cd8f9ccSMugunthan V N ALE_PORT_NOLEARN, 1); 1340cd8f9ccSMugunthan V N cpsw_ale_control_set(ale, i, 1350cd8f9ccSMugunthan V N ALE_PORT_NO_SA_UPDATE, 1); 1360cd8f9ccSMugunthan V N } 1370cd8f9ccSMugunthan V N 1380cd8f9ccSMugunthan V N /* Clear All Untouched entries */ 1390cd8f9ccSMugunthan V N cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1); 1400cd8f9ccSMugunthan V N do { 1410cd8f9ccSMugunthan V N cpu_relax(); 1420cd8f9ccSMugunthan V N if (cpsw_ale_control_get(ale, 0, ALE_AGEOUT)) 1430cd8f9ccSMugunthan V N break; 1440cd8f9ccSMugunthan V N } while (time_after(timeout, jiffies)); 1450cd8f9ccSMugunthan V N cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1); 1460cd8f9ccSMugunthan V N 1470cd8f9ccSMugunthan V N /* Clear all mcast from ALE */ 14861f1cef9SGrygorii Strashko cpsw_ale_flush_multicast(ale, ALE_ALL_PORTS, -1); 14915180ecaSIvan Khoronzhuk __hw_addr_ref_unsync_dev(&ndev->mc, ndev, NULL); 1500cd8f9ccSMugunthan V N 1510cd8f9ccSMugunthan V N /* Flood All Unicast Packets to Host port */ 1520cd8f9ccSMugunthan V N cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 1); 1530cd8f9ccSMugunthan V N dev_dbg(&ndev->dev, "promiscuity enabled\n"); 1540cd8f9ccSMugunthan V N } else { 1556f979eb3SLennart Sorensen /* Don't Flood All Unicast Packets to Host port */ 1560cd8f9ccSMugunthan V N cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 0); 1570cd8f9ccSMugunthan V N 1586f979eb3SLennart Sorensen /* Enable Learn for all ports (host is port 0 and slaves are port 1 and up */ 159606f3993SIvan Khoronzhuk for (i = 0; i <= cpsw->data.slaves; i++) { 1600cd8f9ccSMugunthan V N cpsw_ale_control_set(ale, i, 1610cd8f9ccSMugunthan V N ALE_PORT_NOLEARN, 0); 1620cd8f9ccSMugunthan V N cpsw_ale_control_set(ale, i, 1630cd8f9ccSMugunthan V N ALE_PORT_NO_SA_UPDATE, 0); 1640cd8f9ccSMugunthan V N } 1650cd8f9ccSMugunthan V N dev_dbg(&ndev->dev, "promiscuity disabled\n"); 1660cd8f9ccSMugunthan V N } 1670cd8f9ccSMugunthan V N } 1680cd8f9ccSMugunthan V N } 1690cd8f9ccSMugunthan V N 17015180ecaSIvan Khoronzhuk /** 17115180ecaSIvan Khoronzhuk * cpsw_set_mc - adds multicast entry to the table if it's not added or deletes 17215180ecaSIvan Khoronzhuk * if it's not deleted 17315180ecaSIvan Khoronzhuk * @ndev: device to sync 17415180ecaSIvan Khoronzhuk * @addr: address to be added or deleted 17515180ecaSIvan Khoronzhuk * @vid: vlan id, if vid < 0 set/unset address for real device 17615180ecaSIvan Khoronzhuk * @add: add address if the flag is set or remove otherwise 17715180ecaSIvan Khoronzhuk */ 17815180ecaSIvan Khoronzhuk static int cpsw_set_mc(struct net_device *ndev, const u8 *addr, 17915180ecaSIvan Khoronzhuk int vid, int add) 1805c50a856SMugunthan V N { 1815c50a856SMugunthan V N struct cpsw_priv *priv = netdev_priv(ndev); 182606f3993SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 18315180ecaSIvan Khoronzhuk int mask, flags, ret; 18425906052SMugunthan V N 18515180ecaSIvan Khoronzhuk if (vid < 0) { 18615180ecaSIvan Khoronzhuk if (cpsw->data.dual_emac) 187606f3993SIvan Khoronzhuk vid = cpsw->slaves[priv->emac_port].port_vlan; 18815180ecaSIvan Khoronzhuk else 1895da19489SIvan Khoronzhuk vid = 0; 1905da19489SIvan Khoronzhuk } 1915da19489SIvan Khoronzhuk 19215180ecaSIvan Khoronzhuk mask = cpsw->data.dual_emac ? ALE_PORT_HOST : ALE_ALL_PORTS; 19315180ecaSIvan Khoronzhuk flags = vid ? ALE_VLAN : 0; 19415180ecaSIvan Khoronzhuk 19515180ecaSIvan Khoronzhuk if (add) 19615180ecaSIvan Khoronzhuk ret = cpsw_ale_add_mcast(cpsw->ale, addr, mask, flags, vid, 0); 19715180ecaSIvan Khoronzhuk else 19815180ecaSIvan Khoronzhuk ret = cpsw_ale_del_mcast(cpsw->ale, addr, 0, flags, vid); 19915180ecaSIvan Khoronzhuk 20015180ecaSIvan Khoronzhuk return ret; 20115180ecaSIvan Khoronzhuk } 20215180ecaSIvan Khoronzhuk 20315180ecaSIvan Khoronzhuk static int cpsw_update_vlan_mc(struct net_device *vdev, int vid, void *ctx) 20415180ecaSIvan Khoronzhuk { 20515180ecaSIvan Khoronzhuk struct addr_sync_ctx *sync_ctx = ctx; 20615180ecaSIvan Khoronzhuk struct netdev_hw_addr *ha; 20715180ecaSIvan Khoronzhuk int found = 0, ret = 0; 20815180ecaSIvan Khoronzhuk 20915180ecaSIvan Khoronzhuk if (!vdev || !(vdev->flags & IFF_UP)) 21015180ecaSIvan Khoronzhuk return 0; 21115180ecaSIvan Khoronzhuk 21215180ecaSIvan Khoronzhuk /* vlan address is relevant if its sync_cnt != 0 */ 21315180ecaSIvan Khoronzhuk netdev_for_each_mc_addr(ha, vdev) { 21415180ecaSIvan Khoronzhuk if (ether_addr_equal(ha->addr, sync_ctx->addr)) { 21515180ecaSIvan Khoronzhuk found = ha->sync_cnt; 21615180ecaSIvan Khoronzhuk break; 21715180ecaSIvan Khoronzhuk } 21815180ecaSIvan Khoronzhuk } 21915180ecaSIvan Khoronzhuk 22015180ecaSIvan Khoronzhuk if (found) 22115180ecaSIvan Khoronzhuk sync_ctx->consumed++; 22215180ecaSIvan Khoronzhuk 22315180ecaSIvan Khoronzhuk if (sync_ctx->flush) { 22415180ecaSIvan Khoronzhuk if (!found) 22515180ecaSIvan Khoronzhuk cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0); 22615180ecaSIvan Khoronzhuk return 0; 22715180ecaSIvan Khoronzhuk } 22815180ecaSIvan Khoronzhuk 22915180ecaSIvan Khoronzhuk if (found) 23015180ecaSIvan Khoronzhuk ret = cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 1); 23115180ecaSIvan Khoronzhuk 23215180ecaSIvan Khoronzhuk return ret; 23315180ecaSIvan Khoronzhuk } 23415180ecaSIvan Khoronzhuk 23515180ecaSIvan Khoronzhuk static int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr, int num) 23615180ecaSIvan Khoronzhuk { 23715180ecaSIvan Khoronzhuk struct addr_sync_ctx sync_ctx; 23815180ecaSIvan Khoronzhuk int ret; 23915180ecaSIvan Khoronzhuk 24015180ecaSIvan Khoronzhuk sync_ctx.consumed = 0; 24115180ecaSIvan Khoronzhuk sync_ctx.addr = addr; 24215180ecaSIvan Khoronzhuk sync_ctx.ndev = ndev; 24315180ecaSIvan Khoronzhuk sync_ctx.flush = 0; 24415180ecaSIvan Khoronzhuk 24515180ecaSIvan Khoronzhuk ret = vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx); 24615180ecaSIvan Khoronzhuk if (sync_ctx.consumed < num && !ret) 24715180ecaSIvan Khoronzhuk ret = cpsw_set_mc(ndev, addr, -1, 1); 24815180ecaSIvan Khoronzhuk 24915180ecaSIvan Khoronzhuk return ret; 25015180ecaSIvan Khoronzhuk } 25115180ecaSIvan Khoronzhuk 25215180ecaSIvan Khoronzhuk static int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr, int num) 25315180ecaSIvan Khoronzhuk { 25415180ecaSIvan Khoronzhuk struct addr_sync_ctx sync_ctx; 25515180ecaSIvan Khoronzhuk 25615180ecaSIvan Khoronzhuk sync_ctx.consumed = 0; 25715180ecaSIvan Khoronzhuk sync_ctx.addr = addr; 25815180ecaSIvan Khoronzhuk sync_ctx.ndev = ndev; 25915180ecaSIvan Khoronzhuk sync_ctx.flush = 1; 26015180ecaSIvan Khoronzhuk 26115180ecaSIvan Khoronzhuk vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx); 26215180ecaSIvan Khoronzhuk if (sync_ctx.consumed == num) 26315180ecaSIvan Khoronzhuk cpsw_set_mc(ndev, addr, -1, 0); 26415180ecaSIvan Khoronzhuk 26515180ecaSIvan Khoronzhuk return 0; 26615180ecaSIvan Khoronzhuk } 26715180ecaSIvan Khoronzhuk 26815180ecaSIvan Khoronzhuk static int cpsw_purge_vlan_mc(struct net_device *vdev, int vid, void *ctx) 26915180ecaSIvan Khoronzhuk { 27015180ecaSIvan Khoronzhuk struct addr_sync_ctx *sync_ctx = ctx; 27115180ecaSIvan Khoronzhuk struct netdev_hw_addr *ha; 27215180ecaSIvan Khoronzhuk int found = 0; 27315180ecaSIvan Khoronzhuk 27415180ecaSIvan Khoronzhuk if (!vdev || !(vdev->flags & IFF_UP)) 27515180ecaSIvan Khoronzhuk return 0; 27615180ecaSIvan Khoronzhuk 27715180ecaSIvan Khoronzhuk /* vlan address is relevant if its sync_cnt != 0 */ 27815180ecaSIvan Khoronzhuk netdev_for_each_mc_addr(ha, vdev) { 27915180ecaSIvan Khoronzhuk if (ether_addr_equal(ha->addr, sync_ctx->addr)) { 28015180ecaSIvan Khoronzhuk found = ha->sync_cnt; 28115180ecaSIvan Khoronzhuk break; 28215180ecaSIvan Khoronzhuk } 28315180ecaSIvan Khoronzhuk } 28415180ecaSIvan Khoronzhuk 28515180ecaSIvan Khoronzhuk if (!found) 28615180ecaSIvan Khoronzhuk return 0; 28715180ecaSIvan Khoronzhuk 28815180ecaSIvan Khoronzhuk sync_ctx->consumed++; 28915180ecaSIvan Khoronzhuk cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0); 29015180ecaSIvan Khoronzhuk return 0; 29115180ecaSIvan Khoronzhuk } 29215180ecaSIvan Khoronzhuk 29315180ecaSIvan Khoronzhuk static int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num) 29415180ecaSIvan Khoronzhuk { 29515180ecaSIvan Khoronzhuk struct addr_sync_ctx sync_ctx; 29615180ecaSIvan Khoronzhuk 29715180ecaSIvan Khoronzhuk sync_ctx.addr = addr; 29815180ecaSIvan Khoronzhuk sync_ctx.ndev = ndev; 29915180ecaSIvan Khoronzhuk sync_ctx.consumed = 0; 30015180ecaSIvan Khoronzhuk 30115180ecaSIvan Khoronzhuk vlan_for_each(ndev, cpsw_purge_vlan_mc, &sync_ctx); 30215180ecaSIvan Khoronzhuk if (sync_ctx.consumed < num) 30315180ecaSIvan Khoronzhuk cpsw_set_mc(ndev, addr, -1, 0); 30415180ecaSIvan Khoronzhuk 3055da19489SIvan Khoronzhuk return 0; 3065da19489SIvan Khoronzhuk } 3075da19489SIvan Khoronzhuk 3085da19489SIvan Khoronzhuk static void cpsw_ndo_set_rx_mode(struct net_device *ndev) 3095da19489SIvan Khoronzhuk { 31006095f34SGrygorii Strashko struct cpsw_priv *priv = netdev_priv(ndev); 31106095f34SGrygorii Strashko struct cpsw_common *cpsw = priv->cpsw; 31206095f34SGrygorii Strashko int slave_port = -1; 31306095f34SGrygorii Strashko 31406095f34SGrygorii Strashko if (cpsw->data.dual_emac) 31506095f34SGrygorii Strashko slave_port = priv->emac_port + 1; 3165c50a856SMugunthan V N 3175c50a856SMugunthan V N if (ndev->flags & IFF_PROMISC) { 3185c50a856SMugunthan V N /* Enable promiscuous mode */ 3190cd8f9ccSMugunthan V N cpsw_set_promiscious(ndev, true); 32006095f34SGrygorii Strashko cpsw_ale_set_allmulti(cpsw->ale, IFF_ALLMULTI, slave_port); 3215c50a856SMugunthan V N return; 3220cd8f9ccSMugunthan V N } else { 3230cd8f9ccSMugunthan V N /* Disable promiscuous mode */ 3240cd8f9ccSMugunthan V N cpsw_set_promiscious(ndev, false); 3255c50a856SMugunthan V N } 3265c50a856SMugunthan V N 3271e5c4bc4SLennart Sorensen /* Restore allmulti on vlans if necessary */ 32806095f34SGrygorii Strashko cpsw_ale_set_allmulti(cpsw->ale, 32906095f34SGrygorii Strashko ndev->flags & IFF_ALLMULTI, slave_port); 3301e5c4bc4SLennart Sorensen 33115180ecaSIvan Khoronzhuk /* add/remove mcast address either for real netdev or for vlan */ 33215180ecaSIvan Khoronzhuk __hw_addr_ref_sync_dev(&ndev->mc, ndev, cpsw_add_mc_addr, 33315180ecaSIvan Khoronzhuk cpsw_del_mc_addr); 3345c50a856SMugunthan V N } 3355c50a856SMugunthan V N 3369ed4050cSIvan Khoronzhuk static unsigned int cpsw_rxbuf_total_len(unsigned int len) 3379ed4050cSIvan Khoronzhuk { 3389ed4050cSIvan Khoronzhuk len += CPSW_HEADROOM; 3399ed4050cSIvan Khoronzhuk len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); 3409ed4050cSIvan Khoronzhuk 3419ed4050cSIvan Khoronzhuk return SKB_DATA_ALIGN(len); 3429ed4050cSIvan Khoronzhuk } 3439ed4050cSIvan Khoronzhuk 3441a3b5056SOlof Johansson static void cpsw_rx_handler(void *token, int len, int status) 345df828598SMugunthan V N { 3469ed4050cSIvan Khoronzhuk struct page *new_page, *page = token; 3479ed4050cSIvan Khoronzhuk void *pa = page_address(page); 3489ed4050cSIvan Khoronzhuk struct cpsw_meta_xdp *xmeta = pa + CPSW_XMETA_OFFSET; 3499ed4050cSIvan Khoronzhuk struct cpsw_common *cpsw = ndev_to_cpsw(xmeta->ndev); 3509ed4050cSIvan Khoronzhuk int pkt_size = cpsw->rx_packet_max; 3519ed4050cSIvan Khoronzhuk int ret = 0, port, ch = xmeta->ch; 3529ed4050cSIvan Khoronzhuk int headroom = CPSW_HEADROOM; 3539ed4050cSIvan Khoronzhuk struct net_device *ndev = xmeta->ndev; 354a9423120SIvan Khoronzhuk struct cpsw_priv *priv; 3559ed4050cSIvan Khoronzhuk struct page_pool *pool; 3569ed4050cSIvan Khoronzhuk struct sk_buff *skb; 3579ed4050cSIvan Khoronzhuk struct xdp_buff xdp; 3589ed4050cSIvan Khoronzhuk dma_addr_t dma; 359df828598SMugunthan V N 3609ed4050cSIvan Khoronzhuk if (cpsw->data.dual_emac && status >= 0) { 361fea49f60SIvan Khoronzhuk port = CPDMA_RX_SOURCE_PORT(status); 3629ed4050cSIvan Khoronzhuk if (port) 363fea49f60SIvan Khoronzhuk ndev = cpsw->slaves[--port].ndev; 364fea49f60SIvan Khoronzhuk } 365d9ba8f9eSMugunthan V N 3669ed4050cSIvan Khoronzhuk priv = netdev_priv(ndev); 3679ed4050cSIvan Khoronzhuk pool = cpsw->page_pool[ch]; 36816e5c57dSMugunthan V N if (unlikely(status < 0) || unlikely(!netif_running(ndev))) { 369a0e2c822SMugunthan V N /* In dual emac mode check for all interfaces */ 370d5bc1613SIvan Khoronzhuk if (cpsw->data.dual_emac && cpsw->usage_count && 371fe734d0aSIvan Khoronzhuk (status >= 0)) { 372a0e2c822SMugunthan V N /* The packet received is for the interface which 373a0e2c822SMugunthan V N * is already down and the other interface is up 374dbedd44eSJoe Perches * and running, instead of freeing which results 375a0e2c822SMugunthan V N * in reducing of the number of rx descriptor in 3769ed4050cSIvan Khoronzhuk * DMA engine, requeue page back to cpdma. 377a0e2c822SMugunthan V N */ 3789ed4050cSIvan Khoronzhuk new_page = page; 379a0e2c822SMugunthan V N goto requeue; 380a0e2c822SMugunthan V N } 381a0e2c822SMugunthan V N 3829ed4050cSIvan Khoronzhuk /* the interface is going down, pages are purged */ 3839ed4050cSIvan Khoronzhuk page_pool_recycle_direct(pool, page); 384df828598SMugunthan V N return; 385df828598SMugunthan V N } 386b4727e69SSebastian Siewior 3879ed4050cSIvan Khoronzhuk new_page = page_pool_dev_alloc_pages(pool); 3889ed4050cSIvan Khoronzhuk if (unlikely(!new_page)) { 3899ed4050cSIvan Khoronzhuk new_page = page; 3909ed4050cSIvan Khoronzhuk ndev->stats.rx_dropped++; 3919ed4050cSIvan Khoronzhuk goto requeue; 3929ed4050cSIvan Khoronzhuk } 3939ed4050cSIvan Khoronzhuk 3949ed4050cSIvan Khoronzhuk if (priv->xdp_prog) { 395be9df4afSLorenzo Bianconi int headroom = CPSW_HEADROOM, size = len; 39643b5169dSLorenzo Bianconi 397be9df4afSLorenzo Bianconi xdp_init_buff(&xdp, PAGE_SIZE, &priv->xdp_rxq[ch]); 3989ed4050cSIvan Khoronzhuk if (status & CPDMA_RX_VLAN_ENCAP) { 399be9df4afSLorenzo Bianconi headroom += CPSW_RX_VLAN_ENCAP_HDR_SIZE; 400be9df4afSLorenzo Bianconi size -= CPSW_RX_VLAN_ENCAP_HDR_SIZE; 4019ed4050cSIvan Khoronzhuk } 4029ed4050cSIvan Khoronzhuk 403be9df4afSLorenzo Bianconi xdp_prepare_buff(&xdp, pa, headroom, size, false); 4049ed4050cSIvan Khoronzhuk 405c5013ac1SGrygorii Strashko port = priv->emac_port + cpsw->data.dual_emac; 406a8225efdSLorenzo Bianconi ret = cpsw_run_xdp(priv, ch, &xdp, page, port, &len); 4079ed4050cSIvan Khoronzhuk if (ret != CPSW_XDP_PASS) 4089ed4050cSIvan Khoronzhuk goto requeue; 4099ed4050cSIvan Khoronzhuk 4109ed4050cSIvan Khoronzhuk headroom = xdp.data - xdp.data_hard_start; 4119ed4050cSIvan Khoronzhuk 4129ed4050cSIvan Khoronzhuk /* XDP prog can modify vlan tag, so can't use encap header */ 4139ed4050cSIvan Khoronzhuk status &= ~CPDMA_RX_VLAN_ENCAP; 4149ed4050cSIvan Khoronzhuk } 4159ed4050cSIvan Khoronzhuk 4169ed4050cSIvan Khoronzhuk /* pass skb to netstack if no XDP prog or returned XDP_PASS */ 4179ed4050cSIvan Khoronzhuk skb = build_skb(pa, cpsw_rxbuf_total_len(pkt_size)); 4189ed4050cSIvan Khoronzhuk if (!skb) { 4199ed4050cSIvan Khoronzhuk ndev->stats.rx_dropped++; 4209ed4050cSIvan Khoronzhuk page_pool_recycle_direct(pool, page); 4219ed4050cSIvan Khoronzhuk goto requeue; 4229ed4050cSIvan Khoronzhuk } 4239ed4050cSIvan Khoronzhuk 4249ed4050cSIvan Khoronzhuk skb_reserve(skb, headroom); 425df828598SMugunthan V N skb_put(skb, len); 4269ed4050cSIvan Khoronzhuk skb->dev = ndev; 427a3a41d2fSGrygorii Strashko if (status & CPDMA_RX_VLAN_ENCAP) 428a3a41d2fSGrygorii Strashko cpsw_rx_vlan_encap(skb); 429a9423120SIvan Khoronzhuk if (priv->rx_ts_enabled) 4302a05a622SIvan Khoronzhuk cpts_rx_timestamp(cpsw->cpts, skb); 431df828598SMugunthan V N skb->protocol = eth_type_trans(skb, ndev); 4329ed4050cSIvan Khoronzhuk 433a078d981SLorenzo Bianconi /* mark skb for recycling */ 43457f05bc2SYunsheng Lin skb_mark_for_recycle(skb); 435df828598SMugunthan V N netif_receive_skb(skb); 4369ed4050cSIvan Khoronzhuk 4378dc43ddcSTobias Klauser ndev->stats.rx_bytes += len; 4388dc43ddcSTobias Klauser ndev->stats.rx_packets++; 439df828598SMugunthan V N 440a0e2c822SMugunthan V N requeue: 4419ed4050cSIvan Khoronzhuk xmeta = page_address(new_page) + CPSW_XMETA_OFFSET; 4429ed4050cSIvan Khoronzhuk xmeta->ndev = ndev; 4439ed4050cSIvan Khoronzhuk xmeta->ch = ch; 4449ed4050cSIvan Khoronzhuk 4459ed4050cSIvan Khoronzhuk dma = page_pool_get_dma_addr(new_page) + CPSW_HEADROOM; 4469ed4050cSIvan Khoronzhuk ret = cpdma_chan_submit_mapped(cpsw->rxv[ch].ch, new_page, dma, 4479ed4050cSIvan Khoronzhuk pkt_size, 0); 448871e8465SIvan Khoronzhuk if (ret < 0) { 449871e8465SIvan Khoronzhuk WARN_ON(ret == -ENOMEM); 4509ed4050cSIvan Khoronzhuk page_pool_recycle_direct(pool, new_page); 451df828598SMugunthan V N } 452871e8465SIvan Khoronzhuk } 453df828598SMugunthan V N 454df828598SMugunthan V N static void _cpsw_adjust_link(struct cpsw_slave *slave, 455df828598SMugunthan V N struct cpsw_priv *priv, bool *link) 456df828598SMugunthan V N { 457df828598SMugunthan V N struct phy_device *phy = slave->phy; 458df828598SMugunthan V N u32 mac_control = 0; 459df828598SMugunthan V N u32 slave_port; 460606f3993SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 461df828598SMugunthan V N 462df828598SMugunthan V N if (!phy) 463df828598SMugunthan V N return; 464df828598SMugunthan V N 4656f1f5836SIvan Khoronzhuk slave_port = cpsw_get_slave_port(slave->slave_num); 466df828598SMugunthan V N 467df828598SMugunthan V N if (phy->link) { 468cfc08345SGrygorii Strashko mac_control = CPSW_SL_CTL_GMII_EN; 469cfc08345SGrygorii Strashko 470cfc08345SGrygorii Strashko if (phy->speed == 1000) 471cfc08345SGrygorii Strashko mac_control |= CPSW_SL_CTL_GIG; 472cfc08345SGrygorii Strashko if (phy->duplex) 473cfc08345SGrygorii Strashko mac_control |= CPSW_SL_CTL_FULLDUPLEX; 474cfc08345SGrygorii Strashko 475cfc08345SGrygorii Strashko /* set speed_in input in case RMII mode is used in 100Mbps */ 476cfc08345SGrygorii Strashko if (phy->speed == 100) 477cfc08345SGrygorii Strashko mac_control |= CPSW_SL_CTL_IFCTL_A; 478cfc08345SGrygorii Strashko /* in band mode only works in 10Mbps RGMII mode */ 479cfc08345SGrygorii Strashko else if ((phy->speed == 10) && phy_interface_is_rgmii(phy)) 480cfc08345SGrygorii Strashko mac_control |= CPSW_SL_CTL_EXT_EN; /* In Band mode */ 481cfc08345SGrygorii Strashko 482cfc08345SGrygorii Strashko if (priv->rx_pause) 483cfc08345SGrygorii Strashko mac_control |= CPSW_SL_CTL_RX_FLOW_EN; 484cfc08345SGrygorii Strashko 485cfc08345SGrygorii Strashko if (priv->tx_pause) 486cfc08345SGrygorii Strashko mac_control |= CPSW_SL_CTL_TX_FLOW_EN; 487cfc08345SGrygorii Strashko 488cfc08345SGrygorii Strashko if (mac_control != slave->mac_control) 489cfc08345SGrygorii Strashko cpsw_sl_ctl_set(slave->mac_sl, mac_control); 490df828598SMugunthan V N 491df828598SMugunthan V N /* enable forwarding */ 4922a05a622SIvan Khoronzhuk cpsw_ale_control_set(cpsw->ale, slave_port, 493df828598SMugunthan V N ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); 494df828598SMugunthan V N 495df828598SMugunthan V N *link = true; 49657d90148SIvan Khoronzhuk 49757d90148SIvan Khoronzhuk if (priv->shp_cfg_speed && 49857d90148SIvan Khoronzhuk priv->shp_cfg_speed != slave->phy->speed && 49957d90148SIvan Khoronzhuk !cpsw_shp_is_off(priv)) 50057d90148SIvan Khoronzhuk dev_warn(priv->dev, 50157d90148SIvan Khoronzhuk "Speed was changed, CBS shaper speeds are changed!"); 502df828598SMugunthan V N } else { 503df828598SMugunthan V N mac_control = 0; 504df828598SMugunthan V N /* disable forwarding */ 5052a05a622SIvan Khoronzhuk cpsw_ale_control_set(cpsw->ale, slave_port, 506df828598SMugunthan V N ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); 507cfc08345SGrygorii Strashko 508cfc08345SGrygorii Strashko cpsw_sl_wait_for_idle(slave->mac_sl, 100); 509cfc08345SGrygorii Strashko 510cfc08345SGrygorii Strashko cpsw_sl_ctl_reset(slave->mac_sl); 511df828598SMugunthan V N } 512df828598SMugunthan V N 513cfc08345SGrygorii Strashko if (mac_control != slave->mac_control) 514df828598SMugunthan V N phy_print_status(phy); 515df828598SMugunthan V N 516df828598SMugunthan V N slave->mac_control = mac_control; 517df828598SMugunthan V N } 518df828598SMugunthan V N 519df828598SMugunthan V N static void cpsw_adjust_link(struct net_device *ndev) 520df828598SMugunthan V N { 521df828598SMugunthan V N struct cpsw_priv *priv = netdev_priv(ndev); 5220be01b8eSIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 523df828598SMugunthan V N bool link = false; 524df828598SMugunthan V N 525df828598SMugunthan V N for_each_slave(priv, _cpsw_adjust_link, priv, &link); 526df828598SMugunthan V N 527df828598SMugunthan V N if (link) { 5280be01b8eSIvan Khoronzhuk if (cpsw_need_resplit(cpsw)) 5299763a891SGrygorii Strashko cpsw_split_res(cpsw); 5300be01b8eSIvan Khoronzhuk 531df828598SMugunthan V N netif_carrier_on(ndev); 532df828598SMugunthan V N if (netif_running(ndev)) 533e05107e6SIvan Khoronzhuk netif_tx_wake_all_queues(ndev); 534df828598SMugunthan V N } else { 535df828598SMugunthan V N netif_carrier_off(ndev); 536e05107e6SIvan Khoronzhuk netif_tx_stop_all_queues(ndev); 537df828598SMugunthan V N } 538df828598SMugunthan V N } 539df828598SMugunthan V N 540d9ba8f9eSMugunthan V N static inline void cpsw_add_dual_emac_def_ale_entries( 541d9ba8f9eSMugunthan V N struct cpsw_priv *priv, struct cpsw_slave *slave, 542d9ba8f9eSMugunthan V N u32 slave_port) 543d9ba8f9eSMugunthan V N { 5442a05a622SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 54571a2cbb7SGrygorii Strashko u32 port_mask = 1 << slave_port | ALE_PORT_HOST; 546d9ba8f9eSMugunthan V N 5472a05a622SIvan Khoronzhuk if (cpsw->version == CPSW_VERSION_1) 548d9ba8f9eSMugunthan V N slave_write(slave, slave->port_vlan, CPSW1_PORT_VLAN); 549d9ba8f9eSMugunthan V N else 550d9ba8f9eSMugunthan V N slave_write(slave, slave->port_vlan, CPSW2_PORT_VLAN); 5512a05a622SIvan Khoronzhuk cpsw_ale_add_vlan(cpsw->ale, slave->port_vlan, port_mask, 552d9ba8f9eSMugunthan V N port_mask, port_mask, 0); 5532a05a622SIvan Khoronzhuk cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast, 5545b3a5a14SIvan Khoronzhuk ALE_PORT_HOST, ALE_VLAN, slave->port_vlan, 0); 5552a05a622SIvan Khoronzhuk cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr, 5562a05a622SIvan Khoronzhuk HOST_PORT_NUM, ALE_VLAN | 5572a05a622SIvan Khoronzhuk ALE_SECURE, slave->port_vlan); 5585e5add17SGrygorii Strashko cpsw_ale_control_set(cpsw->ale, slave_port, 5595e5add17SGrygorii Strashko ALE_PORT_DROP_UNKNOWN_VLAN, 1); 560d9ba8f9eSMugunthan V N } 561d9ba8f9eSMugunthan V N 5621e7a2e21SDaniel Mack static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) 5631e7a2e21SDaniel Mack { 564df828598SMugunthan V N u32 slave_port; 56530c57f07SSekhar Nori struct phy_device *phy; 566649a1688SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 567df828598SMugunthan V N 568cfc08345SGrygorii Strashko cpsw_sl_reset(slave->mac_sl, 100); 569cfc08345SGrygorii Strashko cpsw_sl_ctl_reset(slave->mac_sl); 570df828598SMugunthan V N 571df828598SMugunthan V N /* setup priority mapping */ 572cfc08345SGrygorii Strashko cpsw_sl_reg_write(slave->mac_sl, CPSW_SL_RX_PRI_MAP, 573cfc08345SGrygorii Strashko RX_PRIORITY_MAPPING); 5749750a3adSRichard Cochran 5752a05a622SIvan Khoronzhuk switch (cpsw->version) { 5769750a3adSRichard Cochran case CPSW_VERSION_1: 5779750a3adSRichard Cochran slave_write(slave, TX_PRIORITY_MAPPING, CPSW1_TX_PRI_MAP); 57848f5bcccSGrygorii Strashko /* Increase RX FIFO size to 5 for supporting fullduplex 57948f5bcccSGrygorii Strashko * flow control mode 58048f5bcccSGrygorii Strashko */ 58148f5bcccSGrygorii Strashko slave_write(slave, 58248f5bcccSGrygorii Strashko (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) | 58348f5bcccSGrygorii Strashko CPSW_MAX_BLKS_RX, CPSW1_MAX_BLKS); 5849750a3adSRichard Cochran break; 5859750a3adSRichard Cochran case CPSW_VERSION_2: 586c193f365SMugunthan V N case CPSW_VERSION_3: 587926489beSMugunthan V N case CPSW_VERSION_4: 5889750a3adSRichard Cochran slave_write(slave, TX_PRIORITY_MAPPING, CPSW2_TX_PRI_MAP); 58948f5bcccSGrygorii Strashko /* Increase RX FIFO size to 5 for supporting fullduplex 59048f5bcccSGrygorii Strashko * flow control mode 59148f5bcccSGrygorii Strashko */ 59248f5bcccSGrygorii Strashko slave_write(slave, 59348f5bcccSGrygorii Strashko (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) | 59448f5bcccSGrygorii Strashko CPSW_MAX_BLKS_RX, CPSW2_MAX_BLKS); 5959750a3adSRichard Cochran break; 5969750a3adSRichard Cochran } 597df828598SMugunthan V N 598df828598SMugunthan V N /* setup max packet size, and mac address */ 599cfc08345SGrygorii Strashko cpsw_sl_reg_write(slave->mac_sl, CPSW_SL_RX_MAXLEN, 600cfc08345SGrygorii Strashko cpsw->rx_packet_max); 601df828598SMugunthan V N cpsw_set_slave_mac(slave, priv); 602df828598SMugunthan V N 603df828598SMugunthan V N slave->mac_control = 0; /* no link yet */ 604df828598SMugunthan V N 6056f1f5836SIvan Khoronzhuk slave_port = cpsw_get_slave_port(slave->slave_num); 606df828598SMugunthan V N 607606f3993SIvan Khoronzhuk if (cpsw->data.dual_emac) 608d9ba8f9eSMugunthan V N cpsw_add_dual_emac_def_ale_entries(priv, slave, slave_port); 609d9ba8f9eSMugunthan V N else 6102a05a622SIvan Khoronzhuk cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast, 611e11b220fSMugunthan V N 1 << slave_port, 0, 0, ALE_MCAST_FWD_2); 612df828598SMugunthan V N 613d733f754SDavid Rivshin if (slave->data->phy_node) { 61430c57f07SSekhar Nori phy = of_phy_connect(priv->ndev, slave->data->phy_node, 6159e42f715SHeiko Schocher &cpsw_adjust_link, 0, slave->data->phy_if); 61630c57f07SSekhar Nori if (!phy) { 617f7ce9103SRob Herring dev_err(priv->dev, "phy \"%pOF\" not found on slave %d\n", 618f7ce9103SRob Herring slave->data->phy_node, 619d733f754SDavid Rivshin slave->slave_num); 620d733f754SDavid Rivshin return; 621d733f754SDavid Rivshin } 622d733f754SDavid Rivshin } else { 62330c57f07SSekhar Nori phy = phy_connect(priv->ndev, slave->data->phy_id, 624f9a8f83bSFlorian Fainelli &cpsw_adjust_link, slave->data->phy_if); 62530c57f07SSekhar Nori if (IS_ERR(phy)) { 626d733f754SDavid Rivshin dev_err(priv->dev, 627d733f754SDavid Rivshin "phy \"%s\" not found on slave %d, err %ld\n", 628d733f754SDavid Rivshin slave->data->phy_id, slave->slave_num, 62930c57f07SSekhar Nori PTR_ERR(phy)); 630d733f754SDavid Rivshin return; 631d733f754SDavid Rivshin } 632d733f754SDavid Rivshin } 633d733f754SDavid Rivshin 63430c57f07SSekhar Nori slave->phy = phy; 63530c57f07SSekhar Nori 6362220943aSAndrew Lunn phy_attached_info(slave->phy); 6372220943aSAndrew Lunn 638df828598SMugunthan V N phy_start(slave->phy); 639388367a5SMugunthan V N 640388367a5SMugunthan V N /* Configure GMII_SEL register */ 6413ff18849SGrygorii Strashko if (!IS_ERR(slave->data->ifphy)) 6423ff18849SGrygorii Strashko phy_set_mode_ext(slave->data->ifphy, PHY_MODE_ETHERNET, 6433ff18849SGrygorii Strashko slave->data->phy_if); 6443ff18849SGrygorii Strashko else 6453ff18849SGrygorii Strashko cpsw_phy_sel(cpsw->dev, slave->phy->interface, 6463ff18849SGrygorii Strashko slave->slave_num); 647df828598SMugunthan V N } 648df828598SMugunthan V N 6493b72c2feSMugunthan V N static inline void cpsw_add_default_vlan(struct cpsw_priv *priv) 6503b72c2feSMugunthan V N { 651606f3993SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 652606f3993SIvan Khoronzhuk const int vlan = cpsw->data.default_vlan; 6533b72c2feSMugunthan V N u32 reg; 6543b72c2feSMugunthan V N int i; 6551e5c4bc4SLennart Sorensen int unreg_mcast_mask; 6563b72c2feSMugunthan V N 6572a05a622SIvan Khoronzhuk reg = (cpsw->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN : 6583b72c2feSMugunthan V N CPSW2_PORT_VLAN; 6593b72c2feSMugunthan V N 6605d8d0d4dSIvan Khoronzhuk writel(vlan, &cpsw->host_port_regs->port_vlan); 6613b72c2feSMugunthan V N 662606f3993SIvan Khoronzhuk for (i = 0; i < cpsw->data.slaves; i++) 663606f3993SIvan Khoronzhuk slave_write(cpsw->slaves + i, vlan, reg); 6643b72c2feSMugunthan V N 6651e5c4bc4SLennart Sorensen if (priv->ndev->flags & IFF_ALLMULTI) 6661e5c4bc4SLennart Sorensen unreg_mcast_mask = ALE_ALL_PORTS; 6671e5c4bc4SLennart Sorensen else 6681e5c4bc4SLennart Sorensen unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2; 6691e5c4bc4SLennart Sorensen 6702a05a622SIvan Khoronzhuk cpsw_ale_add_vlan(cpsw->ale, vlan, ALE_ALL_PORTS, 67161f1cef9SGrygorii Strashko ALE_ALL_PORTS, ALE_ALL_PORTS, 67261f1cef9SGrygorii Strashko unreg_mcast_mask); 6733b72c2feSMugunthan V N } 6743b72c2feSMugunthan V N 675df828598SMugunthan V N static void cpsw_init_host_port(struct cpsw_priv *priv) 676df828598SMugunthan V N { 677d9ba8f9eSMugunthan V N u32 fifo_mode; 6785d8d0d4dSIvan Khoronzhuk u32 control_reg; 6795d8d0d4dSIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 6803b72c2feSMugunthan V N 681df828598SMugunthan V N /* soft reset the controller and initialize ale */ 6825d8d0d4dSIvan Khoronzhuk soft_reset("cpsw", &cpsw->regs->soft_reset); 6832a05a622SIvan Khoronzhuk cpsw_ale_start(cpsw->ale); 684df828598SMugunthan V N 685df828598SMugunthan V N /* switch to vlan unaware mode */ 6862a05a622SIvan Khoronzhuk cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_VLAN_AWARE, 6873b72c2feSMugunthan V N CPSW_ALE_VLAN_AWARE); 6885d8d0d4dSIvan Khoronzhuk control_reg = readl(&cpsw->regs->control); 689a3a41d2fSGrygorii Strashko control_reg |= CPSW_VLAN_AWARE | CPSW_RX_VLAN_ENCAP; 6905d8d0d4dSIvan Khoronzhuk writel(control_reg, &cpsw->regs->control); 691606f3993SIvan Khoronzhuk fifo_mode = (cpsw->data.dual_emac) ? CPSW_FIFO_DUAL_MAC_MODE : 692d9ba8f9eSMugunthan V N CPSW_FIFO_NORMAL_MODE; 6935d8d0d4dSIvan Khoronzhuk writel(fifo_mode, &cpsw->host_port_regs->tx_in_ctl); 694df828598SMugunthan V N 695df828598SMugunthan V N /* setup host port priority mapping */ 696dda5f5feSGrygorii Strashko writel_relaxed(CPDMA_TX_PRIORITY_MAP, 6975d8d0d4dSIvan Khoronzhuk &cpsw->host_port_regs->cpdma_tx_pri_map); 698dda5f5feSGrygorii Strashko writel_relaxed(0, &cpsw->host_port_regs->cpdma_rx_chan_map); 699df828598SMugunthan V N 7002a05a622SIvan Khoronzhuk cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, 701df828598SMugunthan V N ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); 702df828598SMugunthan V N 703606f3993SIvan Khoronzhuk if (!cpsw->data.dual_emac) { 7042a05a622SIvan Khoronzhuk cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr, HOST_PORT_NUM, 705d9ba8f9eSMugunthan V N 0, 0); 7062a05a622SIvan Khoronzhuk cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast, 70771a2cbb7SGrygorii Strashko ALE_PORT_HOST, 0, 0, ALE_MCAST_FWD_2); 708df828598SMugunthan V N } 709d9ba8f9eSMugunthan V N } 710df828598SMugunthan V N 7112a05a622SIvan Khoronzhuk static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_common *cpsw) 712aacebbf8SSebastian Siewior { 7133995d265SSchuyler Patton u32 slave_port; 7143995d265SSchuyler Patton 7156f1f5836SIvan Khoronzhuk slave_port = cpsw_get_slave_port(slave->slave_num); 7163995d265SSchuyler Patton 717aacebbf8SSebastian Siewior if (!slave->phy) 718aacebbf8SSebastian Siewior return; 719aacebbf8SSebastian Siewior phy_stop(slave->phy); 720aacebbf8SSebastian Siewior phy_disconnect(slave->phy); 721aacebbf8SSebastian Siewior slave->phy = NULL; 7222a05a622SIvan Khoronzhuk cpsw_ale_control_set(cpsw->ale, slave_port, 7233995d265SSchuyler Patton ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); 724cfc08345SGrygorii Strashko cpsw_sl_reset(slave->mac_sl, 100); 725cfc08345SGrygorii Strashko cpsw_sl_ctl_reset(slave->mac_sl); 726aacebbf8SSebastian Siewior } 727aacebbf8SSebastian Siewior 72800fe4712SIvan Khoronzhuk static int cpsw_restore_vlans(struct net_device *vdev, int vid, void *arg) 72900fe4712SIvan Khoronzhuk { 73000fe4712SIvan Khoronzhuk struct cpsw_priv *priv = arg; 73100fe4712SIvan Khoronzhuk 73200fe4712SIvan Khoronzhuk if (!vdev) 73300fe4712SIvan Khoronzhuk return 0; 73400fe4712SIvan Khoronzhuk 73500fe4712SIvan Khoronzhuk cpsw_ndo_vlan_rx_add_vid(priv->ndev, 0, vid); 73600fe4712SIvan Khoronzhuk return 0; 73700fe4712SIvan Khoronzhuk } 73800fe4712SIvan Khoronzhuk 7394b4255edSIvan Khoronzhuk /* restore resources after port reset */ 7404b4255edSIvan Khoronzhuk static void cpsw_restore(struct cpsw_priv *priv) 7414b4255edSIvan Khoronzhuk { 74200fe4712SIvan Khoronzhuk /* restore vlan configurations */ 74300fe4712SIvan Khoronzhuk vlan_for_each(priv->ndev, cpsw_restore_vlans, priv); 74400fe4712SIvan Khoronzhuk 7454b4255edSIvan Khoronzhuk /* restore MQPRIO offload */ 7464b4255edSIvan Khoronzhuk for_each_slave(priv, cpsw_mqprio_resume, priv); 7474b4255edSIvan Khoronzhuk 7484b4255edSIvan Khoronzhuk /* restore CBS offload */ 7494b4255edSIvan Khoronzhuk for_each_slave(priv, cpsw_cbs_resume, priv); 7504b4255edSIvan Khoronzhuk } 7514b4255edSIvan Khoronzhuk 752df828598SMugunthan V N static int cpsw_ndo_open(struct net_device *ndev) 753df828598SMugunthan V N { 754df828598SMugunthan V N struct cpsw_priv *priv = netdev_priv(ndev); 755649a1688SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 7563802dce1SIvan Khoronzhuk int ret; 757df828598SMugunthan V N u32 reg; 758df828598SMugunthan V N 75956e31bd8SIvan Khoronzhuk ret = pm_runtime_get_sync(cpsw->dev); 760108a6537SGrygorii Strashko if (ret < 0) { 76156e31bd8SIvan Khoronzhuk pm_runtime_put_noidle(cpsw->dev); 762108a6537SGrygorii Strashko return ret; 763108a6537SGrygorii Strashko } 7643fa88c51SGrygorii Strashko 765df828598SMugunthan V N netif_carrier_off(ndev); 766df828598SMugunthan V N 767e05107e6SIvan Khoronzhuk /* Notify the stack of the actual queue counts. */ 768e05107e6SIvan Khoronzhuk ret = netif_set_real_num_tx_queues(ndev, cpsw->tx_ch_num); 769e05107e6SIvan Khoronzhuk if (ret) { 770e05107e6SIvan Khoronzhuk dev_err(priv->dev, "cannot set real number of tx queues\n"); 771e05107e6SIvan Khoronzhuk goto err_cleanup; 772e05107e6SIvan Khoronzhuk } 773e05107e6SIvan Khoronzhuk 774e05107e6SIvan Khoronzhuk ret = netif_set_real_num_rx_queues(ndev, cpsw->rx_ch_num); 775e05107e6SIvan Khoronzhuk if (ret) { 776e05107e6SIvan Khoronzhuk dev_err(priv->dev, "cannot set real number of rx queues\n"); 777e05107e6SIvan Khoronzhuk goto err_cleanup; 778e05107e6SIvan Khoronzhuk } 779e05107e6SIvan Khoronzhuk 7802a05a622SIvan Khoronzhuk reg = cpsw->version; 781df828598SMugunthan V N 782df828598SMugunthan V N dev_info(priv->dev, "initializing cpsw version %d.%d (%d)\n", 783df828598SMugunthan V N CPSW_MAJOR_VERSION(reg), CPSW_MINOR_VERSION(reg), 784df828598SMugunthan V N CPSW_RTL_VERSION(reg)); 785df828598SMugunthan V N 786d5bc1613SIvan Khoronzhuk /* Initialize host and slave ports */ 787d5bc1613SIvan Khoronzhuk if (!cpsw->usage_count) 788df828598SMugunthan V N cpsw_init_host_port(priv); 789df828598SMugunthan V N for_each_slave(priv, cpsw_slave_open, priv); 790df828598SMugunthan V N 7913b72c2feSMugunthan V N /* Add default VLAN */ 792606f3993SIvan Khoronzhuk if (!cpsw->data.dual_emac) 7933b72c2feSMugunthan V N cpsw_add_default_vlan(priv); 794e6afea0bSMugunthan V N else 7952a05a622SIvan Khoronzhuk cpsw_ale_add_vlan(cpsw->ale, cpsw->data.default_vlan, 79661f1cef9SGrygorii Strashko ALE_ALL_PORTS, ALE_ALL_PORTS, 0, 0); 7973b72c2feSMugunthan V N 798d5bc1613SIvan Khoronzhuk /* initialize shared resources for every ndev */ 799d5bc1613SIvan Khoronzhuk if (!cpsw->usage_count) { 800d9ba8f9eSMugunthan V N /* disable priority elevation */ 801dda5f5feSGrygorii Strashko writel_relaxed(0, &cpsw->regs->ptype); 802df828598SMugunthan V N 803d9ba8f9eSMugunthan V N /* enable statistics collection only on all ports */ 804dda5f5feSGrygorii Strashko writel_relaxed(0x7, &cpsw->regs->stat_port_en); 805df828598SMugunthan V N 8061923d6e4SMugunthan V N /* Enable internal fifo flow control */ 8075d8d0d4dSIvan Khoronzhuk writel(0x7, &cpsw->regs->flow_control); 8081923d6e4SMugunthan V N 809dbc4ec52SIvan Khoronzhuk napi_enable(&cpsw->napi_rx); 810dbc4ec52SIvan Khoronzhuk napi_enable(&cpsw->napi_tx); 811d354eb85SMugunthan V N 812e38b5a3dSIvan Khoronzhuk if (cpsw->tx_irq_disabled) { 813e38b5a3dSIvan Khoronzhuk cpsw->tx_irq_disabled = false; 814e38b5a3dSIvan Khoronzhuk enable_irq(cpsw->irqs_table[1]); 8157da11600SMugunthan V N } 8167da11600SMugunthan V N 817e38b5a3dSIvan Khoronzhuk if (cpsw->rx_irq_disabled) { 818e38b5a3dSIvan Khoronzhuk cpsw->rx_irq_disabled = false; 819e38b5a3dSIvan Khoronzhuk enable_irq(cpsw->irqs_table[0]); 8207da11600SMugunthan V N } 8217da11600SMugunthan V N 8229ed4050cSIvan Khoronzhuk /* create rxqs for both infs in dual mac as they use same pool 8239ed4050cSIvan Khoronzhuk * and must be destroyed together when no users. 8249ed4050cSIvan Khoronzhuk */ 8259ed4050cSIvan Khoronzhuk ret = cpsw_create_xdp_rxqs(cpsw); 8269ed4050cSIvan Khoronzhuk if (ret < 0) 8279ed4050cSIvan Khoronzhuk goto err_cleanup; 8289ed4050cSIvan Khoronzhuk 8293802dce1SIvan Khoronzhuk ret = cpsw_fill_rx_channels(priv); 8303802dce1SIvan Khoronzhuk if (ret < 0) 831aacebbf8SSebastian Siewior goto err_cleanup; 832f280e89aSMugunthan V N 8332b566873SGrygorii Strashko if (cpsw->cpts) { 8348a2c9a5aSGrygorii Strashko if (cpts_register(cpsw->cpts)) 835f280e89aSMugunthan V N dev_err(priv->dev, "error registering cpts device\n"); 8362b566873SGrygorii Strashko else 8372b566873SGrygorii Strashko writel(0x10, &cpsw->wr_regs->misc_en); 8382b566873SGrygorii Strashko } 839d9ba8f9eSMugunthan V N } 840df828598SMugunthan V N 8414b4255edSIvan Khoronzhuk cpsw_restore(priv); 8424b4255edSIvan Khoronzhuk 843ff5b8ef2SMugunthan V N /* Enable Interrupt pacing if configured */ 8442a05a622SIvan Khoronzhuk if (cpsw->coal_intvl != 0) { 845ff5b8ef2SMugunthan V N struct ethtool_coalesce coal; 846ff5b8ef2SMugunthan V N 8472a05a622SIvan Khoronzhuk coal.rx_coalesce_usecs = cpsw->coal_intvl; 848f3ccfda1SYufeng Mo cpsw_set_coalesce(ndev, &coal, NULL, NULL); 849ff5b8ef2SMugunthan V N } 850ff5b8ef2SMugunthan V N 8512c836bd9SIvan Khoronzhuk cpdma_ctlr_start(cpsw->dma); 8522c836bd9SIvan Khoronzhuk cpsw_intr_enable(cpsw); 853d5bc1613SIvan Khoronzhuk cpsw->usage_count++; 854f63a975eSMugunthan V N 855df828598SMugunthan V N return 0; 856df828598SMugunthan V N 857aacebbf8SSebastian Siewior err_cleanup: 85802cacedeSIvan Khoronzhuk if (!cpsw->usage_count) { 8592c836bd9SIvan Khoronzhuk cpdma_ctlr_stop(cpsw->dma); 8609ed4050cSIvan Khoronzhuk cpsw_destroy_xdp_rxqs(cpsw); 86102cacedeSIvan Khoronzhuk } 86202cacedeSIvan Khoronzhuk 8639ed4050cSIvan Khoronzhuk for_each_slave(priv, cpsw_slave_stop, cpsw); 86456e31bd8SIvan Khoronzhuk pm_runtime_put_sync(cpsw->dev); 865aacebbf8SSebastian Siewior netif_carrier_off(priv->ndev); 866aacebbf8SSebastian Siewior return ret; 867df828598SMugunthan V N } 868df828598SMugunthan V N 869df828598SMugunthan V N static int cpsw_ndo_stop(struct net_device *ndev) 870df828598SMugunthan V N { 871df828598SMugunthan V N struct cpsw_priv *priv = netdev_priv(ndev); 872649a1688SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 873df828598SMugunthan V N 874df828598SMugunthan V N cpsw_info(priv, ifdown, "shutting down cpsw device\n"); 87515180ecaSIvan Khoronzhuk __hw_addr_ref_unsync_dev(&ndev->mc, ndev, cpsw_purge_all_mc); 876e05107e6SIvan Khoronzhuk netif_tx_stop_all_queues(priv->ndev); 877df828598SMugunthan V N netif_carrier_off(priv->ndev); 878d9ba8f9eSMugunthan V N 879d5bc1613SIvan Khoronzhuk if (cpsw->usage_count <= 1) { 880dbc4ec52SIvan Khoronzhuk napi_disable(&cpsw->napi_rx); 881dbc4ec52SIvan Khoronzhuk napi_disable(&cpsw->napi_tx); 8822a05a622SIvan Khoronzhuk cpts_unregister(cpsw->cpts); 8832c836bd9SIvan Khoronzhuk cpsw_intr_disable(cpsw); 8842c836bd9SIvan Khoronzhuk cpdma_ctlr_stop(cpsw->dma); 8852a05a622SIvan Khoronzhuk cpsw_ale_stop(cpsw->ale); 8869ed4050cSIvan Khoronzhuk cpsw_destroy_xdp_rxqs(cpsw); 887d9ba8f9eSMugunthan V N } 8882a05a622SIvan Khoronzhuk for_each_slave(priv, cpsw_slave_stop, cpsw); 8890be01b8eSIvan Khoronzhuk 8900be01b8eSIvan Khoronzhuk if (cpsw_need_resplit(cpsw)) 8919763a891SGrygorii Strashko cpsw_split_res(cpsw); 8920be01b8eSIvan Khoronzhuk 893d5bc1613SIvan Khoronzhuk cpsw->usage_count--; 89456e31bd8SIvan Khoronzhuk pm_runtime_put_sync(cpsw->dev); 895df828598SMugunthan V N return 0; 896df828598SMugunthan V N } 897df828598SMugunthan V N 898df828598SMugunthan V N static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, 899df828598SMugunthan V N struct net_device *ndev) 900df828598SMugunthan V N { 901df828598SMugunthan V N struct cpsw_priv *priv = netdev_priv(ndev); 9022c836bd9SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 903f44f8417SIvan Khoronzhuk struct cpts *cpts = cpsw->cpts; 904e05107e6SIvan Khoronzhuk struct netdev_queue *txq; 905e05107e6SIvan Khoronzhuk struct cpdma_chan *txch; 906e05107e6SIvan Khoronzhuk int ret, q_idx; 907df828598SMugunthan V N 9081f88d5d5SGrygorii Strashko if (skb_put_padto(skb, CPSW_MIN_PACKET_SIZE)) { 909df828598SMugunthan V N cpsw_err(priv, tx_err, "packet pad failed\n"); 9108dc43ddcSTobias Klauser ndev->stats.tx_dropped++; 9111bf96050SIvan Khoronzhuk return NET_XMIT_DROP; 912df828598SMugunthan V N } 913df828598SMugunthan V N 9149232b16dSMugunthan V N if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 915a9423120SIvan Khoronzhuk priv->tx_ts_enabled && cpts_can_timestamp(cpts, skb)) 9162e5b38abSRichard Cochran skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 9172e5b38abSRichard Cochran 918e05107e6SIvan Khoronzhuk q_idx = skb_get_queue_mapping(skb); 919e05107e6SIvan Khoronzhuk if (q_idx >= cpsw->tx_ch_num) 920e05107e6SIvan Khoronzhuk q_idx = q_idx % cpsw->tx_ch_num; 921e05107e6SIvan Khoronzhuk 9228feb0a19SIvan Khoronzhuk txch = cpsw->txv[q_idx].ch; 92362f94c21SGrygorii Strashko txq = netdev_get_tx_queue(ndev, q_idx); 92410ae8054SGrygorii Strashko skb_tx_timestamp(skb); 92510ae8054SGrygorii Strashko ret = cpdma_chan_submit(txch, skb, skb->data, skb->len, 92610ae8054SGrygorii Strashko priv->emac_port + cpsw->data.dual_emac); 927df828598SMugunthan V N if (unlikely(ret != 0)) { 928df828598SMugunthan V N cpsw_err(priv, tx_err, "desc submit failed\n"); 929df828598SMugunthan V N goto fail; 930df828598SMugunthan V N } 931df828598SMugunthan V N 932fae50823SMugunthan V N /* If there is no more tx desc left free then we need to 933fae50823SMugunthan V N * tell the kernel to stop sending us tx frames. 934fae50823SMugunthan V N */ 935e05107e6SIvan Khoronzhuk if (unlikely(!cpdma_check_free_tx_desc(txch))) { 936e05107e6SIvan Khoronzhuk netif_tx_stop_queue(txq); 93762f94c21SGrygorii Strashko 93862f94c21SGrygorii Strashko /* Barrier, so that stop_queue visible to other cpus */ 93962f94c21SGrygorii Strashko smp_mb__after_atomic(); 94062f94c21SGrygorii Strashko 94162f94c21SGrygorii Strashko if (cpdma_check_free_tx_desc(txch)) 94262f94c21SGrygorii Strashko netif_tx_wake_queue(txq); 943e05107e6SIvan Khoronzhuk } 944fae50823SMugunthan V N 945df828598SMugunthan V N return NETDEV_TX_OK; 946df828598SMugunthan V N fail: 9478dc43ddcSTobias Klauser ndev->stats.tx_dropped++; 948e05107e6SIvan Khoronzhuk netif_tx_stop_queue(txq); 94962f94c21SGrygorii Strashko 95062f94c21SGrygorii Strashko /* Barrier, so that stop_queue visible to other cpus */ 95162f94c21SGrygorii Strashko smp_mb__after_atomic(); 95262f94c21SGrygorii Strashko 95362f94c21SGrygorii Strashko if (cpdma_check_free_tx_desc(txch)) 95462f94c21SGrygorii Strashko netif_tx_wake_queue(txq); 95562f94c21SGrygorii Strashko 956df828598SMugunthan V N return NETDEV_TX_BUSY; 957df828598SMugunthan V N } 958df828598SMugunthan V N 959dcfd8d58SMugunthan V N static int cpsw_ndo_set_mac_address(struct net_device *ndev, void *p) 960dcfd8d58SMugunthan V N { 961dcfd8d58SMugunthan V N struct cpsw_priv *priv = netdev_priv(ndev); 962dcfd8d58SMugunthan V N struct sockaddr *addr = (struct sockaddr *)p; 963649a1688SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 964dcfd8d58SMugunthan V N int flags = 0; 965dcfd8d58SMugunthan V N u16 vid = 0; 966a6c5d14fSGrygorii Strashko int ret; 967dcfd8d58SMugunthan V N 968dcfd8d58SMugunthan V N if (!is_valid_ether_addr(addr->sa_data)) 969dcfd8d58SMugunthan V N return -EADDRNOTAVAIL; 970dcfd8d58SMugunthan V N 97156e31bd8SIvan Khoronzhuk ret = pm_runtime_get_sync(cpsw->dev); 972a6c5d14fSGrygorii Strashko if (ret < 0) { 97356e31bd8SIvan Khoronzhuk pm_runtime_put_noidle(cpsw->dev); 974a6c5d14fSGrygorii Strashko return ret; 975a6c5d14fSGrygorii Strashko } 976a6c5d14fSGrygorii Strashko 977606f3993SIvan Khoronzhuk if (cpsw->data.dual_emac) { 978606f3993SIvan Khoronzhuk vid = cpsw->slaves[priv->emac_port].port_vlan; 979dcfd8d58SMugunthan V N flags = ALE_VLAN; 980dcfd8d58SMugunthan V N } 981dcfd8d58SMugunthan V N 9822a05a622SIvan Khoronzhuk cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr, HOST_PORT_NUM, 983dcfd8d58SMugunthan V N flags, vid); 9842a05a622SIvan Khoronzhuk cpsw_ale_add_ucast(cpsw->ale, addr->sa_data, HOST_PORT_NUM, 985dcfd8d58SMugunthan V N flags, vid); 986dcfd8d58SMugunthan V N 987dcfd8d58SMugunthan V N memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN); 988*a96d317fSJakub Kicinski eth_hw_addr_set(ndev, priv->mac_addr); 989dcfd8d58SMugunthan V N for_each_slave(priv, cpsw_set_slave_mac, priv); 990dcfd8d58SMugunthan V N 99156e31bd8SIvan Khoronzhuk pm_runtime_put(cpsw->dev); 992a6c5d14fSGrygorii Strashko 993dcfd8d58SMugunthan V N return 0; 994dcfd8d58SMugunthan V N } 995dcfd8d58SMugunthan V N 9963b72c2feSMugunthan V N static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv, 9973b72c2feSMugunthan V N unsigned short vid) 9983b72c2feSMugunthan V N { 9993b72c2feSMugunthan V N int ret; 10009f6bd8faSMugunthan V N int unreg_mcast_mask = 0; 10015b3a5a14SIvan Khoronzhuk int mcast_mask; 10029f6bd8faSMugunthan V N u32 port_mask; 1003606f3993SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 10049f6bd8faSMugunthan V N 1005606f3993SIvan Khoronzhuk if (cpsw->data.dual_emac) { 10069f6bd8faSMugunthan V N port_mask = (1 << (priv->emac_port + 1)) | ALE_PORT_HOST; 10079f6bd8faSMugunthan V N 10085b3a5a14SIvan Khoronzhuk mcast_mask = ALE_PORT_HOST; 10099f6bd8faSMugunthan V N if (priv->ndev->flags & IFF_ALLMULTI) 10105b3a5a14SIvan Khoronzhuk unreg_mcast_mask = mcast_mask; 10119f6bd8faSMugunthan V N } else { 10129f6bd8faSMugunthan V N port_mask = ALE_ALL_PORTS; 10135b3a5a14SIvan Khoronzhuk mcast_mask = port_mask; 10141e5c4bc4SLennart Sorensen 10151e5c4bc4SLennart Sorensen if (priv->ndev->flags & IFF_ALLMULTI) 10161e5c4bc4SLennart Sorensen unreg_mcast_mask = ALE_ALL_PORTS; 10171e5c4bc4SLennart Sorensen else 10181e5c4bc4SLennart Sorensen unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2; 10199f6bd8faSMugunthan V N } 10203b72c2feSMugunthan V N 10212a05a622SIvan Khoronzhuk ret = cpsw_ale_add_vlan(cpsw->ale, vid, port_mask, 0, port_mask, 102261f1cef9SGrygorii Strashko unreg_mcast_mask); 10233b72c2feSMugunthan V N if (ret != 0) 10243b72c2feSMugunthan V N return ret; 10253b72c2feSMugunthan V N 10262a05a622SIvan Khoronzhuk ret = cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr, 102771a2cbb7SGrygorii Strashko HOST_PORT_NUM, ALE_VLAN, vid); 10283b72c2feSMugunthan V N if (ret != 0) 10293b72c2feSMugunthan V N goto clean_vid; 10303b72c2feSMugunthan V N 10312a05a622SIvan Khoronzhuk ret = cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast, 10325b3a5a14SIvan Khoronzhuk mcast_mask, ALE_VLAN, vid, 0); 10333b72c2feSMugunthan V N if (ret != 0) 10343b72c2feSMugunthan V N goto clean_vlan_ucast; 10353b72c2feSMugunthan V N return 0; 10363b72c2feSMugunthan V N 10373b72c2feSMugunthan V N clean_vlan_ucast: 10382a05a622SIvan Khoronzhuk cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr, 103971a2cbb7SGrygorii Strashko HOST_PORT_NUM, ALE_VLAN, vid); 10403b72c2feSMugunthan V N clean_vid: 10412a05a622SIvan Khoronzhuk cpsw_ale_del_vlan(cpsw->ale, vid, 0); 10423b72c2feSMugunthan V N return ret; 10433b72c2feSMugunthan V N } 10443b72c2feSMugunthan V N 10453b72c2feSMugunthan V N static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev, 104680d5c368SPatrick McHardy __be16 proto, u16 vid) 10473b72c2feSMugunthan V N { 10483b72c2feSMugunthan V N struct cpsw_priv *priv = netdev_priv(ndev); 1049649a1688SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 1050a6c5d14fSGrygorii Strashko int ret; 10513b72c2feSMugunthan V N 1052606f3993SIvan Khoronzhuk if (vid == cpsw->data.default_vlan) 10533b72c2feSMugunthan V N return 0; 10543b72c2feSMugunthan V N 105556e31bd8SIvan Khoronzhuk ret = pm_runtime_get_sync(cpsw->dev); 1056a6c5d14fSGrygorii Strashko if (ret < 0) { 105756e31bd8SIvan Khoronzhuk pm_runtime_put_noidle(cpsw->dev); 1058a6c5d14fSGrygorii Strashko return ret; 1059a6c5d14fSGrygorii Strashko } 1060a6c5d14fSGrygorii Strashko 1061606f3993SIvan Khoronzhuk if (cpsw->data.dual_emac) { 106202a54164SMugunthan V N /* In dual EMAC, reserved VLAN id should not be used for 106302a54164SMugunthan V N * creating VLAN interfaces as this can break the dual 106402a54164SMugunthan V N * EMAC port separation 106502a54164SMugunthan V N */ 106602a54164SMugunthan V N int i; 106702a54164SMugunthan V N 1068606f3993SIvan Khoronzhuk for (i = 0; i < cpsw->data.slaves; i++) { 1069803c4f64SIvan Khoronzhuk if (vid == cpsw->slaves[i].port_vlan) { 1070803c4f64SIvan Khoronzhuk ret = -EINVAL; 1071803c4f64SIvan Khoronzhuk goto err; 1072803c4f64SIvan Khoronzhuk } 107302a54164SMugunthan V N } 107402a54164SMugunthan V N } 107502a54164SMugunthan V N 10763b72c2feSMugunthan V N dev_info(priv->dev, "Adding vlanid %d to vlan filter\n", vid); 1077a6c5d14fSGrygorii Strashko ret = cpsw_add_vlan_ale_entry(priv, vid); 1078803c4f64SIvan Khoronzhuk err: 107956e31bd8SIvan Khoronzhuk pm_runtime_put(cpsw->dev); 1080a6c5d14fSGrygorii Strashko return ret; 10813b72c2feSMugunthan V N } 10823b72c2feSMugunthan V N 10833b72c2feSMugunthan V N static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev, 108480d5c368SPatrick McHardy __be16 proto, u16 vid) 10853b72c2feSMugunthan V N { 10863b72c2feSMugunthan V N struct cpsw_priv *priv = netdev_priv(ndev); 1087649a1688SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 10883b72c2feSMugunthan V N int ret; 10893b72c2feSMugunthan V N 1090606f3993SIvan Khoronzhuk if (vid == cpsw->data.default_vlan) 10913b72c2feSMugunthan V N return 0; 10923b72c2feSMugunthan V N 109356e31bd8SIvan Khoronzhuk ret = pm_runtime_get_sync(cpsw->dev); 1094a6c5d14fSGrygorii Strashko if (ret < 0) { 109556e31bd8SIvan Khoronzhuk pm_runtime_put_noidle(cpsw->dev); 1096a6c5d14fSGrygorii Strashko return ret; 1097a6c5d14fSGrygorii Strashko } 1098a6c5d14fSGrygorii Strashko 1099606f3993SIvan Khoronzhuk if (cpsw->data.dual_emac) { 110002a54164SMugunthan V N int i; 110102a54164SMugunthan V N 1102606f3993SIvan Khoronzhuk for (i = 0; i < cpsw->data.slaves; i++) { 1103606f3993SIvan Khoronzhuk if (vid == cpsw->slaves[i].port_vlan) 1104803c4f64SIvan Khoronzhuk goto err; 110502a54164SMugunthan V N } 110602a54164SMugunthan V N } 110702a54164SMugunthan V N 11083b72c2feSMugunthan V N dev_info(priv->dev, "removing vlanid %d from vlan filter\n", vid); 11092a05a622SIvan Khoronzhuk ret = cpsw_ale_del_vlan(cpsw->ale, vid, 0); 1110be35b982SIvan Khoronzhuk ret |= cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr, 111161f1cef9SGrygorii Strashko HOST_PORT_NUM, ALE_VLAN, vid); 1112be35b982SIvan Khoronzhuk ret |= cpsw_ale_del_mcast(cpsw->ale, priv->ndev->broadcast, 11133b72c2feSMugunthan V N 0, ALE_VLAN, vid); 111499d469fcSMurali Karicheri ret |= cpsw_ale_flush_multicast(cpsw->ale, ALE_PORT_HOST, vid); 1115803c4f64SIvan Khoronzhuk err: 111656e31bd8SIvan Khoronzhuk pm_runtime_put(cpsw->dev); 1117a6c5d14fSGrygorii Strashko return ret; 11183b72c2feSMugunthan V N } 11193b72c2feSMugunthan V N 11209ed4050cSIvan Khoronzhuk static int cpsw_ndo_xdp_xmit(struct net_device *ndev, int n, 11219ed4050cSIvan Khoronzhuk struct xdp_frame **frames, u32 flags) 11229ed4050cSIvan Khoronzhuk { 11239ed4050cSIvan Khoronzhuk struct cpsw_priv *priv = netdev_priv(ndev); 1124c5013ac1SGrygorii Strashko struct cpsw_common *cpsw = priv->cpsw; 11259ed4050cSIvan Khoronzhuk struct xdp_frame *xdpf; 1126fdc13979SLorenzo Bianconi int i, nxmit = 0, port; 11279ed4050cSIvan Khoronzhuk 11289ed4050cSIvan Khoronzhuk if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) 11299ed4050cSIvan Khoronzhuk return -EINVAL; 11309ed4050cSIvan Khoronzhuk 11319ed4050cSIvan Khoronzhuk for (i = 0; i < n; i++) { 11329ed4050cSIvan Khoronzhuk xdpf = frames[i]; 1133fdc13979SLorenzo Bianconi if (xdpf->len < CPSW_MIN_PACKET_SIZE) 1134fdc13979SLorenzo Bianconi break; 11359ed4050cSIvan Khoronzhuk 1136c5013ac1SGrygorii Strashko port = priv->emac_port + cpsw->data.dual_emac; 1137c5013ac1SGrygorii Strashko if (cpsw_xdp_tx_frame(priv, xdpf, NULL, port)) 1138fdc13979SLorenzo Bianconi break; 1139fdc13979SLorenzo Bianconi nxmit++; 11409ed4050cSIvan Khoronzhuk } 11419ed4050cSIvan Khoronzhuk 1142fdc13979SLorenzo Bianconi return nxmit; 11439ed4050cSIvan Khoronzhuk } 11449ed4050cSIvan Khoronzhuk 1145026cc9c3SDavid S. Miller #ifdef CONFIG_NET_POLL_CONTROLLER 1146026cc9c3SDavid S. Miller static void cpsw_ndo_poll_controller(struct net_device *ndev) 1147026cc9c3SDavid S. Miller { 1148026cc9c3SDavid S. Miller struct cpsw_common *cpsw = ndev_to_cpsw(ndev); 1149026cc9c3SDavid S. Miller 1150026cc9c3SDavid S. Miller cpsw_intr_disable(cpsw); 1151026cc9c3SDavid S. Miller cpsw_rx_interrupt(cpsw->irqs_table[0], cpsw); 1152026cc9c3SDavid S. Miller cpsw_tx_interrupt(cpsw->irqs_table[1], cpsw); 1153026cc9c3SDavid S. Miller cpsw_intr_enable(cpsw); 1154026cc9c3SDavid S. Miller } 1155026cc9c3SDavid S. Miller #endif 1156026cc9c3SDavid S. Miller 1157df828598SMugunthan V N static const struct net_device_ops cpsw_netdev_ops = { 1158df828598SMugunthan V N .ndo_open = cpsw_ndo_open, 1159df828598SMugunthan V N .ndo_stop = cpsw_ndo_stop, 1160df828598SMugunthan V N .ndo_start_xmit = cpsw_ndo_start_xmit, 1161dcfd8d58SMugunthan V N .ndo_set_mac_address = cpsw_ndo_set_mac_address, 1162a7605370SArnd Bergmann .ndo_eth_ioctl = cpsw_ndo_ioctl, 1163df828598SMugunthan V N .ndo_validate_addr = eth_validate_addr, 1164df828598SMugunthan V N .ndo_tx_timeout = cpsw_ndo_tx_timeout, 11655c50a856SMugunthan V N .ndo_set_rx_mode = cpsw_ndo_set_rx_mode, 116683fcad0cSIvan Khoronzhuk .ndo_set_tx_maxrate = cpsw_ndo_set_tx_maxrate, 1167df828598SMugunthan V N #ifdef CONFIG_NET_POLL_CONTROLLER 1168df828598SMugunthan V N .ndo_poll_controller = cpsw_ndo_poll_controller, 1169df828598SMugunthan V N #endif 11703b72c2feSMugunthan V N .ndo_vlan_rx_add_vid = cpsw_ndo_vlan_rx_add_vid, 11713b72c2feSMugunthan V N .ndo_vlan_rx_kill_vid = cpsw_ndo_vlan_rx_kill_vid, 11727929a668SIvan Khoronzhuk .ndo_setup_tc = cpsw_ndo_setup_tc, 11739ed4050cSIvan Khoronzhuk .ndo_bpf = cpsw_ndo_bpf, 11749ed4050cSIvan Khoronzhuk .ndo_xdp_xmit = cpsw_ndo_xdp_xmit, 1175df828598SMugunthan V N }; 1176df828598SMugunthan V N 1177df828598SMugunthan V N static void cpsw_get_drvinfo(struct net_device *ndev, 1178df828598SMugunthan V N struct ethtool_drvinfo *info) 1179df828598SMugunthan V N { 1180649a1688SIvan Khoronzhuk struct cpsw_common *cpsw = ndev_to_cpsw(ndev); 118156e31bd8SIvan Khoronzhuk struct platform_device *pdev = to_platform_device(cpsw->dev); 11827826d43fSJiri Pirko 118352c4f0ecSMugunthan V N strlcpy(info->driver, "cpsw", sizeof(info->driver)); 11847826d43fSJiri Pirko strlcpy(info->version, "1.0", sizeof(info->version)); 118556e31bd8SIvan Khoronzhuk strlcpy(info->bus_info, pdev->name, sizeof(info->bus_info)); 1186df828598SMugunthan V N } 1187df828598SMugunthan V N 11881923d6e4SMugunthan V N static int cpsw_set_pauseparam(struct net_device *ndev, 11891923d6e4SMugunthan V N struct ethtool_pauseparam *pause) 11901923d6e4SMugunthan V N { 11911923d6e4SMugunthan V N struct cpsw_priv *priv = netdev_priv(ndev); 11921923d6e4SMugunthan V N bool link; 11931923d6e4SMugunthan V N 11941923d6e4SMugunthan V N priv->rx_pause = pause->rx_pause ? true : false; 11951923d6e4SMugunthan V N priv->tx_pause = pause->tx_pause ? true : false; 11961923d6e4SMugunthan V N 11971923d6e4SMugunthan V N for_each_slave(priv, _cpsw_adjust_link, priv, &link); 11981923d6e4SMugunthan V N return 0; 11991923d6e4SMugunthan V N } 12001923d6e4SMugunthan V N 1201022d7ad7SIvan Khoronzhuk static int cpsw_set_channels(struct net_device *ndev, 1202022d7ad7SIvan Khoronzhuk struct ethtool_channels *chs) 1203022d7ad7SIvan Khoronzhuk { 1204c24eef28SGrygorii Strashko return cpsw_set_channels_common(ndev, chs, cpsw_rx_handler); 1205be034fc1SGrygorii Strashko } 1206be034fc1SGrygorii Strashko 1207df828598SMugunthan V N static const struct ethtool_ops cpsw_ethtool_ops = { 12083b6e1a4eSJakub Kicinski .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS, 1209df828598SMugunthan V N .get_drvinfo = cpsw_get_drvinfo, 1210df828598SMugunthan V N .get_msglevel = cpsw_get_msglevel, 1211df828598SMugunthan V N .set_msglevel = cpsw_set_msglevel, 1212df828598SMugunthan V N .get_link = ethtool_op_get_link, 12132e5b38abSRichard Cochran .get_ts_info = cpsw_get_ts_info, 1214ff5b8ef2SMugunthan V N .get_coalesce = cpsw_get_coalesce, 1215ff5b8ef2SMugunthan V N .set_coalesce = cpsw_set_coalesce, 1216d9718546SMugunthan V N .get_sset_count = cpsw_get_sset_count, 1217d9718546SMugunthan V N .get_strings = cpsw_get_strings, 1218d9718546SMugunthan V N .get_ethtool_stats = cpsw_get_ethtool_stats, 12191923d6e4SMugunthan V N .get_pauseparam = cpsw_get_pauseparam, 12201923d6e4SMugunthan V N .set_pauseparam = cpsw_set_pauseparam, 1221d8a64420SMatus Ujhelyi .get_wol = cpsw_get_wol, 1222d8a64420SMatus Ujhelyi .set_wol = cpsw_set_wol, 122352c4f0ecSMugunthan V N .get_regs_len = cpsw_get_regs_len, 122452c4f0ecSMugunthan V N .get_regs = cpsw_get_regs, 12257898b1daSGrygorii Strashko .begin = cpsw_ethtool_op_begin, 12267898b1daSGrygorii Strashko .complete = cpsw_ethtool_op_complete, 1227ce52c744SIvan Khoronzhuk .get_channels = cpsw_get_channels, 1228ce52c744SIvan Khoronzhuk .set_channels = cpsw_set_channels, 12292479876dSPhilippe Reynes .get_link_ksettings = cpsw_get_link_ksettings, 12302479876dSPhilippe Reynes .set_link_ksettings = cpsw_set_link_ksettings, 1231a0909949SYegor Yefremov .get_eee = cpsw_get_eee, 1232a0909949SYegor Yefremov .set_eee = cpsw_set_eee, 12336bb10c2bSYegor Yefremov .nway_reset = cpsw_nway_reset, 1234be034fc1SGrygorii Strashko .get_ringparam = cpsw_get_ringparam, 1235be034fc1SGrygorii Strashko .set_ringparam = cpsw_set_ringparam, 1236df828598SMugunthan V N }; 1237df828598SMugunthan V N 1238552165bcSDavid Rivshin static int cpsw_probe_dt(struct cpsw_platform_data *data, 12392eb32b0aSMugunthan V N struct platform_device *pdev) 12402eb32b0aSMugunthan V N { 12412eb32b0aSMugunthan V N struct device_node *node = pdev->dev.of_node; 12422eb32b0aSMugunthan V N struct device_node *slave_node; 12432eb32b0aSMugunthan V N int i = 0, ret; 12442eb32b0aSMugunthan V N u32 prop; 12452eb32b0aSMugunthan V N 12462eb32b0aSMugunthan V N if (!node) 12472eb32b0aSMugunthan V N return -EINVAL; 12482eb32b0aSMugunthan V N 12492eb32b0aSMugunthan V N if (of_property_read_u32(node, "slaves", &prop)) { 125088c99ff6SGeorge Cherian dev_err(&pdev->dev, "Missing slaves property in the DT.\n"); 12512eb32b0aSMugunthan V N return -EINVAL; 12522eb32b0aSMugunthan V N } 12532eb32b0aSMugunthan V N data->slaves = prop; 12542eb32b0aSMugunthan V N 1255e86ac13bSMugunthan V N if (of_property_read_u32(node, "active_slave", &prop)) { 125688c99ff6SGeorge Cherian dev_err(&pdev->dev, "Missing active_slave property in the DT.\n"); 1257aa1a15e2SDaniel Mack return -EINVAL; 125878ca0b28SRichard Cochran } 1259e86ac13bSMugunthan V N data->active_slave = prop; 126078ca0b28SRichard Cochran 1261a86854d0SKees Cook data->slave_data = devm_kcalloc(&pdev->dev, 1262a86854d0SKees Cook data->slaves, 1263a86854d0SKees Cook sizeof(struct cpsw_slave_data), 1264b2adaca9SJoe Perches GFP_KERNEL); 1265b2adaca9SJoe Perches if (!data->slave_data) 1266aa1a15e2SDaniel Mack return -ENOMEM; 12672eb32b0aSMugunthan V N 12682eb32b0aSMugunthan V N if (of_property_read_u32(node, "cpdma_channels", &prop)) { 126988c99ff6SGeorge Cherian dev_err(&pdev->dev, "Missing cpdma_channels property in the DT.\n"); 1270aa1a15e2SDaniel Mack return -EINVAL; 12712eb32b0aSMugunthan V N } 12722eb32b0aSMugunthan V N data->channels = prop; 12732eb32b0aSMugunthan V N 12742eb32b0aSMugunthan V N if (of_property_read_u32(node, "bd_ram_size", &prop)) { 127588c99ff6SGeorge Cherian dev_err(&pdev->dev, "Missing bd_ram_size property in the DT.\n"); 1276aa1a15e2SDaniel Mack return -EINVAL; 12772eb32b0aSMugunthan V N } 12782eb32b0aSMugunthan V N data->bd_ram_size = prop; 12792eb32b0aSMugunthan V N 12802eb32b0aSMugunthan V N if (of_property_read_u32(node, "mac_control", &prop)) { 128188c99ff6SGeorge Cherian dev_err(&pdev->dev, "Missing mac_control property in the DT.\n"); 1282aa1a15e2SDaniel Mack return -EINVAL; 12832eb32b0aSMugunthan V N } 12842eb32b0aSMugunthan V N data->mac_control = prop; 12852eb32b0aSMugunthan V N 1286281abd96SMarkus Pargmann if (of_property_read_bool(node, "dual_emac")) 1287a78766d9SJason Yan data->dual_emac = true; 1288d9ba8f9eSMugunthan V N 12891fb19aa7SVaibhav Hiremath /* 12901fb19aa7SVaibhav Hiremath * Populate all the child nodes here... 12911fb19aa7SVaibhav Hiremath */ 12921fb19aa7SVaibhav Hiremath ret = of_platform_populate(node, NULL, NULL, &pdev->dev); 12931fb19aa7SVaibhav Hiremath /* We do not want to force this, as in some cases may not have child */ 12941fb19aa7SVaibhav Hiremath if (ret) 129588c99ff6SGeorge Cherian dev_warn(&pdev->dev, "Doesn't have any child node\n"); 12961fb19aa7SVaibhav Hiremath 12978658aaf2SBen Hutchings for_each_available_child_of_node(node, slave_node) { 1298549985eeSRichard Cochran struct cpsw_slave_data *slave_data = data->slave_data + i; 1299549985eeSRichard Cochran int lenp; 1300549985eeSRichard Cochran const __be32 *parp; 1301549985eeSRichard Cochran 1302f468b10eSMarkus Pargmann /* This is no slave child node, continue */ 1303bf5849f1SRob Herring if (!of_node_name_eq(slave_node, "slave")) 1304f468b10eSMarkus Pargmann continue; 1305f468b10eSMarkus Pargmann 13063ff18849SGrygorii Strashko slave_data->ifphy = devm_of_phy_get(&pdev->dev, slave_node, 13073ff18849SGrygorii Strashko NULL); 13083ff18849SGrygorii Strashko if (!IS_ENABLED(CONFIG_TI_CPSW_PHY_SEL) && 13093ff18849SGrygorii Strashko IS_ERR(slave_data->ifphy)) { 13103ff18849SGrygorii Strashko ret = PTR_ERR(slave_data->ifphy); 13113ff18849SGrygorii Strashko dev_err(&pdev->dev, 13123ff18849SGrygorii Strashko "%d: Error retrieving port phy: %d\n", i, ret); 13133cd6e20fSNishka Dasgupta goto err_node_put; 13143ff18849SGrygorii Strashko } 13153ff18849SGrygorii Strashko 1316337d1727SMarek Vasut slave_data->slave_node = slave_node; 1317552165bcSDavid Rivshin slave_data->phy_node = of_parse_phandle(slave_node, 1318552165bcSDavid Rivshin "phy-handle", 0); 1319f1eea5c1SDavid Rivshin parp = of_get_property(slave_node, "phy_id", &lenp); 1320ae092b5bSDavid Rivshin if (slave_data->phy_node) { 1321ae092b5bSDavid Rivshin dev_dbg(&pdev->dev, 1322f7ce9103SRob Herring "slave[%d] using phy-handle=\"%pOF\"\n", 1323f7ce9103SRob Herring i, slave_data->phy_node); 1324ae092b5bSDavid Rivshin } else if (of_phy_is_fixed_link(slave_node)) { 1325dfc0a6d3SDavid Rivshin /* In the case of a fixed PHY, the DT node associated 1326dfc0a6d3SDavid Rivshin * to the PHY is the Ethernet MAC DT node. 1327dfc0a6d3SDavid Rivshin */ 13281f71e8c9SMarkus Brunner ret = of_phy_register_fixed_link(slave_node); 132923a09873SJohan Hovold if (ret) { 133023a09873SJohan Hovold if (ret != -EPROBE_DEFER) 133123a09873SJohan Hovold dev_err(&pdev->dev, "failed to register fixed-link phy: %d\n", ret); 13323cd6e20fSNishka Dasgupta goto err_node_put; 133323a09873SJohan Hovold } 133406cd6d6eSDavid Rivshin slave_data->phy_node = of_node_get(slave_node); 1335f1eea5c1SDavid Rivshin } else if (parp) { 1336f1eea5c1SDavid Rivshin u32 phyid; 1337f1eea5c1SDavid Rivshin struct device_node *mdio_node; 1338f1eea5c1SDavid Rivshin struct platform_device *mdio; 1339f1eea5c1SDavid Rivshin 1340f1eea5c1SDavid Rivshin if (lenp != (sizeof(__be32) * 2)) { 1341f1eea5c1SDavid Rivshin dev_err(&pdev->dev, "Invalid slave[%d] phy_id property\n", i); 134247276fccSMugunthan V N goto no_phy_slave; 1343549985eeSRichard Cochran } 1344549985eeSRichard Cochran mdio_node = of_find_node_by_phandle(be32_to_cpup(parp)); 1345549985eeSRichard Cochran phyid = be32_to_cpup(parp+1); 1346549985eeSRichard Cochran mdio = of_find_device_by_node(mdio_node); 134760e71ab5SJohan Hovold of_node_put(mdio_node); 13486954cc1fSJohan Hovold if (!mdio) { 134956fdb2e0SMarkus Pargmann dev_err(&pdev->dev, "Missing mdio platform device\n"); 13503cd6e20fSNishka Dasgupta ret = -EINVAL; 13513cd6e20fSNishka Dasgupta goto err_node_put; 13526954cc1fSJohan Hovold } 1353549985eeSRichard Cochran snprintf(slave_data->phy_id, sizeof(slave_data->phy_id), 1354549985eeSRichard Cochran PHY_ID_FMT, mdio->name, phyid); 135586e1d5adSJohan Hovold put_device(&mdio->dev); 1356f1eea5c1SDavid Rivshin } else { 1357ae092b5bSDavid Rivshin dev_err(&pdev->dev, 1358ae092b5bSDavid Rivshin "No slave[%d] phy_id, phy-handle, or fixed-link property\n", 1359ae092b5bSDavid Rivshin i); 1360f1eea5c1SDavid Rivshin goto no_phy_slave; 1361f1eea5c1SDavid Rivshin } 13620c65b2b9SAndrew Lunn ret = of_get_phy_mode(slave_node, &slave_data->phy_if); 13630c65b2b9SAndrew Lunn if (ret) { 136447276fccSMugunthan V N dev_err(&pdev->dev, "Missing or malformed slave[%d] phy-mode property\n", 136547276fccSMugunthan V N i); 13663cd6e20fSNishka Dasgupta goto err_node_put; 136747276fccSMugunthan V N } 136847276fccSMugunthan V N 136947276fccSMugunthan V N no_phy_slave: 137083216e39SMichael Walle ret = of_get_mac_address(slave_node, slave_data->mac_addr); 137183216e39SMichael Walle if (ret) { 1372b6745f6eSMugunthan V N ret = ti_cm_get_macid(&pdev->dev, i, 13730ba517b1SMarkus Pargmann slave_data->mac_addr); 13740ba517b1SMarkus Pargmann if (ret) 13753cd6e20fSNishka Dasgupta goto err_node_put; 13760ba517b1SMarkus Pargmann } 1377d9ba8f9eSMugunthan V N if (data->dual_emac) { 137891c4166cSMugunthan V N if (of_property_read_u32(slave_node, "dual_emac_res_vlan", 1379d9ba8f9eSMugunthan V N &prop)) { 138088c99ff6SGeorge Cherian dev_err(&pdev->dev, "Missing dual_emac_res_vlan in DT.\n"); 1381d9ba8f9eSMugunthan V N slave_data->dual_emac_res_vlan = i+1; 138288c99ff6SGeorge Cherian dev_err(&pdev->dev, "Using %d as Reserved VLAN for %d slave\n", 1383d9ba8f9eSMugunthan V N slave_data->dual_emac_res_vlan, i); 1384d9ba8f9eSMugunthan V N } else { 1385d9ba8f9eSMugunthan V N slave_data->dual_emac_res_vlan = prop; 1386d9ba8f9eSMugunthan V N } 1387d9ba8f9eSMugunthan V N } 1388d9ba8f9eSMugunthan V N 1389549985eeSRichard Cochran i++; 13903cd6e20fSNishka Dasgupta if (i == data->slaves) { 13913cd6e20fSNishka Dasgupta ret = 0; 13923cd6e20fSNishka Dasgupta goto err_node_put; 13933cd6e20fSNishka Dasgupta } 1394549985eeSRichard Cochran } 1395549985eeSRichard Cochran 13962eb32b0aSMugunthan V N return 0; 13973cd6e20fSNishka Dasgupta 13983cd6e20fSNishka Dasgupta err_node_put: 13993cd6e20fSNishka Dasgupta of_node_put(slave_node); 14003cd6e20fSNishka Dasgupta return ret; 14012eb32b0aSMugunthan V N } 14022eb32b0aSMugunthan V N 1403a4e32b0dSJohan Hovold static void cpsw_remove_dt(struct platform_device *pdev) 1404a4e32b0dSJohan Hovold { 1405bfe59032SIvan Khoronzhuk struct cpsw_common *cpsw = platform_get_drvdata(pdev); 14068cbcc466SJohan Hovold struct cpsw_platform_data *data = &cpsw->data; 14078cbcc466SJohan Hovold struct device_node *node = pdev->dev.of_node; 14088cbcc466SJohan Hovold struct device_node *slave_node; 14098cbcc466SJohan Hovold int i = 0; 14108cbcc466SJohan Hovold 14118cbcc466SJohan Hovold for_each_available_child_of_node(node, slave_node) { 14128cbcc466SJohan Hovold struct cpsw_slave_data *slave_data = &data->slave_data[i]; 14138cbcc466SJohan Hovold 1414bf5849f1SRob Herring if (!of_node_name_eq(slave_node, "slave")) 14158cbcc466SJohan Hovold continue; 14168cbcc466SJohan Hovold 14173f65047cSJohan Hovold if (of_phy_is_fixed_link(slave_node)) 14183f65047cSJohan Hovold of_phy_deregister_fixed_link(slave_node); 14198cbcc466SJohan Hovold 14208cbcc466SJohan Hovold of_node_put(slave_data->phy_node); 14218cbcc466SJohan Hovold 14228cbcc466SJohan Hovold i++; 14233cd6e20fSNishka Dasgupta if (i == data->slaves) { 14243cd6e20fSNishka Dasgupta of_node_put(slave_node); 14258cbcc466SJohan Hovold break; 14268cbcc466SJohan Hovold } 14273cd6e20fSNishka Dasgupta } 14288cbcc466SJohan Hovold 1429a4e32b0dSJohan Hovold of_platform_depopulate(&pdev->dev); 1430a4e32b0dSJohan Hovold } 1431a4e32b0dSJohan Hovold 143256e31bd8SIvan Khoronzhuk static int cpsw_probe_dual_emac(struct cpsw_priv *priv) 1433d9ba8f9eSMugunthan V N { 1434606f3993SIvan Khoronzhuk struct cpsw_common *cpsw = priv->cpsw; 1435606f3993SIvan Khoronzhuk struct cpsw_platform_data *data = &cpsw->data; 1436d9ba8f9eSMugunthan V N struct net_device *ndev; 1437d9ba8f9eSMugunthan V N struct cpsw_priv *priv_sl2; 1438e38b5a3dSIvan Khoronzhuk int ret = 0; 1439d9ba8f9eSMugunthan V N 1440d183a942SGrygorii Strashko ndev = devm_alloc_etherdev_mqs(cpsw->dev, sizeof(struct cpsw_priv), 1441d183a942SGrygorii Strashko CPSW_MAX_QUEUES, CPSW_MAX_QUEUES); 1442d9ba8f9eSMugunthan V N if (!ndev) { 144356e31bd8SIvan Khoronzhuk dev_err(cpsw->dev, "cpsw: error allocating net_device\n"); 1444d9ba8f9eSMugunthan V N return -ENOMEM; 1445d9ba8f9eSMugunthan V N } 1446d9ba8f9eSMugunthan V N 1447d9ba8f9eSMugunthan V N priv_sl2 = netdev_priv(ndev); 1448606f3993SIvan Khoronzhuk priv_sl2->cpsw = cpsw; 1449d9ba8f9eSMugunthan V N priv_sl2->ndev = ndev; 1450d9ba8f9eSMugunthan V N priv_sl2->dev = &ndev->dev; 1451d9ba8f9eSMugunthan V N priv_sl2->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG); 1452d9ba8f9eSMugunthan V N 1453d9ba8f9eSMugunthan V N if (is_valid_ether_addr(data->slave_data[1].mac_addr)) { 1454d9ba8f9eSMugunthan V N memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr, 1455d9ba8f9eSMugunthan V N ETH_ALEN); 145656e31bd8SIvan Khoronzhuk dev_info(cpsw->dev, "cpsw: Detected MACID = %pM\n", 145756e31bd8SIvan Khoronzhuk priv_sl2->mac_addr); 1458d9ba8f9eSMugunthan V N } else { 14596c1f0a1fSJoe Perches eth_random_addr(priv_sl2->mac_addr); 146056e31bd8SIvan Khoronzhuk dev_info(cpsw->dev, "cpsw: Random MACID = %pM\n", 146156e31bd8SIvan Khoronzhuk priv_sl2->mac_addr); 1462d9ba8f9eSMugunthan V N } 1463*a96d317fSJakub Kicinski eth_hw_addr_set(ndev, priv_sl2->mac_addr); 1464d9ba8f9eSMugunthan V N 1465d9ba8f9eSMugunthan V N priv_sl2->emac_port = 1; 1466606f3993SIvan Khoronzhuk cpsw->slaves[1].ndev = ndev; 1467193736c8SIvan Khoronzhuk ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX; 1468d9ba8f9eSMugunthan V N 1469d9ba8f9eSMugunthan V N ndev->netdev_ops = &cpsw_netdev_ops; 14707ad24ea4SWilfried Klaebe ndev->ethtool_ops = &cpsw_ethtool_ops; 1471d9ba8f9eSMugunthan V N 1472d9ba8f9eSMugunthan V N /* register the network device */ 147356e31bd8SIvan Khoronzhuk SET_NETDEV_DEV(ndev, cpsw->dev); 1474337d1727SMarek Vasut ndev->dev.of_node = cpsw->slaves[1].data->slave_node; 1475d9ba8f9eSMugunthan V N ret = register_netdev(ndev); 1476d183a942SGrygorii Strashko if (ret) 147756e31bd8SIvan Khoronzhuk dev_err(cpsw->dev, "cpsw: error registering net device\n"); 1478d9ba8f9eSMugunthan V N 1479d9ba8f9eSMugunthan V N return ret; 1480d9ba8f9eSMugunthan V N } 1481d9ba8f9eSMugunthan V N 14827da11600SMugunthan V N static const struct of_device_id cpsw_of_mtable[] = { 14839611d6d6SIvan Khoronzhuk { .compatible = "ti,cpsw"}, 14849611d6d6SIvan Khoronzhuk { .compatible = "ti,am335x-cpsw"}, 14859611d6d6SIvan Khoronzhuk { .compatible = "ti,am4372-cpsw"}, 14869611d6d6SIvan Khoronzhuk { .compatible = "ti,dra7-cpsw"}, 14877da11600SMugunthan V N { /* sentinel */ }, 14887da11600SMugunthan V N }; 14897da11600SMugunthan V N MODULE_DEVICE_TABLE(of, cpsw_of_mtable); 14907da11600SMugunthan V N 14919611d6d6SIvan Khoronzhuk static const struct soc_device_attribute cpsw_soc_devices[] = { 14929611d6d6SIvan Khoronzhuk { .family = "AM33xx", .revision = "ES1.0"}, 14939611d6d6SIvan Khoronzhuk { /* sentinel */ } 14949611d6d6SIvan Khoronzhuk }; 14959611d6d6SIvan Khoronzhuk 1496663e12e6SBill Pemberton static int cpsw_probe(struct platform_device *pdev) 1497df828598SMugunthan V N { 1498c8fb5668SGrygorii Strashko struct device *dev = &pdev->dev; 1499ef4183a1SIvan Khoronzhuk struct clk *clk; 1500d1bd9acfSSebastian Siewior struct cpsw_platform_data *data; 1501df828598SMugunthan V N struct net_device *ndev; 1502df828598SMugunthan V N struct cpsw_priv *priv; 1503aa1a15e2SDaniel Mack void __iomem *ss_regs; 1504c8ace62fSYueHaibing struct resource *ss_res; 15051d147ccbSMugunthan V N struct gpio_descs *mode; 15069611d6d6SIvan Khoronzhuk const struct soc_device_attribute *soc; 1507649a1688SIvan Khoronzhuk struct cpsw_common *cpsw; 1508e6a84624SGrygorii Strashko int ret = 0, ch; 15095087b915SFelipe Balbi int irq; 1510df828598SMugunthan V N 1511c8fb5668SGrygorii Strashko cpsw = devm_kzalloc(dev, sizeof(struct cpsw_common), GFP_KERNEL); 15123420ea88SJohan Hovold if (!cpsw) 15133420ea88SJohan Hovold return -ENOMEM; 15143420ea88SJohan Hovold 15152d683eaaSAntoine Tenart platform_set_drvdata(pdev, cpsw); 151651a95337SGrygorii Strashko cpsw_slave_index = cpsw_slave_index_priv; 151751a95337SGrygorii Strashko 1518c8fb5668SGrygorii Strashko cpsw->dev = dev; 1519649a1688SIvan Khoronzhuk 1520c8fb5668SGrygorii Strashko mode = devm_gpiod_get_array_optional(dev, "mode", GPIOD_OUT_LOW); 15211d147ccbSMugunthan V N if (IS_ERR(mode)) { 15221d147ccbSMugunthan V N ret = PTR_ERR(mode); 1523c8fb5668SGrygorii Strashko dev_err(dev, "gpio request failed, ret %d\n", ret); 1524d183a942SGrygorii Strashko return ret; 15251d147ccbSMugunthan V N } 15261d147ccbSMugunthan V N 152783a8471bSGrygorii Strashko clk = devm_clk_get(dev, "fck"); 152883a8471bSGrygorii Strashko if (IS_ERR(clk)) { 1529ac97a359SYueHaibing ret = PTR_ERR(clk); 153083a8471bSGrygorii Strashko dev_err(dev, "fck is not found %d\n", ret); 153183a8471bSGrygorii Strashko return ret; 153283a8471bSGrygorii Strashko } 153383a8471bSGrygorii Strashko cpsw->bus_freq_mhz = clk_get_rate(clk) / 1000000; 153483a8471bSGrygorii Strashko 1535aced6d37SYang Yingliang ss_regs = devm_platform_get_and_ioremap_resource(pdev, 0, &ss_res); 153683a8471bSGrygorii Strashko if (IS_ERR(ss_regs)) 153783a8471bSGrygorii Strashko return PTR_ERR(ss_regs); 153883a8471bSGrygorii Strashko cpsw->regs = ss_regs; 153983a8471bSGrygorii Strashko 1540c8ace62fSYueHaibing cpsw->wr_regs = devm_platform_ioremap_resource(pdev, 1); 154183a8471bSGrygorii Strashko if (IS_ERR(cpsw->wr_regs)) 154283a8471bSGrygorii Strashko return PTR_ERR(cpsw->wr_regs); 154383a8471bSGrygorii Strashko 154483a8471bSGrygorii Strashko /* RX IRQ */ 154583a8471bSGrygorii Strashko irq = platform_get_irq(pdev, 1); 154683a8471bSGrygorii Strashko if (irq < 0) 154783a8471bSGrygorii Strashko return irq; 154883a8471bSGrygorii Strashko cpsw->irqs_table[0] = irq; 154983a8471bSGrygorii Strashko 155083a8471bSGrygorii Strashko /* TX IRQ */ 155183a8471bSGrygorii Strashko irq = platform_get_irq(pdev, 2); 155283a8471bSGrygorii Strashko if (irq < 0) 155383a8471bSGrygorii Strashko return irq; 155483a8471bSGrygorii Strashko cpsw->irqs_table[1] = irq; 155583a8471bSGrygorii Strashko 155684ea9c0aSGrygorii Strashko /* get misc irq*/ 155784ea9c0aSGrygorii Strashko irq = platform_get_irq(pdev, 3); 155884ea9c0aSGrygorii Strashko if (irq <= 0) 155984ea9c0aSGrygorii Strashko return irq; 156084ea9c0aSGrygorii Strashko cpsw->misc_irq = irq; 156184ea9c0aSGrygorii Strashko 15621fb19aa7SVaibhav Hiremath /* 15631fb19aa7SVaibhav Hiremath * This may be required here for child devices. 15641fb19aa7SVaibhav Hiremath */ 1565c8fb5668SGrygorii Strashko pm_runtime_enable(dev); 15661fb19aa7SVaibhav Hiremath 1567a4e32b0dSJohan Hovold /* Need to enable clocks with runtime PM api to access module 1568a4e32b0dSJohan Hovold * registers 1569a4e32b0dSJohan Hovold */ 1570c8fb5668SGrygorii Strashko ret = pm_runtime_get_sync(dev); 1571a4e32b0dSJohan Hovold if (ret < 0) { 1572c8fb5668SGrygorii Strashko pm_runtime_put_noidle(dev); 1573aa1a15e2SDaniel Mack goto clean_runtime_disable_ret; 15742eb32b0aSMugunthan V N } 1575a4e32b0dSJohan Hovold 157623a09873SJohan Hovold ret = cpsw_probe_dt(&cpsw->data, pdev); 157723a09873SJohan Hovold if (ret) 1578a4e32b0dSJohan Hovold goto clean_dt_ret; 157923a09873SJohan Hovold 158083a8471bSGrygorii Strashko soc = soc_device_match(cpsw_soc_devices); 158183a8471bSGrygorii Strashko if (soc) 1582a78766d9SJason Yan cpsw->quirk_irq = true; 158383a8471bSGrygorii Strashko 1584606f3993SIvan Khoronzhuk data = &cpsw->data; 1585c8fb5668SGrygorii Strashko cpsw->slaves = devm_kcalloc(dev, 1586a86854d0SKees Cook data->slaves, sizeof(struct cpsw_slave), 1587df828598SMugunthan V N GFP_KERNEL); 1588606f3993SIvan Khoronzhuk if (!cpsw->slaves) { 1589aa1a15e2SDaniel Mack ret = -ENOMEM; 1590a4e32b0dSJohan Hovold goto clean_dt_ret; 1591df828598SMugunthan V N } 1592df828598SMugunthan V N 159383a8471bSGrygorii Strashko cpsw->rx_packet_max = max(rx_packet_max, CPSW_MAX_PACKET_SIZE); 1594c24eef28SGrygorii Strashko cpsw->descs_pool_size = descs_pool_size; 1595df828598SMugunthan V N 1596e6a84624SGrygorii Strashko ret = cpsw_init_common(cpsw, ss_regs, ale_ageout, 1597e6a84624SGrygorii Strashko ss_res->start + CPSW2_BD_OFFSET, 1598e6a84624SGrygorii Strashko descs_pool_size); 1599e6a84624SGrygorii Strashko if (ret) 1600a4e32b0dSJohan Hovold goto clean_dt_ret; 16018a2c9a5aSGrygorii Strashko 160283a8471bSGrygorii Strashko ch = cpsw->quirk_irq ? 0 : 7; 160383a8471bSGrygorii Strashko cpsw->txv[0].ch = cpdma_chan_create(cpsw->dma, ch, cpsw_tx_handler, 0); 160483a8471bSGrygorii Strashko if (IS_ERR(cpsw->txv[0].ch)) { 160583a8471bSGrygorii Strashko dev_err(dev, "error initializing tx dma channel\n"); 160683a8471bSGrygorii Strashko ret = PTR_ERR(cpsw->txv[0].ch); 160783a8471bSGrygorii Strashko goto clean_cpts; 1608df828598SMugunthan V N } 1609df828598SMugunthan V N 161083a8471bSGrygorii Strashko cpsw->rxv[0].ch = cpdma_chan_create(cpsw->dma, 0, cpsw_rx_handler, 1); 161183a8471bSGrygorii Strashko if (IS_ERR(cpsw->rxv[0].ch)) { 161283a8471bSGrygorii Strashko dev_err(dev, "error initializing rx dma channel\n"); 161383a8471bSGrygorii Strashko ret = PTR_ERR(cpsw->rxv[0].ch); 161483a8471bSGrygorii Strashko goto clean_cpts; 161583a8471bSGrygorii Strashko } 161683a8471bSGrygorii Strashko cpsw_split_res(cpsw); 161783a8471bSGrygorii Strashko 161883a8471bSGrygorii Strashko /* setup netdev */ 161983a8471bSGrygorii Strashko ndev = devm_alloc_etherdev_mqs(dev, sizeof(struct cpsw_priv), 162083a8471bSGrygorii Strashko CPSW_MAX_QUEUES, CPSW_MAX_QUEUES); 162183a8471bSGrygorii Strashko if (!ndev) { 162283a8471bSGrygorii Strashko dev_err(dev, "error allocating net_device\n"); 162335f735c6SZhang Changzhong ret = -ENOMEM; 162483a8471bSGrygorii Strashko goto clean_cpts; 162583a8471bSGrygorii Strashko } 162683a8471bSGrygorii Strashko 162783a8471bSGrygorii Strashko priv = netdev_priv(ndev); 162883a8471bSGrygorii Strashko priv->cpsw = cpsw; 162983a8471bSGrygorii Strashko priv->ndev = ndev; 163083a8471bSGrygorii Strashko priv->dev = dev; 163183a8471bSGrygorii Strashko priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG); 163283a8471bSGrygorii Strashko priv->emac_port = 0; 163383a8471bSGrygorii Strashko 163483a8471bSGrygorii Strashko if (is_valid_ether_addr(data->slave_data[0].mac_addr)) { 163583a8471bSGrygorii Strashko memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN); 163683a8471bSGrygorii Strashko dev_info(dev, "Detected MACID = %pM\n", priv->mac_addr); 163783a8471bSGrygorii Strashko } else { 163883a8471bSGrygorii Strashko eth_random_addr(priv->mac_addr); 163983a8471bSGrygorii Strashko dev_info(dev, "Random MACID = %pM\n", priv->mac_addr); 164083a8471bSGrygorii Strashko } 164183a8471bSGrygorii Strashko 1642*a96d317fSJakub Kicinski eth_hw_addr_set(ndev, priv->mac_addr); 164383a8471bSGrygorii Strashko 164483a8471bSGrygorii Strashko cpsw->slaves[0].ndev = ndev; 164583a8471bSGrygorii Strashko 1646a3a41d2fSGrygorii Strashko ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX; 1647070f9c65SKeerthy 1648070f9c65SKeerthy ndev->netdev_ops = &cpsw_netdev_ops; 1649070f9c65SKeerthy ndev->ethtool_ops = &cpsw_ethtool_ops; 16509611d6d6SIvan Khoronzhuk netif_napi_add(ndev, &cpsw->napi_rx, 16519611d6d6SIvan Khoronzhuk cpsw->quirk_irq ? cpsw_rx_poll : cpsw_rx_mq_poll, 16529611d6d6SIvan Khoronzhuk CPSW_POLL_WEIGHT); 16539611d6d6SIvan Khoronzhuk netif_tx_napi_add(ndev, &cpsw->napi_tx, 16549611d6d6SIvan Khoronzhuk cpsw->quirk_irq ? cpsw_tx_poll : cpsw_tx_mq_poll, 16559611d6d6SIvan Khoronzhuk CPSW_POLL_WEIGHT); 1656070f9c65SKeerthy 1657070f9c65SKeerthy /* register the network device */ 1658c8fb5668SGrygorii Strashko SET_NETDEV_DEV(ndev, dev); 1659337d1727SMarek Vasut ndev->dev.of_node = cpsw->slaves[0].data->slave_node; 1660070f9c65SKeerthy ret = register_netdev(ndev); 1661070f9c65SKeerthy if (ret) { 1662c8fb5668SGrygorii Strashko dev_err(dev, "error registering net device\n"); 1663070f9c65SKeerthy ret = -ENODEV; 166483a8471bSGrygorii Strashko goto clean_cpts; 1665070f9c65SKeerthy } 1666070f9c65SKeerthy 1667070f9c65SKeerthy if (cpsw->data.dual_emac) { 1668070f9c65SKeerthy ret = cpsw_probe_dual_emac(priv); 1669070f9c65SKeerthy if (ret) { 1670070f9c65SKeerthy cpsw_err(priv, probe, "error probe slave 2 emac interface\n"); 1671070f9c65SKeerthy goto clean_unregister_netdev_ret; 1672070f9c65SKeerthy } 1673070f9c65SKeerthy } 1674070f9c65SKeerthy 1675c03abd84SFelipe Balbi /* Grab RX and TX IRQs. Note that we also have RX_THRESHOLD and 1676c03abd84SFelipe Balbi * MISC IRQs which are always kept disabled with this driver so 1677c03abd84SFelipe Balbi * we will not request them. 1678c03abd84SFelipe Balbi * 1679c03abd84SFelipe Balbi * If anyone wants to implement support for those, make sure to 1680c03abd84SFelipe Balbi * first request and append them to irqs_table array. 1681c03abd84SFelipe Balbi */ 168283a8471bSGrygorii Strashko ret = devm_request_irq(dev, cpsw->irqs_table[0], cpsw_rx_interrupt, 1683c8fb5668SGrygorii Strashko 0, dev_name(dev), cpsw); 16845087b915SFelipe Balbi if (ret < 0) { 1685c8fb5668SGrygorii Strashko dev_err(dev, "error attaching irq (%d)\n", ret); 168683a8471bSGrygorii Strashko goto clean_unregister_netdev_ret; 1687df828598SMugunthan V N } 1688df828598SMugunthan V N 16895087b915SFelipe Balbi 169083a8471bSGrygorii Strashko ret = devm_request_irq(dev, cpsw->irqs_table[1], cpsw_tx_interrupt, 1691dbc4ec52SIvan Khoronzhuk 0, dev_name(&pdev->dev), cpsw); 16925087b915SFelipe Balbi if (ret < 0) { 1693c8fb5668SGrygorii Strashko dev_err(dev, "error attaching irq (%d)\n", ret); 169483a8471bSGrygorii Strashko goto clean_unregister_netdev_ret; 16955087b915SFelipe Balbi } 1696c2b32e58SDaniel Mack 169784ea9c0aSGrygorii Strashko if (!cpsw->cpts) 169884ea9c0aSGrygorii Strashko goto skip_cpts; 169984ea9c0aSGrygorii Strashko 170084ea9c0aSGrygorii Strashko ret = devm_request_irq(&pdev->dev, cpsw->misc_irq, cpsw_misc_interrupt, 170184ea9c0aSGrygorii Strashko 0, dev_name(&pdev->dev), cpsw); 170284ea9c0aSGrygorii Strashko if (ret < 0) { 170384ea9c0aSGrygorii Strashko dev_err(dev, "error attaching misc irq (%d)\n", ret); 170484ea9c0aSGrygorii Strashko goto clean_unregister_netdev_ret; 170584ea9c0aSGrygorii Strashko } 170684ea9c0aSGrygorii Strashko 170784ea9c0aSGrygorii Strashko /* Enable misc CPTS evnt_pend IRQ */ 170884ea9c0aSGrygorii Strashko cpts_set_irqpoll(cpsw->cpts, false); 170984ea9c0aSGrygorii Strashko 171084ea9c0aSGrygorii Strashko skip_cpts: 171190225bf0SGrygorii Strashko cpsw_notice(priv, probe, 171290225bf0SGrygorii Strashko "initialized device (regs %pa, irq %d, pool size %d)\n", 171383a8471bSGrygorii Strashko &ss_res->start, cpsw->irqs_table[0], descs_pool_size); 1714d9ba8f9eSMugunthan V N 1715c46ab7e0SJohan Hovold pm_runtime_put(&pdev->dev); 1716c46ab7e0SJohan Hovold 1717df828598SMugunthan V N return 0; 1718df828598SMugunthan V N 1719a7fe9d46SJohan Hovold clean_unregister_netdev_ret: 1720a7fe9d46SJohan Hovold unregister_netdev(ndev); 172183a8471bSGrygorii Strashko clean_cpts: 172283a8471bSGrygorii Strashko cpts_release(cpsw->cpts); 17232c836bd9SIvan Khoronzhuk cpdma_ctlr_destroy(cpsw->dma); 1724a4e32b0dSJohan Hovold clean_dt_ret: 1725a4e32b0dSJohan Hovold cpsw_remove_dt(pdev); 1726c46ab7e0SJohan Hovold pm_runtime_put_sync(&pdev->dev); 1727aa1a15e2SDaniel Mack clean_runtime_disable_ret: 1728f150bd7fSMugunthan V N pm_runtime_disable(&pdev->dev); 1729df828598SMugunthan V N return ret; 1730df828598SMugunthan V N } 1731df828598SMugunthan V N 1732663e12e6SBill Pemberton static int cpsw_remove(struct platform_device *pdev) 1733df828598SMugunthan V N { 1734bfe59032SIvan Khoronzhuk struct cpsw_common *cpsw = platform_get_drvdata(pdev); 1735bfe59032SIvan Khoronzhuk int i, ret; 17368a0b6dc9SGrygorii Strashko 17378a0b6dc9SGrygorii Strashko ret = pm_runtime_get_sync(&pdev->dev); 17388a0b6dc9SGrygorii Strashko if (ret < 0) { 17398a0b6dc9SGrygorii Strashko pm_runtime_put_noidle(&pdev->dev); 17408a0b6dc9SGrygorii Strashko return ret; 17418a0b6dc9SGrygorii Strashko } 1742df828598SMugunthan V N 1743bfe59032SIvan Khoronzhuk for (i = 0; i < cpsw->data.slaves; i++) 1744bfe59032SIvan Khoronzhuk if (cpsw->slaves[i].ndev) 1745bfe59032SIvan Khoronzhuk unregister_netdev(cpsw->slaves[i].ndev); 1746df828598SMugunthan V N 17478a2c9a5aSGrygorii Strashko cpts_release(cpsw->cpts); 17482c836bd9SIvan Khoronzhuk cpdma_ctlr_destroy(cpsw->dma); 1749a4e32b0dSJohan Hovold cpsw_remove_dt(pdev); 17508a0b6dc9SGrygorii Strashko pm_runtime_put_sync(&pdev->dev); 17518a0b6dc9SGrygorii Strashko pm_runtime_disable(&pdev->dev); 1752df828598SMugunthan V N return 0; 1753df828598SMugunthan V N } 1754df828598SMugunthan V N 17558963a504SGrygorii Strashko #ifdef CONFIG_PM_SLEEP 1756df828598SMugunthan V N static int cpsw_suspend(struct device *dev) 1757df828598SMugunthan V N { 17582f9b0d93SKeerthy struct cpsw_common *cpsw = dev_get_drvdata(dev); 1759618073e3SMugunthan V N int i; 1760618073e3SMugunthan V N 17614c64b83dSGrygorii Strashko rtnl_lock(); 17624c64b83dSGrygorii Strashko 17632f9b0d93SKeerthy for (i = 0; i < cpsw->data.slaves; i++) 17642f9b0d93SKeerthy if (cpsw->slaves[i].ndev) 1765606f3993SIvan Khoronzhuk if (netif_running(cpsw->slaves[i].ndev)) 1766606f3993SIvan Khoronzhuk cpsw_ndo_stop(cpsw->slaves[i].ndev); 17671e7a2e21SDaniel Mack 17684c64b83dSGrygorii Strashko rtnl_unlock(); 17694c64b83dSGrygorii Strashko 1770739683b4SMugunthan V N /* Select sleep pin state */ 177156e31bd8SIvan Khoronzhuk pinctrl_pm_select_sleep_state(dev); 1772739683b4SMugunthan V N 1773df828598SMugunthan V N return 0; 1774df828598SMugunthan V N } 1775df828598SMugunthan V N 1776df828598SMugunthan V N static int cpsw_resume(struct device *dev) 1777df828598SMugunthan V N { 17782f9b0d93SKeerthy struct cpsw_common *cpsw = dev_get_drvdata(dev); 17792f9b0d93SKeerthy int i; 1780df828598SMugunthan V N 1781739683b4SMugunthan V N /* Select default pin state */ 178256e31bd8SIvan Khoronzhuk pinctrl_pm_select_default_state(dev); 1783739683b4SMugunthan V N 17844ccfd638SGrygorii Strashko /* shut up ASSERT_RTNL() warning in netif_set_real_num_tx/rx_queues */ 17854ccfd638SGrygorii Strashko rtnl_lock(); 1786618073e3SMugunthan V N 17872f9b0d93SKeerthy for (i = 0; i < cpsw->data.slaves; i++) 17882f9b0d93SKeerthy if (cpsw->slaves[i].ndev) 1789606f3993SIvan Khoronzhuk if (netif_running(cpsw->slaves[i].ndev)) 1790606f3993SIvan Khoronzhuk cpsw_ndo_open(cpsw->slaves[i].ndev); 17912f9b0d93SKeerthy 17924ccfd638SGrygorii Strashko rtnl_unlock(); 17934ccfd638SGrygorii Strashko 1794df828598SMugunthan V N return 0; 1795df828598SMugunthan V N } 17968963a504SGrygorii Strashko #endif 1797df828598SMugunthan V N 17988963a504SGrygorii Strashko static SIMPLE_DEV_PM_OPS(cpsw_pm_ops, cpsw_suspend, cpsw_resume); 1799df828598SMugunthan V N 1800df828598SMugunthan V N static struct platform_driver cpsw_driver = { 1801df828598SMugunthan V N .driver = { 1802df828598SMugunthan V N .name = "cpsw", 1803df828598SMugunthan V N .pm = &cpsw_pm_ops, 18041e5c76d4SSachin Kamat .of_match_table = cpsw_of_mtable, 1805df828598SMugunthan V N }, 1806df828598SMugunthan V N .probe = cpsw_probe, 1807663e12e6SBill Pemberton .remove = cpsw_remove, 1808df828598SMugunthan V N }; 1809df828598SMugunthan V N 18106fb3b6b5SGrygorii Strashko module_platform_driver(cpsw_driver); 1811df828598SMugunthan V N 1812df828598SMugunthan V N MODULE_LICENSE("GPL"); 1813df828598SMugunthan V N MODULE_AUTHOR("Cyril Chemparathy <cyril@ti.com>"); 1814df828598SMugunthan V N MODULE_AUTHOR("Mugunthan V N <mugunthanvnm@ti.com>"); 1815df828598SMugunthan V N MODULE_DESCRIPTION("TI CPSW Ethernet driver"); 1816