18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Texas Instruments Ethernet Switch Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Texas Instruments 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/timer.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/irqreturn.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 188c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 208c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h> 218c2ecf20Sopenharmony_ci#include <linux/phy.h> 228c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 238c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 248c2ecf20Sopenharmony_ci#include <linux/delay.h> 258c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 268c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 278c2ecf20Sopenharmony_ci#include <linux/of.h> 288c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 298c2ecf20Sopenharmony_ci#include <linux/of_net.h> 308c2ecf20Sopenharmony_ci#include <linux/of_device.h> 318c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 328c2ecf20Sopenharmony_ci#include <linux/kmemleak.h> 338c2ecf20Sopenharmony_ci#include <linux/sys_soc.h> 348c2ecf20Sopenharmony_ci#include <net/page_pool.h> 358c2ecf20Sopenharmony_ci#include <linux/bpf.h> 368c2ecf20Sopenharmony_ci#include <linux/bpf_trace.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 398c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include "cpsw.h" 428c2ecf20Sopenharmony_ci#include "cpsw_ale.h" 438c2ecf20Sopenharmony_ci#include "cpsw_priv.h" 448c2ecf20Sopenharmony_ci#include "cpsw_sl.h" 458c2ecf20Sopenharmony_ci#include "cpts.h" 468c2ecf20Sopenharmony_ci#include "davinci_cpdma.h" 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int debug_level; 518c2ecf20Sopenharmony_cimodule_param(debug_level, int, 0); 528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_level, "cpsw debug level (NETIF_MSG bits)"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int ale_ageout = 10; 558c2ecf20Sopenharmony_cimodule_param(ale_ageout, int, 0); 568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ale_ageout, "cpsw ale ageout interval (seconds)"); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int rx_packet_max = CPSW_MAX_PACKET_SIZE; 598c2ecf20Sopenharmony_cimodule_param(rx_packet_max, int, 0); 608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_packet_max, "maximum receive packet size (bytes)"); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int descs_pool_size = CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT; 638c2ecf20Sopenharmony_cimodule_param(descs_pool_size, int, 0444); 648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(descs_pool_size, "Number of CPDMA CPPI descriptors in pool"); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define for_each_slave(priv, func, arg...) \ 678c2ecf20Sopenharmony_ci do { \ 688c2ecf20Sopenharmony_ci struct cpsw_slave *slave; \ 698c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = (priv)->cpsw; \ 708c2ecf20Sopenharmony_ci int n; \ 718c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac) \ 728c2ecf20Sopenharmony_ci (func)((cpsw)->slaves + priv->emac_port, ##arg);\ 738c2ecf20Sopenharmony_ci else \ 748c2ecf20Sopenharmony_ci for (n = cpsw->data.slaves, \ 758c2ecf20Sopenharmony_ci slave = cpsw->slaves; \ 768c2ecf20Sopenharmony_ci n; n--) \ 778c2ecf20Sopenharmony_ci (func)(slave++, ##arg); \ 788c2ecf20Sopenharmony_ci } while (0) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int cpsw_slave_index_priv(struct cpsw_common *cpsw, 818c2ecf20Sopenharmony_ci struct cpsw_priv *priv) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci return cpsw->data.dual_emac ? priv->emac_port : cpsw->data.active_slave; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int cpsw_get_slave_port(u32 slave_num) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci return slave_num + 1; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev, 928c2ecf20Sopenharmony_ci __be16 proto, u16 vid); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void cpsw_set_promiscious(struct net_device *ndev, bool enable) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = ndev_to_cpsw(ndev); 978c2ecf20Sopenharmony_ci struct cpsw_ale *ale = cpsw->ale; 988c2ecf20Sopenharmony_ci int i; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac) { 1018c2ecf20Sopenharmony_ci bool flag = false; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Enabling promiscuous mode for one interface will be 1048c2ecf20Sopenharmony_ci * common for both the interface as the interface shares 1058c2ecf20Sopenharmony_ci * the same hardware resource. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci for (i = 0; i < cpsw->data.slaves; i++) 1088c2ecf20Sopenharmony_ci if (cpsw->slaves[i].ndev->flags & IFF_PROMISC) 1098c2ecf20Sopenharmony_ci flag = true; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (!enable && flag) { 1128c2ecf20Sopenharmony_ci enable = true; 1138c2ecf20Sopenharmony_ci dev_err(&ndev->dev, "promiscuity not disabled as the other interface is still in promiscuity mode\n"); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (enable) { 1178c2ecf20Sopenharmony_ci /* Enable Bypass */ 1188c2ecf20Sopenharmony_ci cpsw_ale_control_set(ale, 0, ALE_BYPASS, 1); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci dev_dbg(&ndev->dev, "promiscuity enabled\n"); 1218c2ecf20Sopenharmony_ci } else { 1228c2ecf20Sopenharmony_ci /* Disable Bypass */ 1238c2ecf20Sopenharmony_ci cpsw_ale_control_set(ale, 0, ALE_BYPASS, 0); 1248c2ecf20Sopenharmony_ci dev_dbg(&ndev->dev, "promiscuity disabled\n"); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci } else { 1278c2ecf20Sopenharmony_ci if (enable) { 1288c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + HZ; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* Disable Learn for all ports (host is port 0 and slaves are port 1 and up */ 1318c2ecf20Sopenharmony_ci for (i = 0; i <= cpsw->data.slaves; i++) { 1328c2ecf20Sopenharmony_ci cpsw_ale_control_set(ale, i, 1338c2ecf20Sopenharmony_ci ALE_PORT_NOLEARN, 1); 1348c2ecf20Sopenharmony_ci cpsw_ale_control_set(ale, i, 1358c2ecf20Sopenharmony_ci ALE_PORT_NO_SA_UPDATE, 1); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* Clear All Untouched entries */ 1398c2ecf20Sopenharmony_ci cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1); 1408c2ecf20Sopenharmony_ci do { 1418c2ecf20Sopenharmony_ci cpu_relax(); 1428c2ecf20Sopenharmony_ci if (cpsw_ale_control_get(ale, 0, ALE_AGEOUT)) 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci } while (time_after(timeout, jiffies)); 1458c2ecf20Sopenharmony_ci cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Clear all mcast from ALE */ 1488c2ecf20Sopenharmony_ci cpsw_ale_flush_multicast(ale, ALE_ALL_PORTS, -1); 1498c2ecf20Sopenharmony_ci __hw_addr_ref_unsync_dev(&ndev->mc, ndev, NULL); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Flood All Unicast Packets to Host port */ 1528c2ecf20Sopenharmony_ci cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 1); 1538c2ecf20Sopenharmony_ci dev_dbg(&ndev->dev, "promiscuity enabled\n"); 1548c2ecf20Sopenharmony_ci } else { 1558c2ecf20Sopenharmony_ci /* Don't Flood All Unicast Packets to Host port */ 1568c2ecf20Sopenharmony_ci cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 0); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Enable Learn for all ports (host is port 0 and slaves are port 1 and up */ 1598c2ecf20Sopenharmony_ci for (i = 0; i <= cpsw->data.slaves; i++) { 1608c2ecf20Sopenharmony_ci cpsw_ale_control_set(ale, i, 1618c2ecf20Sopenharmony_ci ALE_PORT_NOLEARN, 0); 1628c2ecf20Sopenharmony_ci cpsw_ale_control_set(ale, i, 1638c2ecf20Sopenharmony_ci ALE_PORT_NO_SA_UPDATE, 0); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci dev_dbg(&ndev->dev, "promiscuity disabled\n"); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/** 1718c2ecf20Sopenharmony_ci * cpsw_set_mc - adds multicast entry to the table if it's not added or deletes 1728c2ecf20Sopenharmony_ci * if it's not deleted 1738c2ecf20Sopenharmony_ci * @ndev: device to sync 1748c2ecf20Sopenharmony_ci * @addr: address to be added or deleted 1758c2ecf20Sopenharmony_ci * @vid: vlan id, if vid < 0 set/unset address for real device 1768c2ecf20Sopenharmony_ci * @add: add address if the flag is set or remove otherwise 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_cistatic int cpsw_set_mc(struct net_device *ndev, const u8 *addr, 1798c2ecf20Sopenharmony_ci int vid, int add) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct cpsw_priv *priv = netdev_priv(ndev); 1828c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 1838c2ecf20Sopenharmony_ci int mask, flags, ret; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (vid < 0) { 1868c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac) 1878c2ecf20Sopenharmony_ci vid = cpsw->slaves[priv->emac_port].port_vlan; 1888c2ecf20Sopenharmony_ci else 1898c2ecf20Sopenharmony_ci vid = 0; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci mask = cpsw->data.dual_emac ? ALE_PORT_HOST : ALE_ALL_PORTS; 1938c2ecf20Sopenharmony_ci flags = vid ? ALE_VLAN : 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (add) 1968c2ecf20Sopenharmony_ci ret = cpsw_ale_add_mcast(cpsw->ale, addr, mask, flags, vid, 0); 1978c2ecf20Sopenharmony_ci else 1988c2ecf20Sopenharmony_ci ret = cpsw_ale_del_mcast(cpsw->ale, addr, 0, flags, vid); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return ret; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int cpsw_update_vlan_mc(struct net_device *vdev, int vid, void *ctx) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct addr_sync_ctx *sync_ctx = ctx; 2068c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 2078c2ecf20Sopenharmony_ci int found = 0, ret = 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!vdev || !(vdev->flags & IFF_UP)) 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* vlan address is relevant if its sync_cnt != 0 */ 2138c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, vdev) { 2148c2ecf20Sopenharmony_ci if (ether_addr_equal(ha->addr, sync_ctx->addr)) { 2158c2ecf20Sopenharmony_ci found = ha->sync_cnt; 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (found) 2218c2ecf20Sopenharmony_ci sync_ctx->consumed++; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (sync_ctx->flush) { 2248c2ecf20Sopenharmony_ci if (!found) 2258c2ecf20Sopenharmony_ci cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0); 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (found) 2308c2ecf20Sopenharmony_ci ret = cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 1); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return ret; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr, int num) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct addr_sync_ctx sync_ctx; 2388c2ecf20Sopenharmony_ci int ret; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci sync_ctx.consumed = 0; 2418c2ecf20Sopenharmony_ci sync_ctx.addr = addr; 2428c2ecf20Sopenharmony_ci sync_ctx.ndev = ndev; 2438c2ecf20Sopenharmony_ci sync_ctx.flush = 0; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ret = vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx); 2468c2ecf20Sopenharmony_ci if (sync_ctx.consumed < num && !ret) 2478c2ecf20Sopenharmony_ci ret = cpsw_set_mc(ndev, addr, -1, 1); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr, int num) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct addr_sync_ctx sync_ctx; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci sync_ctx.consumed = 0; 2578c2ecf20Sopenharmony_ci sync_ctx.addr = addr; 2588c2ecf20Sopenharmony_ci sync_ctx.ndev = ndev; 2598c2ecf20Sopenharmony_ci sync_ctx.flush = 1; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx); 2628c2ecf20Sopenharmony_ci if (sync_ctx.consumed == num) 2638c2ecf20Sopenharmony_ci cpsw_set_mc(ndev, addr, -1, 0); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int cpsw_purge_vlan_mc(struct net_device *vdev, int vid, void *ctx) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct addr_sync_ctx *sync_ctx = ctx; 2718c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 2728c2ecf20Sopenharmony_ci int found = 0; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (!vdev || !(vdev->flags & IFF_UP)) 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* vlan address is relevant if its sync_cnt != 0 */ 2788c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, vdev) { 2798c2ecf20Sopenharmony_ci if (ether_addr_equal(ha->addr, sync_ctx->addr)) { 2808c2ecf20Sopenharmony_ci found = ha->sync_cnt; 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (!found) 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci sync_ctx->consumed++; 2898c2ecf20Sopenharmony_ci cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0); 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct addr_sync_ctx sync_ctx; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci sync_ctx.addr = addr; 2988c2ecf20Sopenharmony_ci sync_ctx.ndev = ndev; 2998c2ecf20Sopenharmony_ci sync_ctx.consumed = 0; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci vlan_for_each(ndev, cpsw_purge_vlan_mc, &sync_ctx); 3028c2ecf20Sopenharmony_ci if (sync_ctx.consumed < num) 3038c2ecf20Sopenharmony_ci cpsw_set_mc(ndev, addr, -1, 0); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic void cpsw_ndo_set_rx_mode(struct net_device *ndev) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct cpsw_priv *priv = netdev_priv(ndev); 3118c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 3128c2ecf20Sopenharmony_ci int slave_port = -1; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac) 3158c2ecf20Sopenharmony_ci slave_port = priv->emac_port + 1; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (ndev->flags & IFF_PROMISC) { 3188c2ecf20Sopenharmony_ci /* Enable promiscuous mode */ 3198c2ecf20Sopenharmony_ci cpsw_set_promiscious(ndev, true); 3208c2ecf20Sopenharmony_ci cpsw_ale_set_allmulti(cpsw->ale, IFF_ALLMULTI, slave_port); 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci } else { 3238c2ecf20Sopenharmony_ci /* Disable promiscuous mode */ 3248c2ecf20Sopenharmony_ci cpsw_set_promiscious(ndev, false); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* Restore allmulti on vlans if necessary */ 3288c2ecf20Sopenharmony_ci cpsw_ale_set_allmulti(cpsw->ale, 3298c2ecf20Sopenharmony_ci ndev->flags & IFF_ALLMULTI, slave_port); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* add/remove mcast address either for real netdev or for vlan */ 3328c2ecf20Sopenharmony_ci __hw_addr_ref_sync_dev(&ndev->mc, ndev, cpsw_add_mc_addr, 3338c2ecf20Sopenharmony_ci cpsw_del_mc_addr); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic unsigned int cpsw_rxbuf_total_len(unsigned int len) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci len += CPSW_HEADROOM; 3398c2ecf20Sopenharmony_ci len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return SKB_DATA_ALIGN(len); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void cpsw_rx_handler(void *token, int len, int status) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct page *new_page, *page = token; 3478c2ecf20Sopenharmony_ci void *pa = page_address(page); 3488c2ecf20Sopenharmony_ci struct cpsw_meta_xdp *xmeta = pa + CPSW_XMETA_OFFSET; 3498c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = ndev_to_cpsw(xmeta->ndev); 3508c2ecf20Sopenharmony_ci int pkt_size = cpsw->rx_packet_max; 3518c2ecf20Sopenharmony_ci int ret = 0, port, ch = xmeta->ch; 3528c2ecf20Sopenharmony_ci int headroom = CPSW_HEADROOM; 3538c2ecf20Sopenharmony_ci struct net_device *ndev = xmeta->ndev; 3548c2ecf20Sopenharmony_ci struct cpsw_priv *priv; 3558c2ecf20Sopenharmony_ci struct page_pool *pool; 3568c2ecf20Sopenharmony_ci struct sk_buff *skb; 3578c2ecf20Sopenharmony_ci struct xdp_buff xdp; 3588c2ecf20Sopenharmony_ci dma_addr_t dma; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac && status >= 0) { 3618c2ecf20Sopenharmony_ci port = CPDMA_RX_SOURCE_PORT(status); 3628c2ecf20Sopenharmony_ci if (port) 3638c2ecf20Sopenharmony_ci ndev = cpsw->slaves[--port].ndev; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci priv = netdev_priv(ndev); 3678c2ecf20Sopenharmony_ci pool = cpsw->page_pool[ch]; 3688c2ecf20Sopenharmony_ci if (unlikely(status < 0) || unlikely(!netif_running(ndev))) { 3698c2ecf20Sopenharmony_ci /* In dual emac mode check for all interfaces */ 3708c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac && cpsw->usage_count && 3718c2ecf20Sopenharmony_ci (status >= 0)) { 3728c2ecf20Sopenharmony_ci /* The packet received is for the interface which 3738c2ecf20Sopenharmony_ci * is already down and the other interface is up 3748c2ecf20Sopenharmony_ci * and running, instead of freeing which results 3758c2ecf20Sopenharmony_ci * in reducing of the number of rx descriptor in 3768c2ecf20Sopenharmony_ci * DMA engine, requeue page back to cpdma. 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci new_page = page; 3798c2ecf20Sopenharmony_ci goto requeue; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* the interface is going down, pages are purged */ 3838c2ecf20Sopenharmony_ci page_pool_recycle_direct(pool, page); 3848c2ecf20Sopenharmony_ci return; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci new_page = page_pool_dev_alloc_pages(pool); 3888c2ecf20Sopenharmony_ci if (unlikely(!new_page)) { 3898c2ecf20Sopenharmony_ci new_page = page; 3908c2ecf20Sopenharmony_ci ndev->stats.rx_dropped++; 3918c2ecf20Sopenharmony_ci goto requeue; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (priv->xdp_prog) { 3958c2ecf20Sopenharmony_ci if (status & CPDMA_RX_VLAN_ENCAP) { 3968c2ecf20Sopenharmony_ci xdp.data = pa + CPSW_HEADROOM + 3978c2ecf20Sopenharmony_ci CPSW_RX_VLAN_ENCAP_HDR_SIZE; 3988c2ecf20Sopenharmony_ci xdp.data_end = xdp.data + len - 3998c2ecf20Sopenharmony_ci CPSW_RX_VLAN_ENCAP_HDR_SIZE; 4008c2ecf20Sopenharmony_ci } else { 4018c2ecf20Sopenharmony_ci xdp.data = pa + CPSW_HEADROOM; 4028c2ecf20Sopenharmony_ci xdp.data_end = xdp.data + len; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci xdp_set_data_meta_invalid(&xdp); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci xdp.data_hard_start = pa; 4088c2ecf20Sopenharmony_ci xdp.rxq = &priv->xdp_rxq[ch]; 4098c2ecf20Sopenharmony_ci xdp.frame_sz = PAGE_SIZE; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci port = priv->emac_port + cpsw->data.dual_emac; 4128c2ecf20Sopenharmony_ci ret = cpsw_run_xdp(priv, ch, &xdp, page, port); 4138c2ecf20Sopenharmony_ci if (ret != CPSW_XDP_PASS) 4148c2ecf20Sopenharmony_ci goto requeue; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* XDP prog might have changed packet data and boundaries */ 4178c2ecf20Sopenharmony_ci len = xdp.data_end - xdp.data; 4188c2ecf20Sopenharmony_ci headroom = xdp.data - xdp.data_hard_start; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* XDP prog can modify vlan tag, so can't use encap header */ 4218c2ecf20Sopenharmony_ci status &= ~CPDMA_RX_VLAN_ENCAP; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* pass skb to netstack if no XDP prog or returned XDP_PASS */ 4258c2ecf20Sopenharmony_ci skb = build_skb(pa, cpsw_rxbuf_total_len(pkt_size)); 4268c2ecf20Sopenharmony_ci if (!skb) { 4278c2ecf20Sopenharmony_ci ndev->stats.rx_dropped++; 4288c2ecf20Sopenharmony_ci page_pool_recycle_direct(pool, page); 4298c2ecf20Sopenharmony_ci goto requeue; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci skb_reserve(skb, headroom); 4338c2ecf20Sopenharmony_ci skb_put(skb, len); 4348c2ecf20Sopenharmony_ci skb->dev = ndev; 4358c2ecf20Sopenharmony_ci if (status & CPDMA_RX_VLAN_ENCAP) 4368c2ecf20Sopenharmony_ci cpsw_rx_vlan_encap(skb); 4378c2ecf20Sopenharmony_ci if (priv->rx_ts_enabled) 4388c2ecf20Sopenharmony_ci cpts_rx_timestamp(cpsw->cpts, skb); 4398c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* unmap page as no netstack skb page recycling */ 4428c2ecf20Sopenharmony_ci page_pool_release_page(pool, page); 4438c2ecf20Sopenharmony_ci netif_receive_skb(skb); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci ndev->stats.rx_bytes += len; 4468c2ecf20Sopenharmony_ci ndev->stats.rx_packets++; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cirequeue: 4498c2ecf20Sopenharmony_ci xmeta = page_address(new_page) + CPSW_XMETA_OFFSET; 4508c2ecf20Sopenharmony_ci xmeta->ndev = ndev; 4518c2ecf20Sopenharmony_ci xmeta->ch = ch; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci dma = page_pool_get_dma_addr(new_page) + CPSW_HEADROOM; 4548c2ecf20Sopenharmony_ci ret = cpdma_chan_submit_mapped(cpsw->rxv[ch].ch, new_page, dma, 4558c2ecf20Sopenharmony_ci pkt_size, 0); 4568c2ecf20Sopenharmony_ci if (ret < 0) { 4578c2ecf20Sopenharmony_ci WARN_ON(ret == -ENOMEM); 4588c2ecf20Sopenharmony_ci page_pool_recycle_direct(pool, new_page); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic void _cpsw_adjust_link(struct cpsw_slave *slave, 4638c2ecf20Sopenharmony_ci struct cpsw_priv *priv, bool *link) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct phy_device *phy = slave->phy; 4668c2ecf20Sopenharmony_ci u32 mac_control = 0; 4678c2ecf20Sopenharmony_ci u32 slave_port; 4688c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (!phy) 4718c2ecf20Sopenharmony_ci return; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci slave_port = cpsw_get_slave_port(slave->slave_num); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (phy->link) { 4768c2ecf20Sopenharmony_ci mac_control = CPSW_SL_CTL_GMII_EN; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (phy->speed == 1000) 4798c2ecf20Sopenharmony_ci mac_control |= CPSW_SL_CTL_GIG; 4808c2ecf20Sopenharmony_ci if (phy->duplex) 4818c2ecf20Sopenharmony_ci mac_control |= CPSW_SL_CTL_FULLDUPLEX; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* set speed_in input in case RMII mode is used in 100Mbps */ 4848c2ecf20Sopenharmony_ci if (phy->speed == 100) 4858c2ecf20Sopenharmony_ci mac_control |= CPSW_SL_CTL_IFCTL_A; 4868c2ecf20Sopenharmony_ci /* in band mode only works in 10Mbps RGMII mode */ 4878c2ecf20Sopenharmony_ci else if ((phy->speed == 10) && phy_interface_is_rgmii(phy)) 4888c2ecf20Sopenharmony_ci mac_control |= CPSW_SL_CTL_EXT_EN; /* In Band mode */ 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (priv->rx_pause) 4918c2ecf20Sopenharmony_ci mac_control |= CPSW_SL_CTL_RX_FLOW_EN; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (priv->tx_pause) 4948c2ecf20Sopenharmony_ci mac_control |= CPSW_SL_CTL_TX_FLOW_EN; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (mac_control != slave->mac_control) 4978c2ecf20Sopenharmony_ci cpsw_sl_ctl_set(slave->mac_sl, mac_control); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* enable forwarding */ 5008c2ecf20Sopenharmony_ci cpsw_ale_control_set(cpsw->ale, slave_port, 5018c2ecf20Sopenharmony_ci ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci *link = true; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (priv->shp_cfg_speed && 5068c2ecf20Sopenharmony_ci priv->shp_cfg_speed != slave->phy->speed && 5078c2ecf20Sopenharmony_ci !cpsw_shp_is_off(priv)) 5088c2ecf20Sopenharmony_ci dev_warn(priv->dev, 5098c2ecf20Sopenharmony_ci "Speed was changed, CBS shaper speeds are changed!"); 5108c2ecf20Sopenharmony_ci } else { 5118c2ecf20Sopenharmony_ci mac_control = 0; 5128c2ecf20Sopenharmony_ci /* disable forwarding */ 5138c2ecf20Sopenharmony_ci cpsw_ale_control_set(cpsw->ale, slave_port, 5148c2ecf20Sopenharmony_ci ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci cpsw_sl_wait_for_idle(slave->mac_sl, 100); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci cpsw_sl_ctl_reset(slave->mac_sl); 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (mac_control != slave->mac_control) 5228c2ecf20Sopenharmony_ci phy_print_status(phy); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci slave->mac_control = mac_control; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void cpsw_adjust_link(struct net_device *ndev) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct cpsw_priv *priv = netdev_priv(ndev); 5308c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 5318c2ecf20Sopenharmony_ci bool link = false; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci for_each_slave(priv, _cpsw_adjust_link, priv, &link); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (link) { 5368c2ecf20Sopenharmony_ci if (cpsw_need_resplit(cpsw)) 5378c2ecf20Sopenharmony_ci cpsw_split_res(cpsw); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci netif_carrier_on(ndev); 5408c2ecf20Sopenharmony_ci if (netif_running(ndev)) 5418c2ecf20Sopenharmony_ci netif_tx_wake_all_queues(ndev); 5428c2ecf20Sopenharmony_ci } else { 5438c2ecf20Sopenharmony_ci netif_carrier_off(ndev); 5448c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(ndev); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic inline void cpsw_add_dual_emac_def_ale_entries( 5498c2ecf20Sopenharmony_ci struct cpsw_priv *priv, struct cpsw_slave *slave, 5508c2ecf20Sopenharmony_ci u32 slave_port) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 5538c2ecf20Sopenharmony_ci u32 port_mask = 1 << slave_port | ALE_PORT_HOST; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (cpsw->version == CPSW_VERSION_1) 5568c2ecf20Sopenharmony_ci slave_write(slave, slave->port_vlan, CPSW1_PORT_VLAN); 5578c2ecf20Sopenharmony_ci else 5588c2ecf20Sopenharmony_ci slave_write(slave, slave->port_vlan, CPSW2_PORT_VLAN); 5598c2ecf20Sopenharmony_ci cpsw_ale_add_vlan(cpsw->ale, slave->port_vlan, port_mask, 5608c2ecf20Sopenharmony_ci port_mask, port_mask, 0); 5618c2ecf20Sopenharmony_ci cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast, 5628c2ecf20Sopenharmony_ci ALE_PORT_HOST, ALE_VLAN, slave->port_vlan, 0); 5638c2ecf20Sopenharmony_ci cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr, 5648c2ecf20Sopenharmony_ci HOST_PORT_NUM, ALE_VLAN | 5658c2ecf20Sopenharmony_ci ALE_SECURE, slave->port_vlan); 5668c2ecf20Sopenharmony_ci cpsw_ale_control_set(cpsw->ale, slave_port, 5678c2ecf20Sopenharmony_ci ALE_PORT_DROP_UNKNOWN_VLAN, 1); 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci u32 slave_port; 5738c2ecf20Sopenharmony_ci struct phy_device *phy; 5748c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci cpsw_sl_reset(slave->mac_sl, 100); 5778c2ecf20Sopenharmony_ci cpsw_sl_ctl_reset(slave->mac_sl); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* setup priority mapping */ 5808c2ecf20Sopenharmony_ci cpsw_sl_reg_write(slave->mac_sl, CPSW_SL_RX_PRI_MAP, 5818c2ecf20Sopenharmony_ci RX_PRIORITY_MAPPING); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci switch (cpsw->version) { 5848c2ecf20Sopenharmony_ci case CPSW_VERSION_1: 5858c2ecf20Sopenharmony_ci slave_write(slave, TX_PRIORITY_MAPPING, CPSW1_TX_PRI_MAP); 5868c2ecf20Sopenharmony_ci /* Increase RX FIFO size to 5 for supporting fullduplex 5878c2ecf20Sopenharmony_ci * flow control mode 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_ci slave_write(slave, 5908c2ecf20Sopenharmony_ci (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) | 5918c2ecf20Sopenharmony_ci CPSW_MAX_BLKS_RX, CPSW1_MAX_BLKS); 5928c2ecf20Sopenharmony_ci break; 5938c2ecf20Sopenharmony_ci case CPSW_VERSION_2: 5948c2ecf20Sopenharmony_ci case CPSW_VERSION_3: 5958c2ecf20Sopenharmony_ci case CPSW_VERSION_4: 5968c2ecf20Sopenharmony_ci slave_write(slave, TX_PRIORITY_MAPPING, CPSW2_TX_PRI_MAP); 5978c2ecf20Sopenharmony_ci /* Increase RX FIFO size to 5 for supporting fullduplex 5988c2ecf20Sopenharmony_ci * flow control mode 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_ci slave_write(slave, 6018c2ecf20Sopenharmony_ci (CPSW_MAX_BLKS_TX << CPSW_MAX_BLKS_TX_SHIFT) | 6028c2ecf20Sopenharmony_ci CPSW_MAX_BLKS_RX, CPSW2_MAX_BLKS); 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* setup max packet size, and mac address */ 6078c2ecf20Sopenharmony_ci cpsw_sl_reg_write(slave->mac_sl, CPSW_SL_RX_MAXLEN, 6088c2ecf20Sopenharmony_ci cpsw->rx_packet_max); 6098c2ecf20Sopenharmony_ci cpsw_set_slave_mac(slave, priv); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci slave->mac_control = 0; /* no link yet */ 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci slave_port = cpsw_get_slave_port(slave->slave_num); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac) 6168c2ecf20Sopenharmony_ci cpsw_add_dual_emac_def_ale_entries(priv, slave, slave_port); 6178c2ecf20Sopenharmony_ci else 6188c2ecf20Sopenharmony_ci cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast, 6198c2ecf20Sopenharmony_ci 1 << slave_port, 0, 0, ALE_MCAST_FWD_2); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (slave->data->phy_node) { 6228c2ecf20Sopenharmony_ci phy = of_phy_connect(priv->ndev, slave->data->phy_node, 6238c2ecf20Sopenharmony_ci &cpsw_adjust_link, 0, slave->data->phy_if); 6248c2ecf20Sopenharmony_ci if (!phy) { 6258c2ecf20Sopenharmony_ci dev_err(priv->dev, "phy \"%pOF\" not found on slave %d\n", 6268c2ecf20Sopenharmony_ci slave->data->phy_node, 6278c2ecf20Sopenharmony_ci slave->slave_num); 6288c2ecf20Sopenharmony_ci return; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci } else { 6318c2ecf20Sopenharmony_ci phy = phy_connect(priv->ndev, slave->data->phy_id, 6328c2ecf20Sopenharmony_ci &cpsw_adjust_link, slave->data->phy_if); 6338c2ecf20Sopenharmony_ci if (IS_ERR(phy)) { 6348c2ecf20Sopenharmony_ci dev_err(priv->dev, 6358c2ecf20Sopenharmony_ci "phy \"%s\" not found on slave %d, err %ld\n", 6368c2ecf20Sopenharmony_ci slave->data->phy_id, slave->slave_num, 6378c2ecf20Sopenharmony_ci PTR_ERR(phy)); 6388c2ecf20Sopenharmony_ci return; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci slave->phy = phy; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci phy_attached_info(slave->phy); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci phy_start(slave->phy); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* Configure GMII_SEL register */ 6498c2ecf20Sopenharmony_ci if (!IS_ERR(slave->data->ifphy)) 6508c2ecf20Sopenharmony_ci phy_set_mode_ext(slave->data->ifphy, PHY_MODE_ETHERNET, 6518c2ecf20Sopenharmony_ci slave->data->phy_if); 6528c2ecf20Sopenharmony_ci else 6538c2ecf20Sopenharmony_ci cpsw_phy_sel(cpsw->dev, slave->phy->interface, 6548c2ecf20Sopenharmony_ci slave->slave_num); 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic inline void cpsw_add_default_vlan(struct cpsw_priv *priv) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 6608c2ecf20Sopenharmony_ci const int vlan = cpsw->data.default_vlan; 6618c2ecf20Sopenharmony_ci u32 reg; 6628c2ecf20Sopenharmony_ci int i; 6638c2ecf20Sopenharmony_ci int unreg_mcast_mask; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci reg = (cpsw->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN : 6668c2ecf20Sopenharmony_ci CPSW2_PORT_VLAN; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci writel(vlan, &cpsw->host_port_regs->port_vlan); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci for (i = 0; i < cpsw->data.slaves; i++) 6718c2ecf20Sopenharmony_ci slave_write(cpsw->slaves + i, vlan, reg); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (priv->ndev->flags & IFF_ALLMULTI) 6748c2ecf20Sopenharmony_ci unreg_mcast_mask = ALE_ALL_PORTS; 6758c2ecf20Sopenharmony_ci else 6768c2ecf20Sopenharmony_ci unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci cpsw_ale_add_vlan(cpsw->ale, vlan, ALE_ALL_PORTS, 6798c2ecf20Sopenharmony_ci ALE_ALL_PORTS, ALE_ALL_PORTS, 6808c2ecf20Sopenharmony_ci unreg_mcast_mask); 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic void cpsw_init_host_port(struct cpsw_priv *priv) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci u32 fifo_mode; 6868c2ecf20Sopenharmony_ci u32 control_reg; 6878c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* soft reset the controller and initialize ale */ 6908c2ecf20Sopenharmony_ci soft_reset("cpsw", &cpsw->regs->soft_reset); 6918c2ecf20Sopenharmony_ci cpsw_ale_start(cpsw->ale); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci /* switch to vlan unaware mode */ 6948c2ecf20Sopenharmony_ci cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_VLAN_AWARE, 6958c2ecf20Sopenharmony_ci CPSW_ALE_VLAN_AWARE); 6968c2ecf20Sopenharmony_ci control_reg = readl(&cpsw->regs->control); 6978c2ecf20Sopenharmony_ci control_reg |= CPSW_VLAN_AWARE | CPSW_RX_VLAN_ENCAP; 6988c2ecf20Sopenharmony_ci writel(control_reg, &cpsw->regs->control); 6998c2ecf20Sopenharmony_ci fifo_mode = (cpsw->data.dual_emac) ? CPSW_FIFO_DUAL_MAC_MODE : 7008c2ecf20Sopenharmony_ci CPSW_FIFO_NORMAL_MODE; 7018c2ecf20Sopenharmony_ci writel(fifo_mode, &cpsw->host_port_regs->tx_in_ctl); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* setup host port priority mapping */ 7048c2ecf20Sopenharmony_ci writel_relaxed(CPDMA_TX_PRIORITY_MAP, 7058c2ecf20Sopenharmony_ci &cpsw->host_port_regs->cpdma_tx_pri_map); 7068c2ecf20Sopenharmony_ci writel_relaxed(0, &cpsw->host_port_regs->cpdma_rx_chan_map); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, 7098c2ecf20Sopenharmony_ci ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (!cpsw->data.dual_emac) { 7128c2ecf20Sopenharmony_ci cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr, HOST_PORT_NUM, 7138c2ecf20Sopenharmony_ci 0, 0); 7148c2ecf20Sopenharmony_ci cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast, 7158c2ecf20Sopenharmony_ci ALE_PORT_HOST, 0, 0, ALE_MCAST_FWD_2); 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_common *cpsw) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci u32 slave_port; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci slave_port = cpsw_get_slave_port(slave->slave_num); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (!slave->phy) 7268c2ecf20Sopenharmony_ci return; 7278c2ecf20Sopenharmony_ci phy_stop(slave->phy); 7288c2ecf20Sopenharmony_ci phy_disconnect(slave->phy); 7298c2ecf20Sopenharmony_ci slave->phy = NULL; 7308c2ecf20Sopenharmony_ci cpsw_ale_control_set(cpsw->ale, slave_port, 7318c2ecf20Sopenharmony_ci ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); 7328c2ecf20Sopenharmony_ci cpsw_sl_reset(slave->mac_sl, 100); 7338c2ecf20Sopenharmony_ci cpsw_sl_ctl_reset(slave->mac_sl); 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic int cpsw_restore_vlans(struct net_device *vdev, int vid, void *arg) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci struct cpsw_priv *priv = arg; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (!vdev) 7418c2ecf20Sopenharmony_ci return 0; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci cpsw_ndo_vlan_rx_add_vid(priv->ndev, 0, vid); 7448c2ecf20Sopenharmony_ci return 0; 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci/* restore resources after port reset */ 7488c2ecf20Sopenharmony_cistatic void cpsw_restore(struct cpsw_priv *priv) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci /* restore vlan configurations */ 7518c2ecf20Sopenharmony_ci vlan_for_each(priv->ndev, cpsw_restore_vlans, priv); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* restore MQPRIO offload */ 7548c2ecf20Sopenharmony_ci for_each_slave(priv, cpsw_mqprio_resume, priv); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* restore CBS offload */ 7578c2ecf20Sopenharmony_ci for_each_slave(priv, cpsw_cbs_resume, priv); 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic int cpsw_ndo_open(struct net_device *ndev) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci struct cpsw_priv *priv = netdev_priv(ndev); 7638c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 7648c2ecf20Sopenharmony_ci int ret; 7658c2ecf20Sopenharmony_ci u32 reg; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(cpsw->dev); 7688c2ecf20Sopenharmony_ci if (ret < 0) { 7698c2ecf20Sopenharmony_ci pm_runtime_put_noidle(cpsw->dev); 7708c2ecf20Sopenharmony_ci return ret; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci netif_carrier_off(ndev); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* Notify the stack of the actual queue counts. */ 7768c2ecf20Sopenharmony_ci ret = netif_set_real_num_tx_queues(ndev, cpsw->tx_ch_num); 7778c2ecf20Sopenharmony_ci if (ret) { 7788c2ecf20Sopenharmony_ci dev_err(priv->dev, "cannot set real number of tx queues\n"); 7798c2ecf20Sopenharmony_ci goto err_cleanup; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci ret = netif_set_real_num_rx_queues(ndev, cpsw->rx_ch_num); 7838c2ecf20Sopenharmony_ci if (ret) { 7848c2ecf20Sopenharmony_ci dev_err(priv->dev, "cannot set real number of rx queues\n"); 7858c2ecf20Sopenharmony_ci goto err_cleanup; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci reg = cpsw->version; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci dev_info(priv->dev, "initializing cpsw version %d.%d (%d)\n", 7918c2ecf20Sopenharmony_ci CPSW_MAJOR_VERSION(reg), CPSW_MINOR_VERSION(reg), 7928c2ecf20Sopenharmony_ci CPSW_RTL_VERSION(reg)); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* Initialize host and slave ports */ 7958c2ecf20Sopenharmony_ci if (!cpsw->usage_count) 7968c2ecf20Sopenharmony_ci cpsw_init_host_port(priv); 7978c2ecf20Sopenharmony_ci for_each_slave(priv, cpsw_slave_open, priv); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* Add default VLAN */ 8008c2ecf20Sopenharmony_ci if (!cpsw->data.dual_emac) 8018c2ecf20Sopenharmony_ci cpsw_add_default_vlan(priv); 8028c2ecf20Sopenharmony_ci else 8038c2ecf20Sopenharmony_ci cpsw_ale_add_vlan(cpsw->ale, cpsw->data.default_vlan, 8048c2ecf20Sopenharmony_ci ALE_ALL_PORTS, ALE_ALL_PORTS, 0, 0); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* initialize shared resources for every ndev */ 8078c2ecf20Sopenharmony_ci if (!cpsw->usage_count) { 8088c2ecf20Sopenharmony_ci /* disable priority elevation */ 8098c2ecf20Sopenharmony_ci writel_relaxed(0, &cpsw->regs->ptype); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* enable statistics collection only on all ports */ 8128c2ecf20Sopenharmony_ci writel_relaxed(0x7, &cpsw->regs->stat_port_en); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* Enable internal fifo flow control */ 8158c2ecf20Sopenharmony_ci writel(0x7, &cpsw->regs->flow_control); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci napi_enable(&cpsw->napi_rx); 8188c2ecf20Sopenharmony_ci napi_enable(&cpsw->napi_tx); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (cpsw->tx_irq_disabled) { 8218c2ecf20Sopenharmony_ci cpsw->tx_irq_disabled = false; 8228c2ecf20Sopenharmony_ci enable_irq(cpsw->irqs_table[1]); 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (cpsw->rx_irq_disabled) { 8268c2ecf20Sopenharmony_ci cpsw->rx_irq_disabled = false; 8278c2ecf20Sopenharmony_ci enable_irq(cpsw->irqs_table[0]); 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci /* create rxqs for both infs in dual mac as they use same pool 8318c2ecf20Sopenharmony_ci * and must be destroyed together when no users. 8328c2ecf20Sopenharmony_ci */ 8338c2ecf20Sopenharmony_ci ret = cpsw_create_xdp_rxqs(cpsw); 8348c2ecf20Sopenharmony_ci if (ret < 0) 8358c2ecf20Sopenharmony_ci goto err_cleanup; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci ret = cpsw_fill_rx_channels(priv); 8388c2ecf20Sopenharmony_ci if (ret < 0) 8398c2ecf20Sopenharmony_ci goto err_cleanup; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (cpsw->cpts) { 8428c2ecf20Sopenharmony_ci if (cpts_register(cpsw->cpts)) 8438c2ecf20Sopenharmony_ci dev_err(priv->dev, "error registering cpts device\n"); 8448c2ecf20Sopenharmony_ci else 8458c2ecf20Sopenharmony_ci writel(0x10, &cpsw->wr_regs->misc_en); 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci cpsw_restore(priv); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* Enable Interrupt pacing if configured */ 8528c2ecf20Sopenharmony_ci if (cpsw->coal_intvl != 0) { 8538c2ecf20Sopenharmony_ci struct ethtool_coalesce coal; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci coal.rx_coalesce_usecs = cpsw->coal_intvl; 8568c2ecf20Sopenharmony_ci cpsw_set_coalesce(ndev, &coal); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci cpdma_ctlr_start(cpsw->dma); 8608c2ecf20Sopenharmony_ci cpsw_intr_enable(cpsw); 8618c2ecf20Sopenharmony_ci cpsw->usage_count++; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cierr_cleanup: 8668c2ecf20Sopenharmony_ci if (!cpsw->usage_count) { 8678c2ecf20Sopenharmony_ci napi_disable(&cpsw->napi_rx); 8688c2ecf20Sopenharmony_ci napi_disable(&cpsw->napi_tx); 8698c2ecf20Sopenharmony_ci cpdma_ctlr_stop(cpsw->dma); 8708c2ecf20Sopenharmony_ci cpsw_destroy_xdp_rxqs(cpsw); 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci for_each_slave(priv, cpsw_slave_stop, cpsw); 8748c2ecf20Sopenharmony_ci pm_runtime_put_sync(cpsw->dev); 8758c2ecf20Sopenharmony_ci netif_carrier_off(priv->ndev); 8768c2ecf20Sopenharmony_ci return ret; 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic int cpsw_ndo_stop(struct net_device *ndev) 8808c2ecf20Sopenharmony_ci{ 8818c2ecf20Sopenharmony_ci struct cpsw_priv *priv = netdev_priv(ndev); 8828c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci cpsw_info(priv, ifdown, "shutting down cpsw device\n"); 8858c2ecf20Sopenharmony_ci __hw_addr_ref_unsync_dev(&ndev->mc, ndev, cpsw_purge_all_mc); 8868c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(priv->ndev); 8878c2ecf20Sopenharmony_ci netif_carrier_off(priv->ndev); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (cpsw->usage_count <= 1) { 8908c2ecf20Sopenharmony_ci napi_disable(&cpsw->napi_rx); 8918c2ecf20Sopenharmony_ci napi_disable(&cpsw->napi_tx); 8928c2ecf20Sopenharmony_ci cpts_unregister(cpsw->cpts); 8938c2ecf20Sopenharmony_ci cpsw_intr_disable(cpsw); 8948c2ecf20Sopenharmony_ci cpdma_ctlr_stop(cpsw->dma); 8958c2ecf20Sopenharmony_ci cpsw_ale_stop(cpsw->ale); 8968c2ecf20Sopenharmony_ci cpsw_destroy_xdp_rxqs(cpsw); 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci for_each_slave(priv, cpsw_slave_stop, cpsw); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (cpsw_need_resplit(cpsw)) 9018c2ecf20Sopenharmony_ci cpsw_split_res(cpsw); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci cpsw->usage_count--; 9048c2ecf20Sopenharmony_ci pm_runtime_put_sync(cpsw->dev); 9058c2ecf20Sopenharmony_ci return 0; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, 9098c2ecf20Sopenharmony_ci struct net_device *ndev) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci struct cpsw_priv *priv = netdev_priv(ndev); 9128c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 9138c2ecf20Sopenharmony_ci struct cpts *cpts = cpsw->cpts; 9148c2ecf20Sopenharmony_ci struct netdev_queue *txq; 9158c2ecf20Sopenharmony_ci struct cpdma_chan *txch; 9168c2ecf20Sopenharmony_ci int ret, q_idx; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (skb_padto(skb, CPSW_MIN_PACKET_SIZE)) { 9198c2ecf20Sopenharmony_ci cpsw_err(priv, tx_err, "packet pad failed\n"); 9208c2ecf20Sopenharmony_ci ndev->stats.tx_dropped++; 9218c2ecf20Sopenharmony_ci return NET_XMIT_DROP; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 9258c2ecf20Sopenharmony_ci priv->tx_ts_enabled && cpts_can_timestamp(cpts, skb)) 9268c2ecf20Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci q_idx = skb_get_queue_mapping(skb); 9298c2ecf20Sopenharmony_ci if (q_idx >= cpsw->tx_ch_num) 9308c2ecf20Sopenharmony_ci q_idx = q_idx % cpsw->tx_ch_num; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci txch = cpsw->txv[q_idx].ch; 9338c2ecf20Sopenharmony_ci txq = netdev_get_tx_queue(ndev, q_idx); 9348c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 9358c2ecf20Sopenharmony_ci ret = cpdma_chan_submit(txch, skb, skb->data, skb->len, 9368c2ecf20Sopenharmony_ci priv->emac_port + cpsw->data.dual_emac); 9378c2ecf20Sopenharmony_ci if (unlikely(ret != 0)) { 9388c2ecf20Sopenharmony_ci cpsw_err(priv, tx_err, "desc submit failed\n"); 9398c2ecf20Sopenharmony_ci goto fail; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci /* If there is no more tx desc left free then we need to 9438c2ecf20Sopenharmony_ci * tell the kernel to stop sending us tx frames. 9448c2ecf20Sopenharmony_ci */ 9458c2ecf20Sopenharmony_ci if (unlikely(!cpdma_check_free_tx_desc(txch))) { 9468c2ecf20Sopenharmony_ci netif_tx_stop_queue(txq); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci /* Barrier, so that stop_queue visible to other cpus */ 9498c2ecf20Sopenharmony_ci smp_mb__after_atomic(); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (cpdma_check_free_tx_desc(txch)) 9528c2ecf20Sopenharmony_ci netif_tx_wake_queue(txq); 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 9568c2ecf20Sopenharmony_cifail: 9578c2ecf20Sopenharmony_ci ndev->stats.tx_dropped++; 9588c2ecf20Sopenharmony_ci netif_tx_stop_queue(txq); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* Barrier, so that stop_queue visible to other cpus */ 9618c2ecf20Sopenharmony_ci smp_mb__after_atomic(); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (cpdma_check_free_tx_desc(txch)) 9648c2ecf20Sopenharmony_ci netif_tx_wake_queue(txq); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic int cpsw_ndo_set_mac_address(struct net_device *ndev, void *p) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci struct cpsw_priv *priv = netdev_priv(ndev); 9728c2ecf20Sopenharmony_ci struct sockaddr *addr = (struct sockaddr *)p; 9738c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 9748c2ecf20Sopenharmony_ci int flags = 0; 9758c2ecf20Sopenharmony_ci u16 vid = 0; 9768c2ecf20Sopenharmony_ci int ret; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 9798c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(cpsw->dev); 9828c2ecf20Sopenharmony_ci if (ret < 0) { 9838c2ecf20Sopenharmony_ci pm_runtime_put_noidle(cpsw->dev); 9848c2ecf20Sopenharmony_ci return ret; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac) { 9888c2ecf20Sopenharmony_ci vid = cpsw->slaves[priv->emac_port].port_vlan; 9898c2ecf20Sopenharmony_ci flags = ALE_VLAN; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr, HOST_PORT_NUM, 9938c2ecf20Sopenharmony_ci flags, vid); 9948c2ecf20Sopenharmony_ci cpsw_ale_add_ucast(cpsw->ale, addr->sa_data, HOST_PORT_NUM, 9958c2ecf20Sopenharmony_ci flags, vid); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN); 9988c2ecf20Sopenharmony_ci memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN); 9998c2ecf20Sopenharmony_ci for_each_slave(priv, cpsw_set_slave_mac, priv); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci pm_runtime_put(cpsw->dev); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci return 0; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv, 10078c2ecf20Sopenharmony_ci unsigned short vid) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci int ret; 10108c2ecf20Sopenharmony_ci int unreg_mcast_mask = 0; 10118c2ecf20Sopenharmony_ci int mcast_mask; 10128c2ecf20Sopenharmony_ci u32 port_mask; 10138c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac) { 10168c2ecf20Sopenharmony_ci port_mask = (1 << (priv->emac_port + 1)) | ALE_PORT_HOST; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci mcast_mask = ALE_PORT_HOST; 10198c2ecf20Sopenharmony_ci if (priv->ndev->flags & IFF_ALLMULTI) 10208c2ecf20Sopenharmony_ci unreg_mcast_mask = mcast_mask; 10218c2ecf20Sopenharmony_ci } else { 10228c2ecf20Sopenharmony_ci port_mask = ALE_ALL_PORTS; 10238c2ecf20Sopenharmony_ci mcast_mask = port_mask; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (priv->ndev->flags & IFF_ALLMULTI) 10268c2ecf20Sopenharmony_ci unreg_mcast_mask = ALE_ALL_PORTS; 10278c2ecf20Sopenharmony_ci else 10288c2ecf20Sopenharmony_ci unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci ret = cpsw_ale_add_vlan(cpsw->ale, vid, port_mask, 0, port_mask, 10328c2ecf20Sopenharmony_ci unreg_mcast_mask); 10338c2ecf20Sopenharmony_ci if (ret != 0) 10348c2ecf20Sopenharmony_ci return ret; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci ret = cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr, 10378c2ecf20Sopenharmony_ci HOST_PORT_NUM, ALE_VLAN, vid); 10388c2ecf20Sopenharmony_ci if (ret != 0) 10398c2ecf20Sopenharmony_ci goto clean_vid; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci ret = cpsw_ale_add_mcast(cpsw->ale, priv->ndev->broadcast, 10428c2ecf20Sopenharmony_ci mcast_mask, ALE_VLAN, vid, 0); 10438c2ecf20Sopenharmony_ci if (ret != 0) 10448c2ecf20Sopenharmony_ci goto clean_vlan_ucast; 10458c2ecf20Sopenharmony_ci return 0; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ciclean_vlan_ucast: 10488c2ecf20Sopenharmony_ci cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr, 10498c2ecf20Sopenharmony_ci HOST_PORT_NUM, ALE_VLAN, vid); 10508c2ecf20Sopenharmony_ciclean_vid: 10518c2ecf20Sopenharmony_ci cpsw_ale_del_vlan(cpsw->ale, vid, 0); 10528c2ecf20Sopenharmony_ci return ret; 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_cistatic int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev, 10568c2ecf20Sopenharmony_ci __be16 proto, u16 vid) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci struct cpsw_priv *priv = netdev_priv(ndev); 10598c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 10608c2ecf20Sopenharmony_ci int ret; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci if (vid == cpsw->data.default_vlan) 10638c2ecf20Sopenharmony_ci return 0; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(cpsw->dev); 10668c2ecf20Sopenharmony_ci if (ret < 0) { 10678c2ecf20Sopenharmony_ci pm_runtime_put_noidle(cpsw->dev); 10688c2ecf20Sopenharmony_ci return ret; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac) { 10728c2ecf20Sopenharmony_ci /* In dual EMAC, reserved VLAN id should not be used for 10738c2ecf20Sopenharmony_ci * creating VLAN interfaces as this can break the dual 10748c2ecf20Sopenharmony_ci * EMAC port separation 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_ci int i; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci for (i = 0; i < cpsw->data.slaves; i++) { 10798c2ecf20Sopenharmony_ci if (vid == cpsw->slaves[i].port_vlan) { 10808c2ecf20Sopenharmony_ci ret = -EINVAL; 10818c2ecf20Sopenharmony_ci goto err; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci dev_info(priv->dev, "Adding vlanid %d to vlan filter\n", vid); 10878c2ecf20Sopenharmony_ci ret = cpsw_add_vlan_ale_entry(priv, vid); 10888c2ecf20Sopenharmony_cierr: 10898c2ecf20Sopenharmony_ci pm_runtime_put(cpsw->dev); 10908c2ecf20Sopenharmony_ci return ret; 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_cistatic int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev, 10948c2ecf20Sopenharmony_ci __be16 proto, u16 vid) 10958c2ecf20Sopenharmony_ci{ 10968c2ecf20Sopenharmony_ci struct cpsw_priv *priv = netdev_priv(ndev); 10978c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 10988c2ecf20Sopenharmony_ci int ret; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci if (vid == cpsw->data.default_vlan) 11018c2ecf20Sopenharmony_ci return 0; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(cpsw->dev); 11048c2ecf20Sopenharmony_ci if (ret < 0) { 11058c2ecf20Sopenharmony_ci pm_runtime_put_noidle(cpsw->dev); 11068c2ecf20Sopenharmony_ci return ret; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac) { 11108c2ecf20Sopenharmony_ci int i; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci for (i = 0; i < cpsw->data.slaves; i++) { 11138c2ecf20Sopenharmony_ci if (vid == cpsw->slaves[i].port_vlan) 11148c2ecf20Sopenharmony_ci goto err; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci dev_info(priv->dev, "removing vlanid %d from vlan filter\n", vid); 11198c2ecf20Sopenharmony_ci ret = cpsw_ale_del_vlan(cpsw->ale, vid, 0); 11208c2ecf20Sopenharmony_ci ret |= cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr, 11218c2ecf20Sopenharmony_ci HOST_PORT_NUM, ALE_VLAN, vid); 11228c2ecf20Sopenharmony_ci ret |= cpsw_ale_del_mcast(cpsw->ale, priv->ndev->broadcast, 11238c2ecf20Sopenharmony_ci 0, ALE_VLAN, vid); 11248c2ecf20Sopenharmony_ci ret |= cpsw_ale_flush_multicast(cpsw->ale, ALE_PORT_HOST, vid); 11258c2ecf20Sopenharmony_cierr: 11268c2ecf20Sopenharmony_ci pm_runtime_put(cpsw->dev); 11278c2ecf20Sopenharmony_ci return ret; 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic int cpsw_ndo_xdp_xmit(struct net_device *ndev, int n, 11318c2ecf20Sopenharmony_ci struct xdp_frame **frames, u32 flags) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci struct cpsw_priv *priv = netdev_priv(ndev); 11348c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 11358c2ecf20Sopenharmony_ci struct xdp_frame *xdpf; 11368c2ecf20Sopenharmony_ci int i, drops = 0, port; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) 11398c2ecf20Sopenharmony_ci return -EINVAL; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 11428c2ecf20Sopenharmony_ci xdpf = frames[i]; 11438c2ecf20Sopenharmony_ci if (xdpf->len < CPSW_MIN_PACKET_SIZE) { 11448c2ecf20Sopenharmony_ci xdp_return_frame_rx_napi(xdpf); 11458c2ecf20Sopenharmony_ci drops++; 11468c2ecf20Sopenharmony_ci continue; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci port = priv->emac_port + cpsw->data.dual_emac; 11508c2ecf20Sopenharmony_ci if (cpsw_xdp_tx_frame(priv, xdpf, NULL, port)) 11518c2ecf20Sopenharmony_ci drops++; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci return n - drops; 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 11588c2ecf20Sopenharmony_cistatic void cpsw_ndo_poll_controller(struct net_device *ndev) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = ndev_to_cpsw(ndev); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci cpsw_intr_disable(cpsw); 11638c2ecf20Sopenharmony_ci cpsw_rx_interrupt(cpsw->irqs_table[0], cpsw); 11648c2ecf20Sopenharmony_ci cpsw_tx_interrupt(cpsw->irqs_table[1], cpsw); 11658c2ecf20Sopenharmony_ci cpsw_intr_enable(cpsw); 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ci#endif 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_cistatic const struct net_device_ops cpsw_netdev_ops = { 11708c2ecf20Sopenharmony_ci .ndo_open = cpsw_ndo_open, 11718c2ecf20Sopenharmony_ci .ndo_stop = cpsw_ndo_stop, 11728c2ecf20Sopenharmony_ci .ndo_start_xmit = cpsw_ndo_start_xmit, 11738c2ecf20Sopenharmony_ci .ndo_set_mac_address = cpsw_ndo_set_mac_address, 11748c2ecf20Sopenharmony_ci .ndo_do_ioctl = cpsw_ndo_ioctl, 11758c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 11768c2ecf20Sopenharmony_ci .ndo_tx_timeout = cpsw_ndo_tx_timeout, 11778c2ecf20Sopenharmony_ci .ndo_set_rx_mode = cpsw_ndo_set_rx_mode, 11788c2ecf20Sopenharmony_ci .ndo_set_tx_maxrate = cpsw_ndo_set_tx_maxrate, 11798c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 11808c2ecf20Sopenharmony_ci .ndo_poll_controller = cpsw_ndo_poll_controller, 11818c2ecf20Sopenharmony_ci#endif 11828c2ecf20Sopenharmony_ci .ndo_vlan_rx_add_vid = cpsw_ndo_vlan_rx_add_vid, 11838c2ecf20Sopenharmony_ci .ndo_vlan_rx_kill_vid = cpsw_ndo_vlan_rx_kill_vid, 11848c2ecf20Sopenharmony_ci .ndo_setup_tc = cpsw_ndo_setup_tc, 11858c2ecf20Sopenharmony_ci .ndo_bpf = cpsw_ndo_bpf, 11868c2ecf20Sopenharmony_ci .ndo_xdp_xmit = cpsw_ndo_xdp_xmit, 11878c2ecf20Sopenharmony_ci}; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic void cpsw_get_drvinfo(struct net_device *ndev, 11908c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = ndev_to_cpsw(ndev); 11938c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(cpsw->dev); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci strlcpy(info->driver, "cpsw", sizeof(info->driver)); 11968c2ecf20Sopenharmony_ci strlcpy(info->version, "1.0", sizeof(info->version)); 11978c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pdev->name, sizeof(info->bus_info)); 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_cistatic int cpsw_set_pauseparam(struct net_device *ndev, 12018c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci struct cpsw_priv *priv = netdev_priv(ndev); 12048c2ecf20Sopenharmony_ci bool link; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci priv->rx_pause = pause->rx_pause ? true : false; 12078c2ecf20Sopenharmony_ci priv->tx_pause = pause->tx_pause ? true : false; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci for_each_slave(priv, _cpsw_adjust_link, priv, &link); 12108c2ecf20Sopenharmony_ci return 0; 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistatic int cpsw_set_channels(struct net_device *ndev, 12148c2ecf20Sopenharmony_ci struct ethtool_channels *chs) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci return cpsw_set_channels_common(ndev, chs, cpsw_rx_handler); 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistatic const struct ethtool_ops cpsw_ethtool_ops = { 12208c2ecf20Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS, 12218c2ecf20Sopenharmony_ci .get_drvinfo = cpsw_get_drvinfo, 12228c2ecf20Sopenharmony_ci .get_msglevel = cpsw_get_msglevel, 12238c2ecf20Sopenharmony_ci .set_msglevel = cpsw_set_msglevel, 12248c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 12258c2ecf20Sopenharmony_ci .get_ts_info = cpsw_get_ts_info, 12268c2ecf20Sopenharmony_ci .get_coalesce = cpsw_get_coalesce, 12278c2ecf20Sopenharmony_ci .set_coalesce = cpsw_set_coalesce, 12288c2ecf20Sopenharmony_ci .get_sset_count = cpsw_get_sset_count, 12298c2ecf20Sopenharmony_ci .get_strings = cpsw_get_strings, 12308c2ecf20Sopenharmony_ci .get_ethtool_stats = cpsw_get_ethtool_stats, 12318c2ecf20Sopenharmony_ci .get_pauseparam = cpsw_get_pauseparam, 12328c2ecf20Sopenharmony_ci .set_pauseparam = cpsw_set_pauseparam, 12338c2ecf20Sopenharmony_ci .get_wol = cpsw_get_wol, 12348c2ecf20Sopenharmony_ci .set_wol = cpsw_set_wol, 12358c2ecf20Sopenharmony_ci .get_regs_len = cpsw_get_regs_len, 12368c2ecf20Sopenharmony_ci .get_regs = cpsw_get_regs, 12378c2ecf20Sopenharmony_ci .begin = cpsw_ethtool_op_begin, 12388c2ecf20Sopenharmony_ci .complete = cpsw_ethtool_op_complete, 12398c2ecf20Sopenharmony_ci .get_channels = cpsw_get_channels, 12408c2ecf20Sopenharmony_ci .set_channels = cpsw_set_channels, 12418c2ecf20Sopenharmony_ci .get_link_ksettings = cpsw_get_link_ksettings, 12428c2ecf20Sopenharmony_ci .set_link_ksettings = cpsw_set_link_ksettings, 12438c2ecf20Sopenharmony_ci .get_eee = cpsw_get_eee, 12448c2ecf20Sopenharmony_ci .set_eee = cpsw_set_eee, 12458c2ecf20Sopenharmony_ci .nway_reset = cpsw_nway_reset, 12468c2ecf20Sopenharmony_ci .get_ringparam = cpsw_get_ringparam, 12478c2ecf20Sopenharmony_ci .set_ringparam = cpsw_set_ringparam, 12488c2ecf20Sopenharmony_ci}; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic int cpsw_probe_dt(struct cpsw_platform_data *data, 12518c2ecf20Sopenharmony_ci struct platform_device *pdev) 12528c2ecf20Sopenharmony_ci{ 12538c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 12548c2ecf20Sopenharmony_ci struct device_node *slave_node; 12558c2ecf20Sopenharmony_ci int i = 0, ret; 12568c2ecf20Sopenharmony_ci u32 prop; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if (!node) 12598c2ecf20Sopenharmony_ci return -EINVAL; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "slaves", &prop)) { 12628c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing slaves property in the DT.\n"); 12638c2ecf20Sopenharmony_ci return -EINVAL; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci data->slaves = prop; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "active_slave", &prop)) { 12688c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing active_slave property in the DT.\n"); 12698c2ecf20Sopenharmony_ci return -EINVAL; 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci data->active_slave = prop; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci data->slave_data = devm_kcalloc(&pdev->dev, 12748c2ecf20Sopenharmony_ci data->slaves, 12758c2ecf20Sopenharmony_ci sizeof(struct cpsw_slave_data), 12768c2ecf20Sopenharmony_ci GFP_KERNEL); 12778c2ecf20Sopenharmony_ci if (!data->slave_data) 12788c2ecf20Sopenharmony_ci return -ENOMEM; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "cpdma_channels", &prop)) { 12818c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing cpdma_channels property in the DT.\n"); 12828c2ecf20Sopenharmony_ci return -EINVAL; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci data->channels = prop; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "bd_ram_size", &prop)) { 12878c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing bd_ram_size property in the DT.\n"); 12888c2ecf20Sopenharmony_ci return -EINVAL; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci data->bd_ram_size = prop; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "mac_control", &prop)) { 12938c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing mac_control property in the DT.\n"); 12948c2ecf20Sopenharmony_ci return -EINVAL; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci data->mac_control = prop; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (of_property_read_bool(node, "dual_emac")) 12998c2ecf20Sopenharmony_ci data->dual_emac = true; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci /* 13028c2ecf20Sopenharmony_ci * Populate all the child nodes here... 13038c2ecf20Sopenharmony_ci */ 13048c2ecf20Sopenharmony_ci ret = of_platform_populate(node, NULL, NULL, &pdev->dev); 13058c2ecf20Sopenharmony_ci /* We do not want to force this, as in some cases may not have child */ 13068c2ecf20Sopenharmony_ci if (ret) 13078c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Doesn't have any child node\n"); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci for_each_available_child_of_node(node, slave_node) { 13108c2ecf20Sopenharmony_ci struct cpsw_slave_data *slave_data = data->slave_data + i; 13118c2ecf20Sopenharmony_ci const void *mac_addr = NULL; 13128c2ecf20Sopenharmony_ci int lenp; 13138c2ecf20Sopenharmony_ci const __be32 *parp; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci /* This is no slave child node, continue */ 13168c2ecf20Sopenharmony_ci if (!of_node_name_eq(slave_node, "slave")) 13178c2ecf20Sopenharmony_ci continue; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci slave_data->ifphy = devm_of_phy_get(&pdev->dev, slave_node, 13208c2ecf20Sopenharmony_ci NULL); 13218c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_TI_CPSW_PHY_SEL) && 13228c2ecf20Sopenharmony_ci IS_ERR(slave_data->ifphy)) { 13238c2ecf20Sopenharmony_ci ret = PTR_ERR(slave_data->ifphy); 13248c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 13258c2ecf20Sopenharmony_ci "%d: Error retrieving port phy: %d\n", i, ret); 13268c2ecf20Sopenharmony_ci goto err_node_put; 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci slave_data->slave_node = slave_node; 13308c2ecf20Sopenharmony_ci slave_data->phy_node = of_parse_phandle(slave_node, 13318c2ecf20Sopenharmony_ci "phy-handle", 0); 13328c2ecf20Sopenharmony_ci parp = of_get_property(slave_node, "phy_id", &lenp); 13338c2ecf20Sopenharmony_ci if (slave_data->phy_node) { 13348c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, 13358c2ecf20Sopenharmony_ci "slave[%d] using phy-handle=\"%pOF\"\n", 13368c2ecf20Sopenharmony_ci i, slave_data->phy_node); 13378c2ecf20Sopenharmony_ci } else if (of_phy_is_fixed_link(slave_node)) { 13388c2ecf20Sopenharmony_ci /* In the case of a fixed PHY, the DT node associated 13398c2ecf20Sopenharmony_ci * to the PHY is the Ethernet MAC DT node. 13408c2ecf20Sopenharmony_ci */ 13418c2ecf20Sopenharmony_ci ret = of_phy_register_fixed_link(slave_node); 13428c2ecf20Sopenharmony_ci if (ret) { 13438c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 13448c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register fixed-link phy: %d\n", ret); 13458c2ecf20Sopenharmony_ci goto err_node_put; 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci slave_data->phy_node = of_node_get(slave_node); 13488c2ecf20Sopenharmony_ci } else if (parp) { 13498c2ecf20Sopenharmony_ci u32 phyid; 13508c2ecf20Sopenharmony_ci struct device_node *mdio_node; 13518c2ecf20Sopenharmony_ci struct platform_device *mdio; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci if (lenp != (sizeof(__be32) * 2)) { 13548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid slave[%d] phy_id property\n", i); 13558c2ecf20Sopenharmony_ci goto no_phy_slave; 13568c2ecf20Sopenharmony_ci } 13578c2ecf20Sopenharmony_ci mdio_node = of_find_node_by_phandle(be32_to_cpup(parp)); 13588c2ecf20Sopenharmony_ci phyid = be32_to_cpup(parp+1); 13598c2ecf20Sopenharmony_ci mdio = of_find_device_by_node(mdio_node); 13608c2ecf20Sopenharmony_ci of_node_put(mdio_node); 13618c2ecf20Sopenharmony_ci if (!mdio) { 13628c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing mdio platform device\n"); 13638c2ecf20Sopenharmony_ci ret = -EINVAL; 13648c2ecf20Sopenharmony_ci goto err_node_put; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci snprintf(slave_data->phy_id, sizeof(slave_data->phy_id), 13678c2ecf20Sopenharmony_ci PHY_ID_FMT, mdio->name, phyid); 13688c2ecf20Sopenharmony_ci put_device(&mdio->dev); 13698c2ecf20Sopenharmony_ci } else { 13708c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 13718c2ecf20Sopenharmony_ci "No slave[%d] phy_id, phy-handle, or fixed-link property\n", 13728c2ecf20Sopenharmony_ci i); 13738c2ecf20Sopenharmony_ci goto no_phy_slave; 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci ret = of_get_phy_mode(slave_node, &slave_data->phy_if); 13768c2ecf20Sopenharmony_ci if (ret) { 13778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing or malformed slave[%d] phy-mode property\n", 13788c2ecf20Sopenharmony_ci i); 13798c2ecf20Sopenharmony_ci goto err_node_put; 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_cino_phy_slave: 13838c2ecf20Sopenharmony_ci mac_addr = of_get_mac_address(slave_node); 13848c2ecf20Sopenharmony_ci if (!IS_ERR(mac_addr)) { 13858c2ecf20Sopenharmony_ci ether_addr_copy(slave_data->mac_addr, mac_addr); 13868c2ecf20Sopenharmony_ci } else { 13878c2ecf20Sopenharmony_ci ret = ti_cm_get_macid(&pdev->dev, i, 13888c2ecf20Sopenharmony_ci slave_data->mac_addr); 13898c2ecf20Sopenharmony_ci if (ret) 13908c2ecf20Sopenharmony_ci goto err_node_put; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci if (data->dual_emac) { 13938c2ecf20Sopenharmony_ci if (of_property_read_u32(slave_node, "dual_emac_res_vlan", 13948c2ecf20Sopenharmony_ci &prop)) { 13958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing dual_emac_res_vlan in DT.\n"); 13968c2ecf20Sopenharmony_ci slave_data->dual_emac_res_vlan = i+1; 13978c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Using %d as Reserved VLAN for %d slave\n", 13988c2ecf20Sopenharmony_ci slave_data->dual_emac_res_vlan, i); 13998c2ecf20Sopenharmony_ci } else { 14008c2ecf20Sopenharmony_ci slave_data->dual_emac_res_vlan = prop; 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci } 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci i++; 14058c2ecf20Sopenharmony_ci if (i == data->slaves) { 14068c2ecf20Sopenharmony_ci ret = 0; 14078c2ecf20Sopenharmony_ci goto err_node_put; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci return 0; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_cierr_node_put: 14148c2ecf20Sopenharmony_ci of_node_put(slave_node); 14158c2ecf20Sopenharmony_ci return ret; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic void cpsw_remove_dt(struct platform_device *pdev) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = platform_get_drvdata(pdev); 14218c2ecf20Sopenharmony_ci struct cpsw_platform_data *data = &cpsw->data; 14228c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 14238c2ecf20Sopenharmony_ci struct device_node *slave_node; 14248c2ecf20Sopenharmony_ci int i = 0; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci for_each_available_child_of_node(node, slave_node) { 14278c2ecf20Sopenharmony_ci struct cpsw_slave_data *slave_data = &data->slave_data[i]; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci if (!of_node_name_eq(slave_node, "slave")) 14308c2ecf20Sopenharmony_ci continue; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci if (of_phy_is_fixed_link(slave_node)) 14338c2ecf20Sopenharmony_ci of_phy_deregister_fixed_link(slave_node); 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci of_node_put(slave_data->phy_node); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci i++; 14388c2ecf20Sopenharmony_ci if (i == data->slaves) { 14398c2ecf20Sopenharmony_ci of_node_put(slave_node); 14408c2ecf20Sopenharmony_ci break; 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci of_platform_depopulate(&pdev->dev); 14458c2ecf20Sopenharmony_ci} 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_cistatic int cpsw_probe_dual_emac(struct cpsw_priv *priv) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = priv->cpsw; 14508c2ecf20Sopenharmony_ci struct cpsw_platform_data *data = &cpsw->data; 14518c2ecf20Sopenharmony_ci struct net_device *ndev; 14528c2ecf20Sopenharmony_ci struct cpsw_priv *priv_sl2; 14538c2ecf20Sopenharmony_ci int ret = 0; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci ndev = devm_alloc_etherdev_mqs(cpsw->dev, sizeof(struct cpsw_priv), 14568c2ecf20Sopenharmony_ci CPSW_MAX_QUEUES, CPSW_MAX_QUEUES); 14578c2ecf20Sopenharmony_ci if (!ndev) { 14588c2ecf20Sopenharmony_ci dev_err(cpsw->dev, "cpsw: error allocating net_device\n"); 14598c2ecf20Sopenharmony_ci return -ENOMEM; 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci priv_sl2 = netdev_priv(ndev); 14638c2ecf20Sopenharmony_ci priv_sl2->cpsw = cpsw; 14648c2ecf20Sopenharmony_ci priv_sl2->ndev = ndev; 14658c2ecf20Sopenharmony_ci priv_sl2->dev = &ndev->dev; 14668c2ecf20Sopenharmony_ci priv_sl2->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci if (is_valid_ether_addr(data->slave_data[1].mac_addr)) { 14698c2ecf20Sopenharmony_ci memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr, 14708c2ecf20Sopenharmony_ci ETH_ALEN); 14718c2ecf20Sopenharmony_ci dev_info(cpsw->dev, "cpsw: Detected MACID = %pM\n", 14728c2ecf20Sopenharmony_ci priv_sl2->mac_addr); 14738c2ecf20Sopenharmony_ci } else { 14748c2ecf20Sopenharmony_ci eth_random_addr(priv_sl2->mac_addr); 14758c2ecf20Sopenharmony_ci dev_info(cpsw->dev, "cpsw: Random MACID = %pM\n", 14768c2ecf20Sopenharmony_ci priv_sl2->mac_addr); 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci memcpy(ndev->dev_addr, priv_sl2->mac_addr, ETH_ALEN); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci priv_sl2->emac_port = 1; 14818c2ecf20Sopenharmony_ci cpsw->slaves[1].ndev = ndev; 14828c2ecf20Sopenharmony_ci ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci ndev->netdev_ops = &cpsw_netdev_ops; 14858c2ecf20Sopenharmony_ci ndev->ethtool_ops = &cpsw_ethtool_ops; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci /* register the network device */ 14888c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, cpsw->dev); 14898c2ecf20Sopenharmony_ci ndev->dev.of_node = cpsw->slaves[1].data->slave_node; 14908c2ecf20Sopenharmony_ci ret = register_netdev(ndev); 14918c2ecf20Sopenharmony_ci if (ret) 14928c2ecf20Sopenharmony_ci dev_err(cpsw->dev, "cpsw: error registering net device\n"); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci return ret; 14958c2ecf20Sopenharmony_ci} 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_cistatic const struct of_device_id cpsw_of_mtable[] = { 14988c2ecf20Sopenharmony_ci { .compatible = "ti,cpsw"}, 14998c2ecf20Sopenharmony_ci { .compatible = "ti,am335x-cpsw"}, 15008c2ecf20Sopenharmony_ci { .compatible = "ti,am4372-cpsw"}, 15018c2ecf20Sopenharmony_ci { .compatible = "ti,dra7-cpsw"}, 15028c2ecf20Sopenharmony_ci { /* sentinel */ }, 15038c2ecf20Sopenharmony_ci}; 15048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, cpsw_of_mtable); 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_cistatic const struct soc_device_attribute cpsw_soc_devices[] = { 15078c2ecf20Sopenharmony_ci { .family = "AM33xx", .revision = "ES1.0"}, 15088c2ecf20Sopenharmony_ci { /* sentinel */ } 15098c2ecf20Sopenharmony_ci}; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_cistatic int cpsw_probe(struct platform_device *pdev) 15128c2ecf20Sopenharmony_ci{ 15138c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 15148c2ecf20Sopenharmony_ci struct clk *clk; 15158c2ecf20Sopenharmony_ci struct cpsw_platform_data *data; 15168c2ecf20Sopenharmony_ci struct net_device *ndev; 15178c2ecf20Sopenharmony_ci struct cpsw_priv *priv; 15188c2ecf20Sopenharmony_ci void __iomem *ss_regs; 15198c2ecf20Sopenharmony_ci struct resource *ss_res; 15208c2ecf20Sopenharmony_ci struct gpio_descs *mode; 15218c2ecf20Sopenharmony_ci const struct soc_device_attribute *soc; 15228c2ecf20Sopenharmony_ci struct cpsw_common *cpsw; 15238c2ecf20Sopenharmony_ci int ret = 0, ch; 15248c2ecf20Sopenharmony_ci int irq; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci cpsw = devm_kzalloc(dev, sizeof(struct cpsw_common), GFP_KERNEL); 15278c2ecf20Sopenharmony_ci if (!cpsw) 15288c2ecf20Sopenharmony_ci return -ENOMEM; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, cpsw); 15318c2ecf20Sopenharmony_ci cpsw_slave_index = cpsw_slave_index_priv; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci cpsw->dev = dev; 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci mode = devm_gpiod_get_array_optional(dev, "mode", GPIOD_OUT_LOW); 15368c2ecf20Sopenharmony_ci if (IS_ERR(mode)) { 15378c2ecf20Sopenharmony_ci ret = PTR_ERR(mode); 15388c2ecf20Sopenharmony_ci dev_err(dev, "gpio request failed, ret %d\n", ret); 15398c2ecf20Sopenharmony_ci return ret; 15408c2ecf20Sopenharmony_ci } 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci clk = devm_clk_get(dev, "fck"); 15438c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 15448c2ecf20Sopenharmony_ci ret = PTR_ERR(clk); 15458c2ecf20Sopenharmony_ci dev_err(dev, "fck is not found %d\n", ret); 15468c2ecf20Sopenharmony_ci return ret; 15478c2ecf20Sopenharmony_ci } 15488c2ecf20Sopenharmony_ci cpsw->bus_freq_mhz = clk_get_rate(clk) / 1000000; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci ss_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 15518c2ecf20Sopenharmony_ci ss_regs = devm_ioremap_resource(dev, ss_res); 15528c2ecf20Sopenharmony_ci if (IS_ERR(ss_regs)) 15538c2ecf20Sopenharmony_ci return PTR_ERR(ss_regs); 15548c2ecf20Sopenharmony_ci cpsw->regs = ss_regs; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci cpsw->wr_regs = devm_platform_ioremap_resource(pdev, 1); 15578c2ecf20Sopenharmony_ci if (IS_ERR(cpsw->wr_regs)) 15588c2ecf20Sopenharmony_ci return PTR_ERR(cpsw->wr_regs); 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci /* RX IRQ */ 15618c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 1); 15628c2ecf20Sopenharmony_ci if (irq < 0) 15638c2ecf20Sopenharmony_ci return irq; 15648c2ecf20Sopenharmony_ci cpsw->irqs_table[0] = irq; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci /* TX IRQ */ 15678c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 2); 15688c2ecf20Sopenharmony_ci if (irq < 0) 15698c2ecf20Sopenharmony_ci return irq; 15708c2ecf20Sopenharmony_ci cpsw->irqs_table[1] = irq; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci /* get misc irq*/ 15738c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 3); 15748c2ecf20Sopenharmony_ci if (irq <= 0) 15758c2ecf20Sopenharmony_ci return irq; 15768c2ecf20Sopenharmony_ci cpsw->misc_irq = irq; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci /* 15798c2ecf20Sopenharmony_ci * This may be required here for child devices. 15808c2ecf20Sopenharmony_ci */ 15818c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci /* Need to enable clocks with runtime PM api to access module 15848c2ecf20Sopenharmony_ci * registers 15858c2ecf20Sopenharmony_ci */ 15868c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 15878c2ecf20Sopenharmony_ci if (ret < 0) { 15888c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev); 15898c2ecf20Sopenharmony_ci goto clean_runtime_disable_ret; 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci ret = cpsw_probe_dt(&cpsw->data, pdev); 15938c2ecf20Sopenharmony_ci if (ret) 15948c2ecf20Sopenharmony_ci goto clean_dt_ret; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci soc = soc_device_match(cpsw_soc_devices); 15978c2ecf20Sopenharmony_ci if (soc) 15988c2ecf20Sopenharmony_ci cpsw->quirk_irq = true; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci data = &cpsw->data; 16018c2ecf20Sopenharmony_ci cpsw->slaves = devm_kcalloc(dev, 16028c2ecf20Sopenharmony_ci data->slaves, sizeof(struct cpsw_slave), 16038c2ecf20Sopenharmony_ci GFP_KERNEL); 16048c2ecf20Sopenharmony_ci if (!cpsw->slaves) { 16058c2ecf20Sopenharmony_ci ret = -ENOMEM; 16068c2ecf20Sopenharmony_ci goto clean_dt_ret; 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci cpsw->rx_packet_max = max(rx_packet_max, CPSW_MAX_PACKET_SIZE); 16108c2ecf20Sopenharmony_ci cpsw->descs_pool_size = descs_pool_size; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci ret = cpsw_init_common(cpsw, ss_regs, ale_ageout, 16138c2ecf20Sopenharmony_ci ss_res->start + CPSW2_BD_OFFSET, 16148c2ecf20Sopenharmony_ci descs_pool_size); 16158c2ecf20Sopenharmony_ci if (ret) 16168c2ecf20Sopenharmony_ci goto clean_dt_ret; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci ch = cpsw->quirk_irq ? 0 : 7; 16198c2ecf20Sopenharmony_ci cpsw->txv[0].ch = cpdma_chan_create(cpsw->dma, ch, cpsw_tx_handler, 0); 16208c2ecf20Sopenharmony_ci if (IS_ERR(cpsw->txv[0].ch)) { 16218c2ecf20Sopenharmony_ci dev_err(dev, "error initializing tx dma channel\n"); 16228c2ecf20Sopenharmony_ci ret = PTR_ERR(cpsw->txv[0].ch); 16238c2ecf20Sopenharmony_ci goto clean_cpts; 16248c2ecf20Sopenharmony_ci } 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci cpsw->rxv[0].ch = cpdma_chan_create(cpsw->dma, 0, cpsw_rx_handler, 1); 16278c2ecf20Sopenharmony_ci if (IS_ERR(cpsw->rxv[0].ch)) { 16288c2ecf20Sopenharmony_ci dev_err(dev, "error initializing rx dma channel\n"); 16298c2ecf20Sopenharmony_ci ret = PTR_ERR(cpsw->rxv[0].ch); 16308c2ecf20Sopenharmony_ci goto clean_cpts; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci cpsw_split_res(cpsw); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci /* setup netdev */ 16358c2ecf20Sopenharmony_ci ndev = devm_alloc_etherdev_mqs(dev, sizeof(struct cpsw_priv), 16368c2ecf20Sopenharmony_ci CPSW_MAX_QUEUES, CPSW_MAX_QUEUES); 16378c2ecf20Sopenharmony_ci if (!ndev) { 16388c2ecf20Sopenharmony_ci dev_err(dev, "error allocating net_device\n"); 16398c2ecf20Sopenharmony_ci ret = -ENOMEM; 16408c2ecf20Sopenharmony_ci goto clean_cpts; 16418c2ecf20Sopenharmony_ci } 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci priv = netdev_priv(ndev); 16448c2ecf20Sopenharmony_ci priv->cpsw = cpsw; 16458c2ecf20Sopenharmony_ci priv->ndev = ndev; 16468c2ecf20Sopenharmony_ci priv->dev = dev; 16478c2ecf20Sopenharmony_ci priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG); 16488c2ecf20Sopenharmony_ci priv->emac_port = 0; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci if (is_valid_ether_addr(data->slave_data[0].mac_addr)) { 16518c2ecf20Sopenharmony_ci memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN); 16528c2ecf20Sopenharmony_ci dev_info(dev, "Detected MACID = %pM\n", priv->mac_addr); 16538c2ecf20Sopenharmony_ci } else { 16548c2ecf20Sopenharmony_ci eth_random_addr(priv->mac_addr); 16558c2ecf20Sopenharmony_ci dev_info(dev, "Random MACID = %pM\n", priv->mac_addr); 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN); 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci cpsw->slaves[0].ndev = ndev; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci ndev->netdev_ops = &cpsw_netdev_ops; 16658c2ecf20Sopenharmony_ci ndev->ethtool_ops = &cpsw_ethtool_ops; 16668c2ecf20Sopenharmony_ci netif_napi_add(ndev, &cpsw->napi_rx, 16678c2ecf20Sopenharmony_ci cpsw->quirk_irq ? cpsw_rx_poll : cpsw_rx_mq_poll, 16688c2ecf20Sopenharmony_ci CPSW_POLL_WEIGHT); 16698c2ecf20Sopenharmony_ci netif_tx_napi_add(ndev, &cpsw->napi_tx, 16708c2ecf20Sopenharmony_ci cpsw->quirk_irq ? cpsw_tx_poll : cpsw_tx_mq_poll, 16718c2ecf20Sopenharmony_ci CPSW_POLL_WEIGHT); 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci /* register the network device */ 16748c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, dev); 16758c2ecf20Sopenharmony_ci ndev->dev.of_node = cpsw->slaves[0].data->slave_node; 16768c2ecf20Sopenharmony_ci ret = register_netdev(ndev); 16778c2ecf20Sopenharmony_ci if (ret) { 16788c2ecf20Sopenharmony_ci dev_err(dev, "error registering net device\n"); 16798c2ecf20Sopenharmony_ci ret = -ENODEV; 16808c2ecf20Sopenharmony_ci goto clean_cpts; 16818c2ecf20Sopenharmony_ci } 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci if (cpsw->data.dual_emac) { 16848c2ecf20Sopenharmony_ci ret = cpsw_probe_dual_emac(priv); 16858c2ecf20Sopenharmony_ci if (ret) { 16868c2ecf20Sopenharmony_ci cpsw_err(priv, probe, "error probe slave 2 emac interface\n"); 16878c2ecf20Sopenharmony_ci goto clean_unregister_netdev_ret; 16888c2ecf20Sopenharmony_ci } 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci /* Grab RX and TX IRQs. Note that we also have RX_THRESHOLD and 16928c2ecf20Sopenharmony_ci * MISC IRQs which are always kept disabled with this driver so 16938c2ecf20Sopenharmony_ci * we will not request them. 16948c2ecf20Sopenharmony_ci * 16958c2ecf20Sopenharmony_ci * If anyone wants to implement support for those, make sure to 16968c2ecf20Sopenharmony_ci * first request and append them to irqs_table array. 16978c2ecf20Sopenharmony_ci */ 16988c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, cpsw->irqs_table[0], cpsw_rx_interrupt, 16998c2ecf20Sopenharmony_ci 0, dev_name(dev), cpsw); 17008c2ecf20Sopenharmony_ci if (ret < 0) { 17018c2ecf20Sopenharmony_ci dev_err(dev, "error attaching irq (%d)\n", ret); 17028c2ecf20Sopenharmony_ci goto clean_unregister_netdev_ret; 17038c2ecf20Sopenharmony_ci } 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, cpsw->irqs_table[1], cpsw_tx_interrupt, 17078c2ecf20Sopenharmony_ci 0, dev_name(&pdev->dev), cpsw); 17088c2ecf20Sopenharmony_ci if (ret < 0) { 17098c2ecf20Sopenharmony_ci dev_err(dev, "error attaching irq (%d)\n", ret); 17108c2ecf20Sopenharmony_ci goto clean_unregister_netdev_ret; 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci if (!cpsw->cpts) 17148c2ecf20Sopenharmony_ci goto skip_cpts; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, cpsw->misc_irq, cpsw_misc_interrupt, 17178c2ecf20Sopenharmony_ci 0, dev_name(&pdev->dev), cpsw); 17188c2ecf20Sopenharmony_ci if (ret < 0) { 17198c2ecf20Sopenharmony_ci dev_err(dev, "error attaching misc irq (%d)\n", ret); 17208c2ecf20Sopenharmony_ci goto clean_unregister_netdev_ret; 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci /* Enable misc CPTS evnt_pend IRQ */ 17248c2ecf20Sopenharmony_ci cpts_set_irqpoll(cpsw->cpts, false); 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ciskip_cpts: 17278c2ecf20Sopenharmony_ci cpsw_notice(priv, probe, 17288c2ecf20Sopenharmony_ci "initialized device (regs %pa, irq %d, pool size %d)\n", 17298c2ecf20Sopenharmony_ci &ss_res->start, cpsw->irqs_table[0], descs_pool_size); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci pm_runtime_put(&pdev->dev); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci return 0; 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ciclean_unregister_netdev_ret: 17368c2ecf20Sopenharmony_ci unregister_netdev(ndev); 17378c2ecf20Sopenharmony_ciclean_cpts: 17388c2ecf20Sopenharmony_ci cpts_release(cpsw->cpts); 17398c2ecf20Sopenharmony_ci cpdma_ctlr_destroy(cpsw->dma); 17408c2ecf20Sopenharmony_ciclean_dt_ret: 17418c2ecf20Sopenharmony_ci cpsw_remove_dt(pdev); 17428c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 17438c2ecf20Sopenharmony_ciclean_runtime_disable_ret: 17448c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 17458c2ecf20Sopenharmony_ci return ret; 17468c2ecf20Sopenharmony_ci} 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_cistatic int cpsw_remove(struct platform_device *pdev) 17498c2ecf20Sopenharmony_ci{ 17508c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = platform_get_drvdata(pdev); 17518c2ecf20Sopenharmony_ci int i, ret; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(&pdev->dev); 17548c2ecf20Sopenharmony_ci if (ret < 0) { 17558c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 17568c2ecf20Sopenharmony_ci return ret; 17578c2ecf20Sopenharmony_ci } 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci for (i = 0; i < cpsw->data.slaves; i++) 17608c2ecf20Sopenharmony_ci if (cpsw->slaves[i].ndev) 17618c2ecf20Sopenharmony_ci unregister_netdev(cpsw->slaves[i].ndev); 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci cpts_release(cpsw->cpts); 17648c2ecf20Sopenharmony_ci cpdma_ctlr_destroy(cpsw->dma); 17658c2ecf20Sopenharmony_ci cpsw_remove_dt(pdev); 17668c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 17678c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 17688c2ecf20Sopenharmony_ci return 0; 17698c2ecf20Sopenharmony_ci} 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 17728c2ecf20Sopenharmony_cistatic int cpsw_suspend(struct device *dev) 17738c2ecf20Sopenharmony_ci{ 17748c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = dev_get_drvdata(dev); 17758c2ecf20Sopenharmony_ci int i; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci rtnl_lock(); 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci for (i = 0; i < cpsw->data.slaves; i++) 17808c2ecf20Sopenharmony_ci if (cpsw->slaves[i].ndev) 17818c2ecf20Sopenharmony_ci if (netif_running(cpsw->slaves[i].ndev)) 17828c2ecf20Sopenharmony_ci cpsw_ndo_stop(cpsw->slaves[i].ndev); 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci rtnl_unlock(); 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci /* Select sleep pin state */ 17878c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(dev); 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci return 0; 17908c2ecf20Sopenharmony_ci} 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_cistatic int cpsw_resume(struct device *dev) 17938c2ecf20Sopenharmony_ci{ 17948c2ecf20Sopenharmony_ci struct cpsw_common *cpsw = dev_get_drvdata(dev); 17958c2ecf20Sopenharmony_ci int i; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci /* Select default pin state */ 17988c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(dev); 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci /* shut up ASSERT_RTNL() warning in netif_set_real_num_tx/rx_queues */ 18018c2ecf20Sopenharmony_ci rtnl_lock(); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci for (i = 0; i < cpsw->data.slaves; i++) 18048c2ecf20Sopenharmony_ci if (cpsw->slaves[i].ndev) 18058c2ecf20Sopenharmony_ci if (netif_running(cpsw->slaves[i].ndev)) 18068c2ecf20Sopenharmony_ci cpsw_ndo_open(cpsw->slaves[i].ndev); 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci rtnl_unlock(); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci return 0; 18118c2ecf20Sopenharmony_ci} 18128c2ecf20Sopenharmony_ci#endif 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cpsw_pm_ops, cpsw_suspend, cpsw_resume); 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_cistatic struct platform_driver cpsw_driver = { 18178c2ecf20Sopenharmony_ci .driver = { 18188c2ecf20Sopenharmony_ci .name = "cpsw", 18198c2ecf20Sopenharmony_ci .pm = &cpsw_pm_ops, 18208c2ecf20Sopenharmony_ci .of_match_table = cpsw_of_mtable, 18218c2ecf20Sopenharmony_ci }, 18228c2ecf20Sopenharmony_ci .probe = cpsw_probe, 18238c2ecf20Sopenharmony_ci .remove = cpsw_remove, 18248c2ecf20Sopenharmony_ci}; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_cimodule_platform_driver(cpsw_driver); 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 18298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Cyril Chemparathy <cyril@ti.com>"); 18308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mugunthan V N <mugunthanvnm@ti.com>"); 18318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI CPSW Ethernet driver"); 1832