18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Driver for Marvell NETA network card for Armada XP and Armada 370 SoCs. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2012 Marvell 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Rami Rosen <rosenr@marvell.com> 78c2ecf20Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 108c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 118c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/cpu.h> 168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 188c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/mbus.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 258c2ecf20Sopenharmony_ci#include <linux/of.h> 268c2ecf20Sopenharmony_ci#include <linux/of_address.h> 278c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 288c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 298c2ecf20Sopenharmony_ci#include <linux/of_net.h> 308c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 318c2ecf20Sopenharmony_ci#include <linux/phy.h> 328c2ecf20Sopenharmony_ci#include <linux/phylink.h> 338c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 348c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 358c2ecf20Sopenharmony_ci#include <net/hwbm.h> 368c2ecf20Sopenharmony_ci#include "mvneta_bm.h" 378c2ecf20Sopenharmony_ci#include <net/ip.h> 388c2ecf20Sopenharmony_ci#include <net/ipv6.h> 398c2ecf20Sopenharmony_ci#include <net/tso.h> 408c2ecf20Sopenharmony_ci#include <net/page_pool.h> 418c2ecf20Sopenharmony_ci#include <linux/bpf_trace.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Registers */ 448c2ecf20Sopenharmony_ci#define MVNETA_RXQ_CONFIG_REG(q) (0x1400 + ((q) << 2)) 458c2ecf20Sopenharmony_ci#define MVNETA_RXQ_HW_BUF_ALLOC BIT(0) 468c2ecf20Sopenharmony_ci#define MVNETA_RXQ_SHORT_POOL_ID_SHIFT 4 478c2ecf20Sopenharmony_ci#define MVNETA_RXQ_SHORT_POOL_ID_MASK 0x30 488c2ecf20Sopenharmony_ci#define MVNETA_RXQ_LONG_POOL_ID_SHIFT 6 498c2ecf20Sopenharmony_ci#define MVNETA_RXQ_LONG_POOL_ID_MASK 0xc0 508c2ecf20Sopenharmony_ci#define MVNETA_RXQ_PKT_OFFSET_ALL_MASK (0xf << 8) 518c2ecf20Sopenharmony_ci#define MVNETA_RXQ_PKT_OFFSET_MASK(offs) ((offs) << 8) 528c2ecf20Sopenharmony_ci#define MVNETA_RXQ_THRESHOLD_REG(q) (0x14c0 + ((q) << 2)) 538c2ecf20Sopenharmony_ci#define MVNETA_RXQ_NON_OCCUPIED(v) ((v) << 16) 548c2ecf20Sopenharmony_ci#define MVNETA_RXQ_BASE_ADDR_REG(q) (0x1480 + ((q) << 2)) 558c2ecf20Sopenharmony_ci#define MVNETA_RXQ_SIZE_REG(q) (0x14a0 + ((q) << 2)) 568c2ecf20Sopenharmony_ci#define MVNETA_RXQ_BUF_SIZE_SHIFT 19 578c2ecf20Sopenharmony_ci#define MVNETA_RXQ_BUF_SIZE_MASK (0x1fff << 19) 588c2ecf20Sopenharmony_ci#define MVNETA_RXQ_STATUS_REG(q) (0x14e0 + ((q) << 2)) 598c2ecf20Sopenharmony_ci#define MVNETA_RXQ_OCCUPIED_ALL_MASK 0x3fff 608c2ecf20Sopenharmony_ci#define MVNETA_RXQ_STATUS_UPDATE_REG(q) (0x1500 + ((q) << 2)) 618c2ecf20Sopenharmony_ci#define MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT 16 628c2ecf20Sopenharmony_ci#define MVNETA_RXQ_ADD_NON_OCCUPIED_MAX 255 638c2ecf20Sopenharmony_ci#define MVNETA_PORT_POOL_BUFFER_SZ_REG(pool) (0x1700 + ((pool) << 2)) 648c2ecf20Sopenharmony_ci#define MVNETA_PORT_POOL_BUFFER_SZ_SHIFT 3 658c2ecf20Sopenharmony_ci#define MVNETA_PORT_POOL_BUFFER_SZ_MASK 0xfff8 668c2ecf20Sopenharmony_ci#define MVNETA_PORT_RX_RESET 0x1cc0 678c2ecf20Sopenharmony_ci#define MVNETA_PORT_RX_DMA_RESET BIT(0) 688c2ecf20Sopenharmony_ci#define MVNETA_PHY_ADDR 0x2000 698c2ecf20Sopenharmony_ci#define MVNETA_PHY_ADDR_MASK 0x1f 708c2ecf20Sopenharmony_ci#define MVNETA_MBUS_RETRY 0x2010 718c2ecf20Sopenharmony_ci#define MVNETA_UNIT_INTR_CAUSE 0x2080 728c2ecf20Sopenharmony_ci#define MVNETA_UNIT_CONTROL 0x20B0 738c2ecf20Sopenharmony_ci#define MVNETA_PHY_POLLING_ENABLE BIT(1) 748c2ecf20Sopenharmony_ci#define MVNETA_WIN_BASE(w) (0x2200 + ((w) << 3)) 758c2ecf20Sopenharmony_ci#define MVNETA_WIN_SIZE(w) (0x2204 + ((w) << 3)) 768c2ecf20Sopenharmony_ci#define MVNETA_WIN_REMAP(w) (0x2280 + ((w) << 2)) 778c2ecf20Sopenharmony_ci#define MVNETA_BASE_ADDR_ENABLE 0x2290 788c2ecf20Sopenharmony_ci#define MVNETA_ACCESS_PROTECT_ENABLE 0x2294 798c2ecf20Sopenharmony_ci#define MVNETA_PORT_CONFIG 0x2400 808c2ecf20Sopenharmony_ci#define MVNETA_UNI_PROMISC_MODE BIT(0) 818c2ecf20Sopenharmony_ci#define MVNETA_DEF_RXQ(q) ((q) << 1) 828c2ecf20Sopenharmony_ci#define MVNETA_DEF_RXQ_ARP(q) ((q) << 4) 838c2ecf20Sopenharmony_ci#define MVNETA_TX_UNSET_ERR_SUM BIT(12) 848c2ecf20Sopenharmony_ci#define MVNETA_DEF_RXQ_TCP(q) ((q) << 16) 858c2ecf20Sopenharmony_ci#define MVNETA_DEF_RXQ_UDP(q) ((q) << 19) 868c2ecf20Sopenharmony_ci#define MVNETA_DEF_RXQ_BPDU(q) ((q) << 22) 878c2ecf20Sopenharmony_ci#define MVNETA_RX_CSUM_WITH_PSEUDO_HDR BIT(25) 888c2ecf20Sopenharmony_ci#define MVNETA_PORT_CONFIG_DEFL_VALUE(q) (MVNETA_DEF_RXQ(q) | \ 898c2ecf20Sopenharmony_ci MVNETA_DEF_RXQ_ARP(q) | \ 908c2ecf20Sopenharmony_ci MVNETA_DEF_RXQ_TCP(q) | \ 918c2ecf20Sopenharmony_ci MVNETA_DEF_RXQ_UDP(q) | \ 928c2ecf20Sopenharmony_ci MVNETA_DEF_RXQ_BPDU(q) | \ 938c2ecf20Sopenharmony_ci MVNETA_TX_UNSET_ERR_SUM | \ 948c2ecf20Sopenharmony_ci MVNETA_RX_CSUM_WITH_PSEUDO_HDR) 958c2ecf20Sopenharmony_ci#define MVNETA_PORT_CONFIG_EXTEND 0x2404 968c2ecf20Sopenharmony_ci#define MVNETA_MAC_ADDR_LOW 0x2414 978c2ecf20Sopenharmony_ci#define MVNETA_MAC_ADDR_HIGH 0x2418 988c2ecf20Sopenharmony_ci#define MVNETA_SDMA_CONFIG 0x241c 998c2ecf20Sopenharmony_ci#define MVNETA_SDMA_BRST_SIZE_16 4 1008c2ecf20Sopenharmony_ci#define MVNETA_RX_BRST_SZ_MASK(burst) ((burst) << 1) 1018c2ecf20Sopenharmony_ci#define MVNETA_RX_NO_DATA_SWAP BIT(4) 1028c2ecf20Sopenharmony_ci#define MVNETA_TX_NO_DATA_SWAP BIT(5) 1038c2ecf20Sopenharmony_ci#define MVNETA_DESC_SWAP BIT(6) 1048c2ecf20Sopenharmony_ci#define MVNETA_TX_BRST_SZ_MASK(burst) ((burst) << 22) 1058c2ecf20Sopenharmony_ci#define MVNETA_PORT_STATUS 0x2444 1068c2ecf20Sopenharmony_ci#define MVNETA_TX_IN_PRGRS BIT(0) 1078c2ecf20Sopenharmony_ci#define MVNETA_TX_FIFO_EMPTY BIT(8) 1088c2ecf20Sopenharmony_ci#define MVNETA_RX_MIN_FRAME_SIZE 0x247c 1098c2ecf20Sopenharmony_ci/* Only exists on Armada XP and Armada 370 */ 1108c2ecf20Sopenharmony_ci#define MVNETA_SERDES_CFG 0x24A0 1118c2ecf20Sopenharmony_ci#define MVNETA_SGMII_SERDES_PROTO 0x0cc7 1128c2ecf20Sopenharmony_ci#define MVNETA_QSGMII_SERDES_PROTO 0x0667 1138c2ecf20Sopenharmony_ci#define MVNETA_HSGMII_SERDES_PROTO 0x1107 1148c2ecf20Sopenharmony_ci#define MVNETA_TYPE_PRIO 0x24bc 1158c2ecf20Sopenharmony_ci#define MVNETA_FORCE_UNI BIT(21) 1168c2ecf20Sopenharmony_ci#define MVNETA_TXQ_CMD_1 0x24e4 1178c2ecf20Sopenharmony_ci#define MVNETA_TXQ_CMD 0x2448 1188c2ecf20Sopenharmony_ci#define MVNETA_TXQ_DISABLE_SHIFT 8 1198c2ecf20Sopenharmony_ci#define MVNETA_TXQ_ENABLE_MASK 0x000000ff 1208c2ecf20Sopenharmony_ci#define MVNETA_RX_DISCARD_FRAME_COUNT 0x2484 1218c2ecf20Sopenharmony_ci#define MVNETA_OVERRUN_FRAME_COUNT 0x2488 1228c2ecf20Sopenharmony_ci#define MVNETA_GMAC_CLOCK_DIVIDER 0x24f4 1238c2ecf20Sopenharmony_ci#define MVNETA_GMAC_1MS_CLOCK_ENABLE BIT(31) 1248c2ecf20Sopenharmony_ci#define MVNETA_ACC_MODE 0x2500 1258c2ecf20Sopenharmony_ci#define MVNETA_BM_ADDRESS 0x2504 1268c2ecf20Sopenharmony_ci#define MVNETA_CPU_MAP(cpu) (0x2540 + ((cpu) << 2)) 1278c2ecf20Sopenharmony_ci#define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff 1288c2ecf20Sopenharmony_ci#define MVNETA_CPU_TXQ_ACCESS_ALL_MASK 0x0000ff00 1298c2ecf20Sopenharmony_ci#define MVNETA_CPU_RXQ_ACCESS(rxq) BIT(rxq) 1308c2ecf20Sopenharmony_ci#define MVNETA_CPU_TXQ_ACCESS(txq) BIT(txq + 8) 1318c2ecf20Sopenharmony_ci#define MVNETA_RXQ_TIME_COAL_REG(q) (0x2580 + ((q) << 2)) 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* Exception Interrupt Port/Queue Cause register 1348c2ecf20Sopenharmony_ci * 1358c2ecf20Sopenharmony_ci * Their behavior depend of the mapping done using the PCPX2Q 1368c2ecf20Sopenharmony_ci * registers. For a given CPU if the bit associated to a queue is not 1378c2ecf20Sopenharmony_ci * set, then for the register a read from this CPU will always return 1388c2ecf20Sopenharmony_ci * 0 and a write won't do anything 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci#define MVNETA_INTR_NEW_CAUSE 0x25a0 1428c2ecf20Sopenharmony_ci#define MVNETA_INTR_NEW_MASK 0x25a4 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* bits 0..7 = TXQ SENT, one bit per queue. 1458c2ecf20Sopenharmony_ci * bits 8..15 = RXQ OCCUP, one bit per queue. 1468c2ecf20Sopenharmony_ci * bits 16..23 = RXQ FREE, one bit per queue. 1478c2ecf20Sopenharmony_ci * bit 29 = OLD_REG_SUM, see old reg ? 1488c2ecf20Sopenharmony_ci * bit 30 = TX_ERR_SUM, one bit for 4 ports 1498c2ecf20Sopenharmony_ci * bit 31 = MISC_SUM, one bit for 4 ports 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci#define MVNETA_TX_INTR_MASK(nr_txqs) (((1 << nr_txqs) - 1) << 0) 1528c2ecf20Sopenharmony_ci#define MVNETA_TX_INTR_MASK_ALL (0xff << 0) 1538c2ecf20Sopenharmony_ci#define MVNETA_RX_INTR_MASK(nr_rxqs) (((1 << nr_rxqs) - 1) << 8) 1548c2ecf20Sopenharmony_ci#define MVNETA_RX_INTR_MASK_ALL (0xff << 8) 1558c2ecf20Sopenharmony_ci#define MVNETA_MISCINTR_INTR_MASK BIT(31) 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci#define MVNETA_INTR_OLD_CAUSE 0x25a8 1588c2ecf20Sopenharmony_ci#define MVNETA_INTR_OLD_MASK 0x25ac 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* Data Path Port/Queue Cause Register */ 1618c2ecf20Sopenharmony_ci#define MVNETA_INTR_MISC_CAUSE 0x25b0 1628c2ecf20Sopenharmony_ci#define MVNETA_INTR_MISC_MASK 0x25b4 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_PHY_STATUS_CHANGE BIT(0) 1658c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_LINK_CHANGE BIT(1) 1668c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_PTP BIT(4) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_INTERNAL_ADDR_ERR BIT(7) 1698c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_RX_OVERRUN BIT(8) 1708c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_RX_CRC_ERROR BIT(9) 1718c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_RX_LARGE_PKT BIT(10) 1728c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_TX_UNDERUN BIT(11) 1738c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_PRBS_ERR BIT(12) 1748c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_PSC_SYNC_CHANGE BIT(13) 1758c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_SERDES_SYNC_ERR BIT(14) 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_BMU_ALLOC_ERR_SHIFT 16 1788c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_BMU_ALLOC_ERR_ALL_MASK (0xF << MVNETA_CAUSE_BMU_ALLOC_ERR_SHIFT) 1798c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_BMU_ALLOC_ERR_MASK(pool) (1 << (MVNETA_CAUSE_BMU_ALLOC_ERR_SHIFT + (pool))) 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_TXQ_ERROR_SHIFT 24 1828c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_TXQ_ERROR_ALL_MASK (0xFF << MVNETA_CAUSE_TXQ_ERROR_SHIFT) 1838c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_TXQ_ERROR_MASK(q) (1 << (MVNETA_CAUSE_TXQ_ERROR_SHIFT + (q))) 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci#define MVNETA_INTR_ENABLE 0x25b8 1868c2ecf20Sopenharmony_ci#define MVNETA_TXQ_INTR_ENABLE_ALL_MASK 0x0000ff00 1878c2ecf20Sopenharmony_ci#define MVNETA_RXQ_INTR_ENABLE_ALL_MASK 0x000000ff 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci#define MVNETA_RXQ_CMD 0x2680 1908c2ecf20Sopenharmony_ci#define MVNETA_RXQ_DISABLE_SHIFT 8 1918c2ecf20Sopenharmony_ci#define MVNETA_RXQ_ENABLE_MASK 0x000000ff 1928c2ecf20Sopenharmony_ci#define MVETH_TXQ_TOKEN_COUNT_REG(q) (0x2700 + ((q) << 4)) 1938c2ecf20Sopenharmony_ci#define MVETH_TXQ_TOKEN_CFG_REG(q) (0x2704 + ((q) << 4)) 1948c2ecf20Sopenharmony_ci#define MVNETA_GMAC_CTRL_0 0x2c00 1958c2ecf20Sopenharmony_ci#define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2 1968c2ecf20Sopenharmony_ci#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc 1978c2ecf20Sopenharmony_ci#define MVNETA_GMAC0_PORT_1000BASE_X BIT(1) 1988c2ecf20Sopenharmony_ci#define MVNETA_GMAC0_PORT_ENABLE BIT(0) 1998c2ecf20Sopenharmony_ci#define MVNETA_GMAC_CTRL_2 0x2c08 2008c2ecf20Sopenharmony_ci#define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0) 2018c2ecf20Sopenharmony_ci#define MVNETA_GMAC2_PCS_ENABLE BIT(3) 2028c2ecf20Sopenharmony_ci#define MVNETA_GMAC2_PORT_RGMII BIT(4) 2038c2ecf20Sopenharmony_ci#define MVNETA_GMAC2_PORT_RESET BIT(6) 2048c2ecf20Sopenharmony_ci#define MVNETA_GMAC_STATUS 0x2c10 2058c2ecf20Sopenharmony_ci#define MVNETA_GMAC_LINK_UP BIT(0) 2068c2ecf20Sopenharmony_ci#define MVNETA_GMAC_SPEED_1000 BIT(1) 2078c2ecf20Sopenharmony_ci#define MVNETA_GMAC_SPEED_100 BIT(2) 2088c2ecf20Sopenharmony_ci#define MVNETA_GMAC_FULL_DUPLEX BIT(3) 2098c2ecf20Sopenharmony_ci#define MVNETA_GMAC_RX_FLOW_CTRL_ENABLE BIT(4) 2108c2ecf20Sopenharmony_ci#define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5) 2118c2ecf20Sopenharmony_ci#define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6) 2128c2ecf20Sopenharmony_ci#define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7) 2138c2ecf20Sopenharmony_ci#define MVNETA_GMAC_AN_COMPLETE BIT(11) 2148c2ecf20Sopenharmony_ci#define MVNETA_GMAC_SYNC_OK BIT(14) 2158c2ecf20Sopenharmony_ci#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c 2168c2ecf20Sopenharmony_ci#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0) 2178c2ecf20Sopenharmony_ci#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1) 2188c2ecf20Sopenharmony_ci#define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2) 2198c2ecf20Sopenharmony_ci#define MVNETA_GMAC_AN_BYPASS_ENABLE BIT(3) 2208c2ecf20Sopenharmony_ci#define MVNETA_GMAC_INBAND_RESTART_AN BIT(4) 2218c2ecf20Sopenharmony_ci#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5) 2228c2ecf20Sopenharmony_ci#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6) 2238c2ecf20Sopenharmony_ci#define MVNETA_GMAC_AN_SPEED_EN BIT(7) 2248c2ecf20Sopenharmony_ci#define MVNETA_GMAC_CONFIG_FLOW_CTRL BIT(8) 2258c2ecf20Sopenharmony_ci#define MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL BIT(9) 2268c2ecf20Sopenharmony_ci#define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11) 2278c2ecf20Sopenharmony_ci#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12) 2288c2ecf20Sopenharmony_ci#define MVNETA_GMAC_AN_DUPLEX_EN BIT(13) 2298c2ecf20Sopenharmony_ci#define MVNETA_GMAC_CTRL_4 0x2c90 2308c2ecf20Sopenharmony_ci#define MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE BIT(1) 2318c2ecf20Sopenharmony_ci#define MVNETA_MIB_COUNTERS_BASE 0x3000 2328c2ecf20Sopenharmony_ci#define MVNETA_MIB_LATE_COLLISION 0x7c 2338c2ecf20Sopenharmony_ci#define MVNETA_DA_FILT_SPEC_MCAST 0x3400 2348c2ecf20Sopenharmony_ci#define MVNETA_DA_FILT_OTH_MCAST 0x3500 2358c2ecf20Sopenharmony_ci#define MVNETA_DA_FILT_UCAST_BASE 0x3600 2368c2ecf20Sopenharmony_ci#define MVNETA_TXQ_BASE_ADDR_REG(q) (0x3c00 + ((q) << 2)) 2378c2ecf20Sopenharmony_ci#define MVNETA_TXQ_SIZE_REG(q) (0x3c20 + ((q) << 2)) 2388c2ecf20Sopenharmony_ci#define MVNETA_TXQ_SENT_THRESH_ALL_MASK 0x3fff0000 2398c2ecf20Sopenharmony_ci#define MVNETA_TXQ_SENT_THRESH_MASK(coal) ((coal) << 16) 2408c2ecf20Sopenharmony_ci#define MVNETA_TXQ_UPDATE_REG(q) (0x3c60 + ((q) << 2)) 2418c2ecf20Sopenharmony_ci#define MVNETA_TXQ_DEC_SENT_SHIFT 16 2428c2ecf20Sopenharmony_ci#define MVNETA_TXQ_DEC_SENT_MASK 0xff 2438c2ecf20Sopenharmony_ci#define MVNETA_TXQ_STATUS_REG(q) (0x3c40 + ((q) << 2)) 2448c2ecf20Sopenharmony_ci#define MVNETA_TXQ_SENT_DESC_SHIFT 16 2458c2ecf20Sopenharmony_ci#define MVNETA_TXQ_SENT_DESC_MASK 0x3fff0000 2468c2ecf20Sopenharmony_ci#define MVNETA_PORT_TX_RESET 0x3cf0 2478c2ecf20Sopenharmony_ci#define MVNETA_PORT_TX_DMA_RESET BIT(0) 2488c2ecf20Sopenharmony_ci#define MVNETA_TX_MTU 0x3e0c 2498c2ecf20Sopenharmony_ci#define MVNETA_TX_TOKEN_SIZE 0x3e14 2508c2ecf20Sopenharmony_ci#define MVNETA_TX_TOKEN_SIZE_MAX 0xffffffff 2518c2ecf20Sopenharmony_ci#define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2)) 2528c2ecf20Sopenharmony_ci#define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci#define MVNETA_LPI_CTRL_0 0x2cc0 2558c2ecf20Sopenharmony_ci#define MVNETA_LPI_CTRL_1 0x2cc4 2568c2ecf20Sopenharmony_ci#define MVNETA_LPI_REQUEST_ENABLE BIT(0) 2578c2ecf20Sopenharmony_ci#define MVNETA_LPI_CTRL_2 0x2cc8 2588c2ecf20Sopenharmony_ci#define MVNETA_LPI_STATUS 0x2ccc 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci#define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* Descriptor ring Macros */ 2638c2ecf20Sopenharmony_ci#define MVNETA_QUEUE_NEXT_DESC(q, index) \ 2648c2ecf20Sopenharmony_ci (((index) < (q)->last_desc) ? ((index) + 1) : 0) 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* Various constants */ 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/* Coalescing */ 2698c2ecf20Sopenharmony_ci#define MVNETA_TXDONE_COAL_PKTS 0 /* interrupt per packet */ 2708c2ecf20Sopenharmony_ci#define MVNETA_RX_COAL_PKTS 32 2718c2ecf20Sopenharmony_ci#define MVNETA_RX_COAL_USEC 100 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* The two bytes Marvell header. Either contains a special value used 2748c2ecf20Sopenharmony_ci * by Marvell switches when a specific hardware mode is enabled (not 2758c2ecf20Sopenharmony_ci * supported by this driver) or is filled automatically by zeroes on 2768c2ecf20Sopenharmony_ci * the RX side. Those two bytes being at the front of the Ethernet 2778c2ecf20Sopenharmony_ci * header, they allow to have the IP header aligned on a 4 bytes 2788c2ecf20Sopenharmony_ci * boundary automatically: the hardware skips those two bytes on its 2798c2ecf20Sopenharmony_ci * own. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci#define MVNETA_MH_SIZE 2 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci#define MVNETA_VLAN_TAG_LEN 4 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci#define MVNETA_TX_CSUM_DEF_SIZE 1600 2868c2ecf20Sopenharmony_ci#define MVNETA_TX_CSUM_MAX_SIZE 9800 2878c2ecf20Sopenharmony_ci#define MVNETA_ACC_MODE_EXT1 1 2888c2ecf20Sopenharmony_ci#define MVNETA_ACC_MODE_EXT2 2 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci#define MVNETA_MAX_DECODE_WIN 6 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* Timeout constants */ 2938c2ecf20Sopenharmony_ci#define MVNETA_TX_DISABLE_TIMEOUT_MSEC 1000 2948c2ecf20Sopenharmony_ci#define MVNETA_RX_DISABLE_TIMEOUT_MSEC 1000 2958c2ecf20Sopenharmony_ci#define MVNETA_TX_FIFO_EMPTY_TIMEOUT 10000 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci#define MVNETA_TX_MTU_MAX 0x3ffff 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* The RSS lookup table actually has 256 entries but we do not use 3008c2ecf20Sopenharmony_ci * them yet 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_ci#define MVNETA_RSS_LU_TABLE_SIZE 1 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/* Max number of Rx descriptors */ 3058c2ecf20Sopenharmony_ci#define MVNETA_MAX_RXD 512 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/* Max number of Tx descriptors */ 3088c2ecf20Sopenharmony_ci#define MVNETA_MAX_TXD 1024 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* Max number of allowed TCP segments for software TSO */ 3118c2ecf20Sopenharmony_ci#define MVNETA_MAX_TSO_SEGS 100 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci#define MVNETA_MAX_SKB_DESCS (MVNETA_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* descriptor aligned size */ 3168c2ecf20Sopenharmony_ci#define MVNETA_DESC_ALIGNED_SIZE 32 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* Number of bytes to be taken into account by HW when putting incoming data 3198c2ecf20Sopenharmony_ci * to the buffers. It is needed in case NET_SKB_PAD exceeds maximum packet 3208c2ecf20Sopenharmony_ci * offset supported in MVNETA_RXQ_CONFIG_REG(q) registers. 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_ci#define MVNETA_RX_PKT_OFFSET_CORRECTION 64 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci#define MVNETA_RX_PKT_SIZE(mtu) \ 3258c2ecf20Sopenharmony_ci ALIGN((mtu) + MVNETA_MH_SIZE + MVNETA_VLAN_TAG_LEN + \ 3268c2ecf20Sopenharmony_ci ETH_HLEN + ETH_FCS_LEN, \ 3278c2ecf20Sopenharmony_ci cache_line_size()) 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/* Driver assumes that the last 3 bits are 0 */ 3308c2ecf20Sopenharmony_ci#define MVNETA_SKB_HEADROOM ALIGN(max(NET_SKB_PAD, XDP_PACKET_HEADROOM), 8) 3318c2ecf20Sopenharmony_ci#define MVNETA_SKB_PAD (SKB_DATA_ALIGN(sizeof(struct skb_shared_info) + \ 3328c2ecf20Sopenharmony_ci MVNETA_SKB_HEADROOM)) 3338c2ecf20Sopenharmony_ci#define MVNETA_MAX_RX_BUF_SIZE (PAGE_SIZE - MVNETA_SKB_PAD) 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci#define IS_TSO_HEADER(txq, addr) \ 3368c2ecf20Sopenharmony_ci ((addr >= txq->tso_hdrs_phys) && \ 3378c2ecf20Sopenharmony_ci (addr < txq->tso_hdrs_phys + txq->size * TSO_HEADER_SIZE)) 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci#define MVNETA_RX_GET_BM_POOL_ID(rxd) \ 3408c2ecf20Sopenharmony_ci (((rxd)->status & MVNETA_RXD_BM_POOL_MASK) >> MVNETA_RXD_BM_POOL_SHIFT) 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cienum { 3438c2ecf20Sopenharmony_ci ETHTOOL_STAT_EEE_WAKEUP, 3448c2ecf20Sopenharmony_ci ETHTOOL_STAT_SKB_ALLOC_ERR, 3458c2ecf20Sopenharmony_ci ETHTOOL_STAT_REFILL_ERR, 3468c2ecf20Sopenharmony_ci ETHTOOL_XDP_REDIRECT, 3478c2ecf20Sopenharmony_ci ETHTOOL_XDP_PASS, 3488c2ecf20Sopenharmony_ci ETHTOOL_XDP_DROP, 3498c2ecf20Sopenharmony_ci ETHTOOL_XDP_TX, 3508c2ecf20Sopenharmony_ci ETHTOOL_XDP_TX_ERR, 3518c2ecf20Sopenharmony_ci ETHTOOL_XDP_XMIT, 3528c2ecf20Sopenharmony_ci ETHTOOL_XDP_XMIT_ERR, 3538c2ecf20Sopenharmony_ci ETHTOOL_MAX_STATS, 3548c2ecf20Sopenharmony_ci}; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistruct mvneta_statistic { 3578c2ecf20Sopenharmony_ci unsigned short offset; 3588c2ecf20Sopenharmony_ci unsigned short type; 3598c2ecf20Sopenharmony_ci const char name[ETH_GSTRING_LEN]; 3608c2ecf20Sopenharmony_ci}; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci#define T_REG_32 32 3638c2ecf20Sopenharmony_ci#define T_REG_64 64 3648c2ecf20Sopenharmony_ci#define T_SW 1 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci#define MVNETA_XDP_PASS 0 3678c2ecf20Sopenharmony_ci#define MVNETA_XDP_DROPPED BIT(0) 3688c2ecf20Sopenharmony_ci#define MVNETA_XDP_TX BIT(1) 3698c2ecf20Sopenharmony_ci#define MVNETA_XDP_REDIR BIT(2) 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic const struct mvneta_statistic mvneta_statistics[] = { 3728c2ecf20Sopenharmony_ci { 0x3000, T_REG_64, "good_octets_received", }, 3738c2ecf20Sopenharmony_ci { 0x3010, T_REG_32, "good_frames_received", }, 3748c2ecf20Sopenharmony_ci { 0x3008, T_REG_32, "bad_octets_received", }, 3758c2ecf20Sopenharmony_ci { 0x3014, T_REG_32, "bad_frames_received", }, 3768c2ecf20Sopenharmony_ci { 0x3018, T_REG_32, "broadcast_frames_received", }, 3778c2ecf20Sopenharmony_ci { 0x301c, T_REG_32, "multicast_frames_received", }, 3788c2ecf20Sopenharmony_ci { 0x3050, T_REG_32, "unrec_mac_control_received", }, 3798c2ecf20Sopenharmony_ci { 0x3058, T_REG_32, "good_fc_received", }, 3808c2ecf20Sopenharmony_ci { 0x305c, T_REG_32, "bad_fc_received", }, 3818c2ecf20Sopenharmony_ci { 0x3060, T_REG_32, "undersize_received", }, 3828c2ecf20Sopenharmony_ci { 0x3064, T_REG_32, "fragments_received", }, 3838c2ecf20Sopenharmony_ci { 0x3068, T_REG_32, "oversize_received", }, 3848c2ecf20Sopenharmony_ci { 0x306c, T_REG_32, "jabber_received", }, 3858c2ecf20Sopenharmony_ci { 0x3070, T_REG_32, "mac_receive_error", }, 3868c2ecf20Sopenharmony_ci { 0x3074, T_REG_32, "bad_crc_event", }, 3878c2ecf20Sopenharmony_ci { 0x3078, T_REG_32, "collision", }, 3888c2ecf20Sopenharmony_ci { 0x307c, T_REG_32, "late_collision", }, 3898c2ecf20Sopenharmony_ci { 0x2484, T_REG_32, "rx_discard", }, 3908c2ecf20Sopenharmony_ci { 0x2488, T_REG_32, "rx_overrun", }, 3918c2ecf20Sopenharmony_ci { 0x3020, T_REG_32, "frames_64_octets", }, 3928c2ecf20Sopenharmony_ci { 0x3024, T_REG_32, "frames_65_to_127_octets", }, 3938c2ecf20Sopenharmony_ci { 0x3028, T_REG_32, "frames_128_to_255_octets", }, 3948c2ecf20Sopenharmony_ci { 0x302c, T_REG_32, "frames_256_to_511_octets", }, 3958c2ecf20Sopenharmony_ci { 0x3030, T_REG_32, "frames_512_to_1023_octets", }, 3968c2ecf20Sopenharmony_ci { 0x3034, T_REG_32, "frames_1024_to_max_octets", }, 3978c2ecf20Sopenharmony_ci { 0x3038, T_REG_64, "good_octets_sent", }, 3988c2ecf20Sopenharmony_ci { 0x3040, T_REG_32, "good_frames_sent", }, 3998c2ecf20Sopenharmony_ci { 0x3044, T_REG_32, "excessive_collision", }, 4008c2ecf20Sopenharmony_ci { 0x3048, T_REG_32, "multicast_frames_sent", }, 4018c2ecf20Sopenharmony_ci { 0x304c, T_REG_32, "broadcast_frames_sent", }, 4028c2ecf20Sopenharmony_ci { 0x3054, T_REG_32, "fc_sent", }, 4038c2ecf20Sopenharmony_ci { 0x300c, T_REG_32, "internal_mac_transmit_err", }, 4048c2ecf20Sopenharmony_ci { ETHTOOL_STAT_EEE_WAKEUP, T_SW, "eee_wakeup_errors", }, 4058c2ecf20Sopenharmony_ci { ETHTOOL_STAT_SKB_ALLOC_ERR, T_SW, "skb_alloc_errors", }, 4068c2ecf20Sopenharmony_ci { ETHTOOL_STAT_REFILL_ERR, T_SW, "refill_errors", }, 4078c2ecf20Sopenharmony_ci { ETHTOOL_XDP_REDIRECT, T_SW, "rx_xdp_redirect", }, 4088c2ecf20Sopenharmony_ci { ETHTOOL_XDP_PASS, T_SW, "rx_xdp_pass", }, 4098c2ecf20Sopenharmony_ci { ETHTOOL_XDP_DROP, T_SW, "rx_xdp_drop", }, 4108c2ecf20Sopenharmony_ci { ETHTOOL_XDP_TX, T_SW, "rx_xdp_tx", }, 4118c2ecf20Sopenharmony_ci { ETHTOOL_XDP_TX_ERR, T_SW, "rx_xdp_tx_errors", }, 4128c2ecf20Sopenharmony_ci { ETHTOOL_XDP_XMIT, T_SW, "tx_xdp_xmit", }, 4138c2ecf20Sopenharmony_ci { ETHTOOL_XDP_XMIT_ERR, T_SW, "tx_xdp_xmit_errors", }, 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistruct mvneta_stats { 4178c2ecf20Sopenharmony_ci u64 rx_packets; 4188c2ecf20Sopenharmony_ci u64 rx_bytes; 4198c2ecf20Sopenharmony_ci u64 tx_packets; 4208c2ecf20Sopenharmony_ci u64 tx_bytes; 4218c2ecf20Sopenharmony_ci /* xdp */ 4228c2ecf20Sopenharmony_ci u64 xdp_redirect; 4238c2ecf20Sopenharmony_ci u64 xdp_pass; 4248c2ecf20Sopenharmony_ci u64 xdp_drop; 4258c2ecf20Sopenharmony_ci u64 xdp_xmit; 4268c2ecf20Sopenharmony_ci u64 xdp_xmit_err; 4278c2ecf20Sopenharmony_ci u64 xdp_tx; 4288c2ecf20Sopenharmony_ci u64 xdp_tx_err; 4298c2ecf20Sopenharmony_ci}; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistruct mvneta_ethtool_stats { 4328c2ecf20Sopenharmony_ci struct mvneta_stats ps; 4338c2ecf20Sopenharmony_ci u64 skb_alloc_error; 4348c2ecf20Sopenharmony_ci u64 refill_error; 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistruct mvneta_pcpu_stats { 4388c2ecf20Sopenharmony_ci struct u64_stats_sync syncp; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci struct mvneta_ethtool_stats es; 4418c2ecf20Sopenharmony_ci u64 rx_dropped; 4428c2ecf20Sopenharmony_ci u64 rx_errors; 4438c2ecf20Sopenharmony_ci}; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistruct mvneta_pcpu_port { 4468c2ecf20Sopenharmony_ci /* Pointer to the shared port */ 4478c2ecf20Sopenharmony_ci struct mvneta_port *pp; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* Pointer to the CPU-local NAPI struct */ 4508c2ecf20Sopenharmony_ci struct napi_struct napi; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* Cause of the previous interrupt */ 4538c2ecf20Sopenharmony_ci u32 cause_rx_tx; 4548c2ecf20Sopenharmony_ci}; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cienum { 4578c2ecf20Sopenharmony_ci __MVNETA_DOWN, 4588c2ecf20Sopenharmony_ci}; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistruct mvneta_port { 4618c2ecf20Sopenharmony_ci u8 id; 4628c2ecf20Sopenharmony_ci struct mvneta_pcpu_port __percpu *ports; 4638c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats __percpu *stats; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci unsigned long state; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci int pkt_size; 4688c2ecf20Sopenharmony_ci void __iomem *base; 4698c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxqs; 4708c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txqs; 4718c2ecf20Sopenharmony_ci struct net_device *dev; 4728c2ecf20Sopenharmony_ci struct hlist_node node_online; 4738c2ecf20Sopenharmony_ci struct hlist_node node_dead; 4748c2ecf20Sopenharmony_ci int rxq_def; 4758c2ecf20Sopenharmony_ci /* Protect the access to the percpu interrupt registers, 4768c2ecf20Sopenharmony_ci * ensuring that the configuration remains coherent. 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ci spinlock_t lock; 4798c2ecf20Sopenharmony_ci bool is_stopped; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci u32 cause_rx_tx; 4828c2ecf20Sopenharmony_ci struct napi_struct napi; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci struct bpf_prog *xdp_prog; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* Core clock */ 4878c2ecf20Sopenharmony_ci struct clk *clk; 4888c2ecf20Sopenharmony_ci /* AXI clock */ 4898c2ecf20Sopenharmony_ci struct clk *clk_bus; 4908c2ecf20Sopenharmony_ci u8 mcast_count[256]; 4918c2ecf20Sopenharmony_ci u16 tx_ring_size; 4928c2ecf20Sopenharmony_ci u16 rx_ring_size; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci phy_interface_t phy_interface; 4958c2ecf20Sopenharmony_ci struct device_node *dn; 4968c2ecf20Sopenharmony_ci unsigned int tx_csum_limit; 4978c2ecf20Sopenharmony_ci struct phylink *phylink; 4988c2ecf20Sopenharmony_ci struct phylink_config phylink_config; 4998c2ecf20Sopenharmony_ci struct phy *comphy; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci struct mvneta_bm *bm_priv; 5028c2ecf20Sopenharmony_ci struct mvneta_bm_pool *pool_long; 5038c2ecf20Sopenharmony_ci struct mvneta_bm_pool *pool_short; 5048c2ecf20Sopenharmony_ci int bm_win_id; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci bool eee_enabled; 5078c2ecf20Sopenharmony_ci bool eee_active; 5088c2ecf20Sopenharmony_ci bool tx_lpi_enabled; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)]; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci u32 indir[MVNETA_RSS_LU_TABLE_SIZE]; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* Flags for special SoC configurations */ 5158c2ecf20Sopenharmony_ci bool neta_armada3700; 5168c2ecf20Sopenharmony_ci u16 rx_offset_correction; 5178c2ecf20Sopenharmony_ci const struct mbus_dram_target_info *dram_target_info; 5188c2ecf20Sopenharmony_ci}; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci/* The mvneta_tx_desc and mvneta_rx_desc structures describe the 5218c2ecf20Sopenharmony_ci * layout of the transmit and reception DMA descriptors, and their 5228c2ecf20Sopenharmony_ci * layout is therefore defined by the hardware design 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci#define MVNETA_TX_L3_OFF_SHIFT 0 5268c2ecf20Sopenharmony_ci#define MVNETA_TX_IP_HLEN_SHIFT 8 5278c2ecf20Sopenharmony_ci#define MVNETA_TX_L4_UDP BIT(16) 5288c2ecf20Sopenharmony_ci#define MVNETA_TX_L3_IP6 BIT(17) 5298c2ecf20Sopenharmony_ci#define MVNETA_TXD_IP_CSUM BIT(18) 5308c2ecf20Sopenharmony_ci#define MVNETA_TXD_Z_PAD BIT(19) 5318c2ecf20Sopenharmony_ci#define MVNETA_TXD_L_DESC BIT(20) 5328c2ecf20Sopenharmony_ci#define MVNETA_TXD_F_DESC BIT(21) 5338c2ecf20Sopenharmony_ci#define MVNETA_TXD_FLZ_DESC (MVNETA_TXD_Z_PAD | \ 5348c2ecf20Sopenharmony_ci MVNETA_TXD_L_DESC | \ 5358c2ecf20Sopenharmony_ci MVNETA_TXD_F_DESC) 5368c2ecf20Sopenharmony_ci#define MVNETA_TX_L4_CSUM_FULL BIT(30) 5378c2ecf20Sopenharmony_ci#define MVNETA_TX_L4_CSUM_NOT BIT(31) 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci#define MVNETA_RXD_ERR_CRC 0x0 5408c2ecf20Sopenharmony_ci#define MVNETA_RXD_BM_POOL_SHIFT 13 5418c2ecf20Sopenharmony_ci#define MVNETA_RXD_BM_POOL_MASK (BIT(13) | BIT(14)) 5428c2ecf20Sopenharmony_ci#define MVNETA_RXD_ERR_SUMMARY BIT(16) 5438c2ecf20Sopenharmony_ci#define MVNETA_RXD_ERR_OVERRUN BIT(17) 5448c2ecf20Sopenharmony_ci#define MVNETA_RXD_ERR_LEN BIT(18) 5458c2ecf20Sopenharmony_ci#define MVNETA_RXD_ERR_RESOURCE (BIT(17) | BIT(18)) 5468c2ecf20Sopenharmony_ci#define MVNETA_RXD_ERR_CODE_MASK (BIT(17) | BIT(18)) 5478c2ecf20Sopenharmony_ci#define MVNETA_RXD_L3_IP4 BIT(25) 5488c2ecf20Sopenharmony_ci#define MVNETA_RXD_LAST_DESC BIT(26) 5498c2ecf20Sopenharmony_ci#define MVNETA_RXD_FIRST_DESC BIT(27) 5508c2ecf20Sopenharmony_ci#define MVNETA_RXD_FIRST_LAST_DESC (MVNETA_RXD_FIRST_DESC | \ 5518c2ecf20Sopenharmony_ci MVNETA_RXD_LAST_DESC) 5528c2ecf20Sopenharmony_ci#define MVNETA_RXD_L4_CSUM_OK BIT(30) 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci#if defined(__LITTLE_ENDIAN) 5558c2ecf20Sopenharmony_cistruct mvneta_tx_desc { 5568c2ecf20Sopenharmony_ci u32 command; /* Options used by HW for packet transmitting.*/ 5578c2ecf20Sopenharmony_ci u16 reserved1; /* csum_l4 (for future use) */ 5588c2ecf20Sopenharmony_ci u16 data_size; /* Data size of transmitted packet in bytes */ 5598c2ecf20Sopenharmony_ci u32 buf_phys_addr; /* Physical addr of transmitted buffer */ 5608c2ecf20Sopenharmony_ci u32 reserved2; /* hw_cmd - (for future use, PMT) */ 5618c2ecf20Sopenharmony_ci u32 reserved3[4]; /* Reserved - (for future use) */ 5628c2ecf20Sopenharmony_ci}; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistruct mvneta_rx_desc { 5658c2ecf20Sopenharmony_ci u32 status; /* Info about received packet */ 5668c2ecf20Sopenharmony_ci u16 reserved1; /* pnc_info - (for future use, PnC) */ 5678c2ecf20Sopenharmony_ci u16 data_size; /* Size of received packet in bytes */ 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci u32 buf_phys_addr; /* Physical address of the buffer */ 5708c2ecf20Sopenharmony_ci u32 reserved2; /* pnc_flow_id (for future use, PnC) */ 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci u32 buf_cookie; /* cookie for access to RX buffer in rx path */ 5738c2ecf20Sopenharmony_ci u16 reserved3; /* prefetch_cmd, for future use */ 5748c2ecf20Sopenharmony_ci u16 reserved4; /* csum_l4 - (for future use, PnC) */ 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci u32 reserved5; /* pnc_extra PnC (for future use, PnC) */ 5778c2ecf20Sopenharmony_ci u32 reserved6; /* hw_cmd (for future use, PnC and HWF) */ 5788c2ecf20Sopenharmony_ci}; 5798c2ecf20Sopenharmony_ci#else 5808c2ecf20Sopenharmony_cistruct mvneta_tx_desc { 5818c2ecf20Sopenharmony_ci u16 data_size; /* Data size of transmitted packet in bytes */ 5828c2ecf20Sopenharmony_ci u16 reserved1; /* csum_l4 (for future use) */ 5838c2ecf20Sopenharmony_ci u32 command; /* Options used by HW for packet transmitting.*/ 5848c2ecf20Sopenharmony_ci u32 reserved2; /* hw_cmd - (for future use, PMT) */ 5858c2ecf20Sopenharmony_ci u32 buf_phys_addr; /* Physical addr of transmitted buffer */ 5868c2ecf20Sopenharmony_ci u32 reserved3[4]; /* Reserved - (for future use) */ 5878c2ecf20Sopenharmony_ci}; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistruct mvneta_rx_desc { 5908c2ecf20Sopenharmony_ci u16 data_size; /* Size of received packet in bytes */ 5918c2ecf20Sopenharmony_ci u16 reserved1; /* pnc_info - (for future use, PnC) */ 5928c2ecf20Sopenharmony_ci u32 status; /* Info about received packet */ 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci u32 reserved2; /* pnc_flow_id (for future use, PnC) */ 5958c2ecf20Sopenharmony_ci u32 buf_phys_addr; /* Physical address of the buffer */ 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci u16 reserved4; /* csum_l4 - (for future use, PnC) */ 5988c2ecf20Sopenharmony_ci u16 reserved3; /* prefetch_cmd, for future use */ 5998c2ecf20Sopenharmony_ci u32 buf_cookie; /* cookie for access to RX buffer in rx path */ 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci u32 reserved5; /* pnc_extra PnC (for future use, PnC) */ 6028c2ecf20Sopenharmony_ci u32 reserved6; /* hw_cmd (for future use, PnC and HWF) */ 6038c2ecf20Sopenharmony_ci}; 6048c2ecf20Sopenharmony_ci#endif 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cienum mvneta_tx_buf_type { 6078c2ecf20Sopenharmony_ci MVNETA_TYPE_SKB, 6088c2ecf20Sopenharmony_ci MVNETA_TYPE_XDP_TX, 6098c2ecf20Sopenharmony_ci MVNETA_TYPE_XDP_NDO, 6108c2ecf20Sopenharmony_ci}; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistruct mvneta_tx_buf { 6138c2ecf20Sopenharmony_ci enum mvneta_tx_buf_type type; 6148c2ecf20Sopenharmony_ci union { 6158c2ecf20Sopenharmony_ci struct xdp_frame *xdpf; 6168c2ecf20Sopenharmony_ci struct sk_buff *skb; 6178c2ecf20Sopenharmony_ci }; 6188c2ecf20Sopenharmony_ci}; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistruct mvneta_tx_queue { 6218c2ecf20Sopenharmony_ci /* Number of this TX queue, in the range 0-7 */ 6228c2ecf20Sopenharmony_ci u8 id; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* Number of TX DMA descriptors in the descriptor ring */ 6258c2ecf20Sopenharmony_ci int size; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* Number of currently used TX DMA descriptor in the 6288c2ecf20Sopenharmony_ci * descriptor ring 6298c2ecf20Sopenharmony_ci */ 6308c2ecf20Sopenharmony_ci int count; 6318c2ecf20Sopenharmony_ci int pending; 6328c2ecf20Sopenharmony_ci int tx_stop_threshold; 6338c2ecf20Sopenharmony_ci int tx_wake_threshold; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* Array of transmitted buffers */ 6368c2ecf20Sopenharmony_ci struct mvneta_tx_buf *buf; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci /* Index of last TX DMA descriptor that was inserted */ 6398c2ecf20Sopenharmony_ci int txq_put_index; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* Index of the TX DMA descriptor to be cleaned up */ 6428c2ecf20Sopenharmony_ci int txq_get_index; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci u32 done_pkts_coal; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* Virtual address of the TX DMA descriptors array */ 6478c2ecf20Sopenharmony_ci struct mvneta_tx_desc *descs; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* DMA address of the TX DMA descriptors array */ 6508c2ecf20Sopenharmony_ci dma_addr_t descs_phys; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* Index of the last TX DMA descriptor */ 6538c2ecf20Sopenharmony_ci int last_desc; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* Index of the next TX DMA descriptor to process */ 6568c2ecf20Sopenharmony_ci int next_desc_to_proc; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* DMA buffers for TSO headers */ 6598c2ecf20Sopenharmony_ci char *tso_hdrs; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* DMA address of TSO headers */ 6628c2ecf20Sopenharmony_ci dma_addr_t tso_hdrs_phys; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci /* Affinity mask for CPUs*/ 6658c2ecf20Sopenharmony_ci cpumask_t affinity_mask; 6668c2ecf20Sopenharmony_ci}; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistruct mvneta_rx_queue { 6698c2ecf20Sopenharmony_ci /* rx queue number, in the range 0-7 */ 6708c2ecf20Sopenharmony_ci u8 id; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* num of rx descriptors in the rx descriptor ring */ 6738c2ecf20Sopenharmony_ci int size; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci u32 pkts_coal; 6768c2ecf20Sopenharmony_ci u32 time_coal; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* page_pool */ 6798c2ecf20Sopenharmony_ci struct page_pool *page_pool; 6808c2ecf20Sopenharmony_ci struct xdp_rxq_info xdp_rxq; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* Virtual address of the RX buffer */ 6838c2ecf20Sopenharmony_ci void **buf_virt_addr; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* Virtual address of the RX DMA descriptors array */ 6868c2ecf20Sopenharmony_ci struct mvneta_rx_desc *descs; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* DMA address of the RX DMA descriptors array */ 6898c2ecf20Sopenharmony_ci dma_addr_t descs_phys; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* Index of the last RX DMA descriptor */ 6928c2ecf20Sopenharmony_ci int last_desc; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* Index of the next RX DMA descriptor to process */ 6958c2ecf20Sopenharmony_ci int next_desc_to_proc; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* Index of first RX DMA descriptor to refill */ 6988c2ecf20Sopenharmony_ci int first_to_refill; 6998c2ecf20Sopenharmony_ci u32 refill_num; 7008c2ecf20Sopenharmony_ci}; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic enum cpuhp_state online_hpstate; 7038c2ecf20Sopenharmony_ci/* The hardware supports eight (8) rx queues, but we are only allowing 7048c2ecf20Sopenharmony_ci * the first one to be used. Therefore, let's just allocate one queue. 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_cistatic int rxq_number = 8; 7078c2ecf20Sopenharmony_cistatic int txq_number = 8; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic int rxq_def; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic int rx_copybreak __read_mostly = 256; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci/* HW BM need that each port be identify by a unique ID */ 7148c2ecf20Sopenharmony_cistatic int global_port_id; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci#define MVNETA_DRIVER_NAME "mvneta" 7178c2ecf20Sopenharmony_ci#define MVNETA_DRIVER_VERSION "1.0" 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci/* Utility/helper methods */ 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci/* Write helper method */ 7228c2ecf20Sopenharmony_cistatic void mvreg_write(struct mvneta_port *pp, u32 offset, u32 data) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci writel(data, pp->base + offset); 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci/* Read helper method */ 7288c2ecf20Sopenharmony_cistatic u32 mvreg_read(struct mvneta_port *pp, u32 offset) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci return readl(pp->base + offset); 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci/* Increment txq get counter */ 7348c2ecf20Sopenharmony_cistatic void mvneta_txq_inc_get(struct mvneta_tx_queue *txq) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci txq->txq_get_index++; 7378c2ecf20Sopenharmony_ci if (txq->txq_get_index == txq->size) 7388c2ecf20Sopenharmony_ci txq->txq_get_index = 0; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci/* Increment txq put counter */ 7428c2ecf20Sopenharmony_cistatic void mvneta_txq_inc_put(struct mvneta_tx_queue *txq) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci txq->txq_put_index++; 7458c2ecf20Sopenharmony_ci if (txq->txq_put_index == txq->size) 7468c2ecf20Sopenharmony_ci txq->txq_put_index = 0; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci/* Clear all MIB counters */ 7518c2ecf20Sopenharmony_cistatic void mvneta_mib_counters_clear(struct mvneta_port *pp) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci int i; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* Perform dummy reads from MIB counters */ 7568c2ecf20Sopenharmony_ci for (i = 0; i < MVNETA_MIB_LATE_COLLISION; i += 4) 7578c2ecf20Sopenharmony_ci mvreg_read(pp, (MVNETA_MIB_COUNTERS_BASE + i)); 7588c2ecf20Sopenharmony_ci mvreg_read(pp, MVNETA_RX_DISCARD_FRAME_COUNT); 7598c2ecf20Sopenharmony_ci mvreg_read(pp, MVNETA_OVERRUN_FRAME_COUNT); 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci/* Get System Network Statistics */ 7638c2ecf20Sopenharmony_cistatic void 7648c2ecf20Sopenharmony_cimvneta_get_stats64(struct net_device *dev, 7658c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *stats) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 7688c2ecf20Sopenharmony_ci unsigned int start; 7698c2ecf20Sopenharmony_ci int cpu; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 7728c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats *cpu_stats; 7738c2ecf20Sopenharmony_ci u64 rx_packets; 7748c2ecf20Sopenharmony_ci u64 rx_bytes; 7758c2ecf20Sopenharmony_ci u64 rx_dropped; 7768c2ecf20Sopenharmony_ci u64 rx_errors; 7778c2ecf20Sopenharmony_ci u64 tx_packets; 7788c2ecf20Sopenharmony_ci u64 tx_bytes; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci cpu_stats = per_cpu_ptr(pp->stats, cpu); 7818c2ecf20Sopenharmony_ci do { 7828c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); 7838c2ecf20Sopenharmony_ci rx_packets = cpu_stats->es.ps.rx_packets; 7848c2ecf20Sopenharmony_ci rx_bytes = cpu_stats->es.ps.rx_bytes; 7858c2ecf20Sopenharmony_ci rx_dropped = cpu_stats->rx_dropped; 7868c2ecf20Sopenharmony_ci rx_errors = cpu_stats->rx_errors; 7878c2ecf20Sopenharmony_ci tx_packets = cpu_stats->es.ps.tx_packets; 7888c2ecf20Sopenharmony_ci tx_bytes = cpu_stats->es.ps.tx_bytes; 7898c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci stats->rx_packets += rx_packets; 7928c2ecf20Sopenharmony_ci stats->rx_bytes += rx_bytes; 7938c2ecf20Sopenharmony_ci stats->rx_dropped += rx_dropped; 7948c2ecf20Sopenharmony_ci stats->rx_errors += rx_errors; 7958c2ecf20Sopenharmony_ci stats->tx_packets += tx_packets; 7968c2ecf20Sopenharmony_ci stats->tx_bytes += tx_bytes; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci stats->tx_dropped = dev->stats.tx_dropped; 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci/* Rx descriptors helper methods */ 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci/* Checks whether the RX descriptor having this status is both the first 8058c2ecf20Sopenharmony_ci * and the last descriptor for the RX packet. Each RX packet is currently 8068c2ecf20Sopenharmony_ci * received through a single RX descriptor, so not having each RX 8078c2ecf20Sopenharmony_ci * descriptor with its first and last bits set is an error 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_cistatic int mvneta_rxq_desc_is_first_last(u32 status) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci return (status & MVNETA_RXD_FIRST_LAST_DESC) == 8128c2ecf20Sopenharmony_ci MVNETA_RXD_FIRST_LAST_DESC; 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci/* Add number of descriptors ready to receive new packets */ 8168c2ecf20Sopenharmony_cistatic void mvneta_rxq_non_occup_desc_add(struct mvneta_port *pp, 8178c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq, 8188c2ecf20Sopenharmony_ci int ndescs) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci /* Only MVNETA_RXQ_ADD_NON_OCCUPIED_MAX (255) descriptors can 8218c2ecf20Sopenharmony_ci * be added at once 8228c2ecf20Sopenharmony_ci */ 8238c2ecf20Sopenharmony_ci while (ndescs > MVNETA_RXQ_ADD_NON_OCCUPIED_MAX) { 8248c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), 8258c2ecf20Sopenharmony_ci (MVNETA_RXQ_ADD_NON_OCCUPIED_MAX << 8268c2ecf20Sopenharmony_ci MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT)); 8278c2ecf20Sopenharmony_ci ndescs -= MVNETA_RXQ_ADD_NON_OCCUPIED_MAX; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), 8318c2ecf20Sopenharmony_ci (ndescs << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT)); 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci/* Get number of RX descriptors occupied by received packets */ 8358c2ecf20Sopenharmony_cistatic int mvneta_rxq_busy_desc_num_get(struct mvneta_port *pp, 8368c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci u32 val; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_RXQ_STATUS_REG(rxq->id)); 8418c2ecf20Sopenharmony_ci return val & MVNETA_RXQ_OCCUPIED_ALL_MASK; 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci/* Update num of rx desc called upon return from rx path or 8458c2ecf20Sopenharmony_ci * from mvneta_rxq_drop_pkts(). 8468c2ecf20Sopenharmony_ci */ 8478c2ecf20Sopenharmony_cistatic void mvneta_rxq_desc_num_update(struct mvneta_port *pp, 8488c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq, 8498c2ecf20Sopenharmony_ci int rx_done, int rx_filled) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci u32 val; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if ((rx_done <= 0xff) && (rx_filled <= 0xff)) { 8548c2ecf20Sopenharmony_ci val = rx_done | 8558c2ecf20Sopenharmony_ci (rx_filled << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT); 8568c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), val); 8578c2ecf20Sopenharmony_ci return; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci /* Only 255 descriptors can be added at once */ 8618c2ecf20Sopenharmony_ci while ((rx_done > 0) || (rx_filled > 0)) { 8628c2ecf20Sopenharmony_ci if (rx_done <= 0xff) { 8638c2ecf20Sopenharmony_ci val = rx_done; 8648c2ecf20Sopenharmony_ci rx_done = 0; 8658c2ecf20Sopenharmony_ci } else { 8668c2ecf20Sopenharmony_ci val = 0xff; 8678c2ecf20Sopenharmony_ci rx_done -= 0xff; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci if (rx_filled <= 0xff) { 8708c2ecf20Sopenharmony_ci val |= rx_filled << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT; 8718c2ecf20Sopenharmony_ci rx_filled = 0; 8728c2ecf20Sopenharmony_ci } else { 8738c2ecf20Sopenharmony_ci val |= 0xff << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT; 8748c2ecf20Sopenharmony_ci rx_filled -= 0xff; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), val); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci/* Get pointer to next RX descriptor to be processed by SW */ 8818c2ecf20Sopenharmony_cistatic struct mvneta_rx_desc * 8828c2ecf20Sopenharmony_cimvneta_rxq_next_desc_get(struct mvneta_rx_queue *rxq) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci int rx_desc = rxq->next_desc_to_proc; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci rxq->next_desc_to_proc = MVNETA_QUEUE_NEXT_DESC(rxq, rx_desc); 8878c2ecf20Sopenharmony_ci prefetch(rxq->descs + rxq->next_desc_to_proc); 8888c2ecf20Sopenharmony_ci return rxq->descs + rx_desc; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci/* Change maximum receive size of the port. */ 8928c2ecf20Sopenharmony_cistatic void mvneta_max_rx_size_set(struct mvneta_port *pp, int max_rx_size) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci u32 val; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); 8978c2ecf20Sopenharmony_ci val &= ~MVNETA_GMAC_MAX_RX_SIZE_MASK; 8988c2ecf20Sopenharmony_ci val |= ((max_rx_size - MVNETA_MH_SIZE) / 2) << 8998c2ecf20Sopenharmony_ci MVNETA_GMAC_MAX_RX_SIZE_SHIFT; 9008c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci/* Set rx queue offset */ 9058c2ecf20Sopenharmony_cistatic void mvneta_rxq_offset_set(struct mvneta_port *pp, 9068c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq, 9078c2ecf20Sopenharmony_ci int offset) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci u32 val; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id)); 9128c2ecf20Sopenharmony_ci val &= ~MVNETA_RXQ_PKT_OFFSET_ALL_MASK; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci /* Offset is in */ 9158c2ecf20Sopenharmony_ci val |= MVNETA_RXQ_PKT_OFFSET_MASK(offset >> 3); 9168c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val); 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/* Tx descriptors helper methods */ 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci/* Update HW with number of TX descriptors to be sent */ 9238c2ecf20Sopenharmony_cistatic void mvneta_txq_pend_desc_add(struct mvneta_port *pp, 9248c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq, 9258c2ecf20Sopenharmony_ci int pend_desc) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci u32 val; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci pend_desc += txq->pending; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* Only 255 Tx descriptors can be added at once */ 9328c2ecf20Sopenharmony_ci do { 9338c2ecf20Sopenharmony_ci val = min(pend_desc, 255); 9348c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val); 9358c2ecf20Sopenharmony_ci pend_desc -= val; 9368c2ecf20Sopenharmony_ci } while (pend_desc > 0); 9378c2ecf20Sopenharmony_ci txq->pending = 0; 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci/* Get pointer to next TX descriptor to be processed (send) by HW */ 9418c2ecf20Sopenharmony_cistatic struct mvneta_tx_desc * 9428c2ecf20Sopenharmony_cimvneta_txq_next_desc_get(struct mvneta_tx_queue *txq) 9438c2ecf20Sopenharmony_ci{ 9448c2ecf20Sopenharmony_ci int tx_desc = txq->next_desc_to_proc; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci txq->next_desc_to_proc = MVNETA_QUEUE_NEXT_DESC(txq, tx_desc); 9478c2ecf20Sopenharmony_ci return txq->descs + tx_desc; 9488c2ecf20Sopenharmony_ci} 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci/* Release the last allocated TX descriptor. Useful to handle DMA 9518c2ecf20Sopenharmony_ci * mapping failures in the TX path. 9528c2ecf20Sopenharmony_ci */ 9538c2ecf20Sopenharmony_cistatic void mvneta_txq_desc_put(struct mvneta_tx_queue *txq) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci if (txq->next_desc_to_proc == 0) 9568c2ecf20Sopenharmony_ci txq->next_desc_to_proc = txq->last_desc - 1; 9578c2ecf20Sopenharmony_ci else 9588c2ecf20Sopenharmony_ci txq->next_desc_to_proc--; 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci/* Set rxq buf size */ 9628c2ecf20Sopenharmony_cistatic void mvneta_rxq_buf_size_set(struct mvneta_port *pp, 9638c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq, 9648c2ecf20Sopenharmony_ci int buf_size) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci u32 val; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_RXQ_SIZE_REG(rxq->id)); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci val &= ~MVNETA_RXQ_BUF_SIZE_MASK; 9718c2ecf20Sopenharmony_ci val |= ((buf_size >> 3) << MVNETA_RXQ_BUF_SIZE_SHIFT); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), val); 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci/* Disable buffer management (BM) */ 9778c2ecf20Sopenharmony_cistatic void mvneta_rxq_bm_disable(struct mvneta_port *pp, 9788c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci u32 val; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id)); 9838c2ecf20Sopenharmony_ci val &= ~MVNETA_RXQ_HW_BUF_ALLOC; 9848c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val); 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci/* Enable buffer management (BM) */ 9888c2ecf20Sopenharmony_cistatic void mvneta_rxq_bm_enable(struct mvneta_port *pp, 9898c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci u32 val; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id)); 9948c2ecf20Sopenharmony_ci val |= MVNETA_RXQ_HW_BUF_ALLOC; 9958c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val); 9968c2ecf20Sopenharmony_ci} 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci/* Notify HW about port's assignment of pool for bigger packets */ 9998c2ecf20Sopenharmony_cistatic void mvneta_rxq_long_pool_set(struct mvneta_port *pp, 10008c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci u32 val; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id)); 10058c2ecf20Sopenharmony_ci val &= ~MVNETA_RXQ_LONG_POOL_ID_MASK; 10068c2ecf20Sopenharmony_ci val |= (pp->pool_long->id << MVNETA_RXQ_LONG_POOL_ID_SHIFT); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val); 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci/* Notify HW about port's assignment of pool for smaller packets */ 10128c2ecf20Sopenharmony_cistatic void mvneta_rxq_short_pool_set(struct mvneta_port *pp, 10138c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci u32 val; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id)); 10188c2ecf20Sopenharmony_ci val &= ~MVNETA_RXQ_SHORT_POOL_ID_MASK; 10198c2ecf20Sopenharmony_ci val |= (pp->pool_short->id << MVNETA_RXQ_SHORT_POOL_ID_SHIFT); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val); 10228c2ecf20Sopenharmony_ci} 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci/* Set port's receive buffer size for assigned BM pool */ 10258c2ecf20Sopenharmony_cistatic inline void mvneta_bm_pool_bufsize_set(struct mvneta_port *pp, 10268c2ecf20Sopenharmony_ci int buf_size, 10278c2ecf20Sopenharmony_ci u8 pool_id) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci u32 val; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (!IS_ALIGNED(buf_size, 8)) { 10328c2ecf20Sopenharmony_ci dev_warn(pp->dev->dev.parent, 10338c2ecf20Sopenharmony_ci "illegal buf_size value %d, round to %d\n", 10348c2ecf20Sopenharmony_ci buf_size, ALIGN(buf_size, 8)); 10358c2ecf20Sopenharmony_ci buf_size = ALIGN(buf_size, 8); 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_PORT_POOL_BUFFER_SZ_REG(pool_id)); 10398c2ecf20Sopenharmony_ci val |= buf_size & MVNETA_PORT_POOL_BUFFER_SZ_MASK; 10408c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_POOL_BUFFER_SZ_REG(pool_id), val); 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci/* Configure MBUS window in order to enable access BM internal SRAM */ 10448c2ecf20Sopenharmony_cistatic int mvneta_mbus_io_win_set(struct mvneta_port *pp, u32 base, u32 wsize, 10458c2ecf20Sopenharmony_ci u8 target, u8 attr) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci u32 win_enable, win_protect; 10488c2ecf20Sopenharmony_ci int i; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci win_enable = mvreg_read(pp, MVNETA_BASE_ADDR_ENABLE); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (pp->bm_win_id < 0) { 10538c2ecf20Sopenharmony_ci /* Find first not occupied window */ 10548c2ecf20Sopenharmony_ci for (i = 0; i < MVNETA_MAX_DECODE_WIN; i++) { 10558c2ecf20Sopenharmony_ci if (win_enable & (1 << i)) { 10568c2ecf20Sopenharmony_ci pp->bm_win_id = i; 10578c2ecf20Sopenharmony_ci break; 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci if (i == MVNETA_MAX_DECODE_WIN) 10618c2ecf20Sopenharmony_ci return -ENOMEM; 10628c2ecf20Sopenharmony_ci } else { 10638c2ecf20Sopenharmony_ci i = pp->bm_win_id; 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_WIN_BASE(i), 0); 10678c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_WIN_SIZE(i), 0); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci if (i < 4) 10708c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_WIN_REMAP(i), 0); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_WIN_BASE(i), (base & 0xffff0000) | 10738c2ecf20Sopenharmony_ci (attr << 8) | target); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_WIN_SIZE(i), (wsize - 1) & 0xffff0000); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci win_protect = mvreg_read(pp, MVNETA_ACCESS_PROTECT_ENABLE); 10788c2ecf20Sopenharmony_ci win_protect |= 3 << (2 * i); 10798c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_ACCESS_PROTECT_ENABLE, win_protect); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci win_enable &= ~(1 << i); 10828c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_BASE_ADDR_ENABLE, win_enable); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci return 0; 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic int mvneta_bm_port_mbus_init(struct mvneta_port *pp) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci u32 wsize; 10908c2ecf20Sopenharmony_ci u8 target, attr; 10918c2ecf20Sopenharmony_ci int err; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci /* Get BM window information */ 10948c2ecf20Sopenharmony_ci err = mvebu_mbus_get_io_win_info(pp->bm_priv->bppi_phys_addr, &wsize, 10958c2ecf20Sopenharmony_ci &target, &attr); 10968c2ecf20Sopenharmony_ci if (err < 0) 10978c2ecf20Sopenharmony_ci return err; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci pp->bm_win_id = -1; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* Open NETA -> BM window */ 11028c2ecf20Sopenharmony_ci err = mvneta_mbus_io_win_set(pp, pp->bm_priv->bppi_phys_addr, wsize, 11038c2ecf20Sopenharmony_ci target, attr); 11048c2ecf20Sopenharmony_ci if (err < 0) { 11058c2ecf20Sopenharmony_ci netdev_info(pp->dev, "fail to configure mbus window to BM\n"); 11068c2ecf20Sopenharmony_ci return err; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci return 0; 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci/* Assign and initialize pools for port. In case of fail 11128c2ecf20Sopenharmony_ci * buffer manager will remain disabled for current port. 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_cistatic int mvneta_bm_port_init(struct platform_device *pdev, 11158c2ecf20Sopenharmony_ci struct mvneta_port *pp) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci struct device_node *dn = pdev->dev.of_node; 11188c2ecf20Sopenharmony_ci u32 long_pool_id, short_pool_id; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) { 11218c2ecf20Sopenharmony_ci int ret; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci ret = mvneta_bm_port_mbus_init(pp); 11248c2ecf20Sopenharmony_ci if (ret) 11258c2ecf20Sopenharmony_ci return ret; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (of_property_read_u32(dn, "bm,pool-long", &long_pool_id)) { 11298c2ecf20Sopenharmony_ci netdev_info(pp->dev, "missing long pool id\n"); 11308c2ecf20Sopenharmony_ci return -EINVAL; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci /* Create port's long pool depending on mtu */ 11348c2ecf20Sopenharmony_ci pp->pool_long = mvneta_bm_pool_use(pp->bm_priv, long_pool_id, 11358c2ecf20Sopenharmony_ci MVNETA_BM_LONG, pp->id, 11368c2ecf20Sopenharmony_ci MVNETA_RX_PKT_SIZE(pp->dev->mtu)); 11378c2ecf20Sopenharmony_ci if (!pp->pool_long) { 11388c2ecf20Sopenharmony_ci netdev_info(pp->dev, "fail to obtain long pool for port\n"); 11398c2ecf20Sopenharmony_ci return -ENOMEM; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci pp->pool_long->port_map |= 1 << pp->id; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci mvneta_bm_pool_bufsize_set(pp, pp->pool_long->buf_size, 11458c2ecf20Sopenharmony_ci pp->pool_long->id); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci /* If short pool id is not defined, assume using single pool */ 11488c2ecf20Sopenharmony_ci if (of_property_read_u32(dn, "bm,pool-short", &short_pool_id)) 11498c2ecf20Sopenharmony_ci short_pool_id = long_pool_id; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci /* Create port's short pool */ 11528c2ecf20Sopenharmony_ci pp->pool_short = mvneta_bm_pool_use(pp->bm_priv, short_pool_id, 11538c2ecf20Sopenharmony_ci MVNETA_BM_SHORT, pp->id, 11548c2ecf20Sopenharmony_ci MVNETA_BM_SHORT_PKT_SIZE); 11558c2ecf20Sopenharmony_ci if (!pp->pool_short) { 11568c2ecf20Sopenharmony_ci netdev_info(pp->dev, "fail to obtain short pool for port\n"); 11578c2ecf20Sopenharmony_ci mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_long, 1 << pp->id); 11588c2ecf20Sopenharmony_ci return -ENOMEM; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (short_pool_id != long_pool_id) { 11628c2ecf20Sopenharmony_ci pp->pool_short->port_map |= 1 << pp->id; 11638c2ecf20Sopenharmony_ci mvneta_bm_pool_bufsize_set(pp, pp->pool_short->buf_size, 11648c2ecf20Sopenharmony_ci pp->pool_short->id); 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci return 0; 11688c2ecf20Sopenharmony_ci} 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci/* Update settings of a pool for bigger packets */ 11718c2ecf20Sopenharmony_cistatic void mvneta_bm_update_mtu(struct mvneta_port *pp, int mtu) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci struct mvneta_bm_pool *bm_pool = pp->pool_long; 11748c2ecf20Sopenharmony_ci struct hwbm_pool *hwbm_pool = &bm_pool->hwbm_pool; 11758c2ecf20Sopenharmony_ci int num; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci /* Release all buffers from long pool */ 11788c2ecf20Sopenharmony_ci mvneta_bm_bufs_free(pp->bm_priv, bm_pool, 1 << pp->id); 11798c2ecf20Sopenharmony_ci if (hwbm_pool->buf_num) { 11808c2ecf20Sopenharmony_ci WARN(1, "cannot free all buffers in pool %d\n", 11818c2ecf20Sopenharmony_ci bm_pool->id); 11828c2ecf20Sopenharmony_ci goto bm_mtu_err; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci bm_pool->pkt_size = MVNETA_RX_PKT_SIZE(mtu); 11868c2ecf20Sopenharmony_ci bm_pool->buf_size = MVNETA_RX_BUF_SIZE(bm_pool->pkt_size); 11878c2ecf20Sopenharmony_ci hwbm_pool->frag_size = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + 11888c2ecf20Sopenharmony_ci SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(bm_pool->pkt_size)); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* Fill entire long pool */ 11918c2ecf20Sopenharmony_ci num = hwbm_pool_add(hwbm_pool, hwbm_pool->size); 11928c2ecf20Sopenharmony_ci if (num != hwbm_pool->size) { 11938c2ecf20Sopenharmony_ci WARN(1, "pool %d: %d of %d allocated\n", 11948c2ecf20Sopenharmony_ci bm_pool->id, num, hwbm_pool->size); 11958c2ecf20Sopenharmony_ci goto bm_mtu_err; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci mvneta_bm_pool_bufsize_set(pp, bm_pool->buf_size, bm_pool->id); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci return; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cibm_mtu_err: 12028c2ecf20Sopenharmony_ci mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_long, 1 << pp->id); 12038c2ecf20Sopenharmony_ci mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_short, 1 << pp->id); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci pp->bm_priv = NULL; 12068c2ecf20Sopenharmony_ci pp->rx_offset_correction = MVNETA_SKB_HEADROOM; 12078c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_ACC_MODE, MVNETA_ACC_MODE_EXT1); 12088c2ecf20Sopenharmony_ci netdev_info(pp->dev, "fail to update MTU, fall back to software BM\n"); 12098c2ecf20Sopenharmony_ci} 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci/* Start the Ethernet port RX and TX activity */ 12128c2ecf20Sopenharmony_cistatic void mvneta_port_up(struct mvneta_port *pp) 12138c2ecf20Sopenharmony_ci{ 12148c2ecf20Sopenharmony_ci int queue; 12158c2ecf20Sopenharmony_ci u32 q_map; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci /* Enable all initialized TXs. */ 12188c2ecf20Sopenharmony_ci q_map = 0; 12198c2ecf20Sopenharmony_ci for (queue = 0; queue < txq_number; queue++) { 12208c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq = &pp->txqs[queue]; 12218c2ecf20Sopenharmony_ci if (txq->descs) 12228c2ecf20Sopenharmony_ci q_map |= (1 << queue); 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_CMD, q_map); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci q_map = 0; 12278c2ecf20Sopenharmony_ci /* Enable all initialized RXQs. */ 12288c2ecf20Sopenharmony_ci for (queue = 0; queue < rxq_number; queue++) { 12298c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci if (rxq->descs) 12328c2ecf20Sopenharmony_ci q_map |= (1 << queue); 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_CMD, q_map); 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci/* Stop the Ethernet port activity */ 12388c2ecf20Sopenharmony_cistatic void mvneta_port_down(struct mvneta_port *pp) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci u32 val; 12418c2ecf20Sopenharmony_ci int count; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* Stop Rx port activity. Check port Rx activity. */ 12448c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_RXQ_CMD) & MVNETA_RXQ_ENABLE_MASK; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci /* Issue stop command for active channels only */ 12478c2ecf20Sopenharmony_ci if (val != 0) 12488c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_CMD, 12498c2ecf20Sopenharmony_ci val << MVNETA_RXQ_DISABLE_SHIFT); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci /* Wait for all Rx activity to terminate. */ 12528c2ecf20Sopenharmony_ci count = 0; 12538c2ecf20Sopenharmony_ci do { 12548c2ecf20Sopenharmony_ci if (count++ >= MVNETA_RX_DISABLE_TIMEOUT_MSEC) { 12558c2ecf20Sopenharmony_ci netdev_warn(pp->dev, 12568c2ecf20Sopenharmony_ci "TIMEOUT for RX stopped ! rx_queue_cmd: 0x%08x\n", 12578c2ecf20Sopenharmony_ci val); 12588c2ecf20Sopenharmony_ci break; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci mdelay(1); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_RXQ_CMD); 12638c2ecf20Sopenharmony_ci } while (val & MVNETA_RXQ_ENABLE_MASK); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci /* Stop Tx port activity. Check port Tx activity. Issue stop 12668c2ecf20Sopenharmony_ci * command for active channels only 12678c2ecf20Sopenharmony_ci */ 12688c2ecf20Sopenharmony_ci val = (mvreg_read(pp, MVNETA_TXQ_CMD)) & MVNETA_TXQ_ENABLE_MASK; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci if (val != 0) 12718c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_CMD, 12728c2ecf20Sopenharmony_ci (val << MVNETA_TXQ_DISABLE_SHIFT)); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci /* Wait for all Tx activity to terminate. */ 12758c2ecf20Sopenharmony_ci count = 0; 12768c2ecf20Sopenharmony_ci do { 12778c2ecf20Sopenharmony_ci if (count++ >= MVNETA_TX_DISABLE_TIMEOUT_MSEC) { 12788c2ecf20Sopenharmony_ci netdev_warn(pp->dev, 12798c2ecf20Sopenharmony_ci "TIMEOUT for TX stopped status=0x%08x\n", 12808c2ecf20Sopenharmony_ci val); 12818c2ecf20Sopenharmony_ci break; 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci mdelay(1); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci /* Check TX Command reg that all Txqs are stopped */ 12868c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_TXQ_CMD); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci } while (val & MVNETA_TXQ_ENABLE_MASK); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci /* Double check to verify that TX FIFO is empty */ 12918c2ecf20Sopenharmony_ci count = 0; 12928c2ecf20Sopenharmony_ci do { 12938c2ecf20Sopenharmony_ci if (count++ >= MVNETA_TX_FIFO_EMPTY_TIMEOUT) { 12948c2ecf20Sopenharmony_ci netdev_warn(pp->dev, 12958c2ecf20Sopenharmony_ci "TX FIFO empty timeout status=0x%08x\n", 12968c2ecf20Sopenharmony_ci val); 12978c2ecf20Sopenharmony_ci break; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci mdelay(1); 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_PORT_STATUS); 13028c2ecf20Sopenharmony_ci } while (!(val & MVNETA_TX_FIFO_EMPTY) && 13038c2ecf20Sopenharmony_ci (val & MVNETA_TX_IN_PRGRS)); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci udelay(200); 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci/* Enable the port by setting the port enable bit of the MAC control register */ 13098c2ecf20Sopenharmony_cistatic void mvneta_port_enable(struct mvneta_port *pp) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci u32 val; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci /* Enable port */ 13148c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); 13158c2ecf20Sopenharmony_ci val |= MVNETA_GMAC0_PORT_ENABLE; 13168c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci/* Disable the port and wait for about 200 usec before retuning */ 13208c2ecf20Sopenharmony_cistatic void mvneta_port_disable(struct mvneta_port *pp) 13218c2ecf20Sopenharmony_ci{ 13228c2ecf20Sopenharmony_ci u32 val; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* Reset the Enable bit in the Serial Control Register */ 13258c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); 13268c2ecf20Sopenharmony_ci val &= ~MVNETA_GMAC0_PORT_ENABLE; 13278c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci udelay(200); 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci/* Multicast tables methods */ 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci/* Set all entries in Unicast MAC Table; queue==-1 means reject all */ 13358c2ecf20Sopenharmony_cistatic void mvneta_set_ucast_table(struct mvneta_port *pp, int queue) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci int offset; 13388c2ecf20Sopenharmony_ci u32 val; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci if (queue == -1) { 13418c2ecf20Sopenharmony_ci val = 0; 13428c2ecf20Sopenharmony_ci } else { 13438c2ecf20Sopenharmony_ci val = 0x1 | (queue << 1); 13448c2ecf20Sopenharmony_ci val |= (val << 24) | (val << 16) | (val << 8); 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci for (offset = 0; offset <= 0xc; offset += 4) 13488c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_DA_FILT_UCAST_BASE + offset, val); 13498c2ecf20Sopenharmony_ci} 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci/* Set all entries in Special Multicast MAC Table; queue==-1 means reject all */ 13528c2ecf20Sopenharmony_cistatic void mvneta_set_special_mcast_table(struct mvneta_port *pp, int queue) 13538c2ecf20Sopenharmony_ci{ 13548c2ecf20Sopenharmony_ci int offset; 13558c2ecf20Sopenharmony_ci u32 val; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci if (queue == -1) { 13588c2ecf20Sopenharmony_ci val = 0; 13598c2ecf20Sopenharmony_ci } else { 13608c2ecf20Sopenharmony_ci val = 0x1 | (queue << 1); 13618c2ecf20Sopenharmony_ci val |= (val << 24) | (val << 16) | (val << 8); 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci for (offset = 0; offset <= 0xfc; offset += 4) 13658c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_DA_FILT_SPEC_MCAST + offset, val); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci/* Set all entries in Other Multicast MAC Table. queue==-1 means reject all */ 13708c2ecf20Sopenharmony_cistatic void mvneta_set_other_mcast_table(struct mvneta_port *pp, int queue) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci int offset; 13738c2ecf20Sopenharmony_ci u32 val; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci if (queue == -1) { 13768c2ecf20Sopenharmony_ci memset(pp->mcast_count, 0, sizeof(pp->mcast_count)); 13778c2ecf20Sopenharmony_ci val = 0; 13788c2ecf20Sopenharmony_ci } else { 13798c2ecf20Sopenharmony_ci memset(pp->mcast_count, 1, sizeof(pp->mcast_count)); 13808c2ecf20Sopenharmony_ci val = 0x1 | (queue << 1); 13818c2ecf20Sopenharmony_ci val |= (val << 24) | (val << 16) | (val << 8); 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci for (offset = 0; offset <= 0xfc; offset += 4) 13858c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val); 13868c2ecf20Sopenharmony_ci} 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_cistatic void mvneta_percpu_unmask_interrupt(void *arg) 13898c2ecf20Sopenharmony_ci{ 13908c2ecf20Sopenharmony_ci struct mvneta_port *pp = arg; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci /* All the queue are unmasked, but actually only the ones 13938c2ecf20Sopenharmony_ci * mapped to this CPU will be unmasked 13948c2ecf20Sopenharmony_ci */ 13958c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_NEW_MASK, 13968c2ecf20Sopenharmony_ci MVNETA_RX_INTR_MASK_ALL | 13978c2ecf20Sopenharmony_ci MVNETA_TX_INTR_MASK_ALL | 13988c2ecf20Sopenharmony_ci MVNETA_MISCINTR_INTR_MASK); 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_cistatic void mvneta_percpu_mask_interrupt(void *arg) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci struct mvneta_port *pp = arg; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci /* All the queue are masked, but actually only the ones 14068c2ecf20Sopenharmony_ci * mapped to this CPU will be masked 14078c2ecf20Sopenharmony_ci */ 14088c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); 14098c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0); 14108c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0); 14118c2ecf20Sopenharmony_ci} 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_cistatic void mvneta_percpu_clear_intr_cause(void *arg) 14148c2ecf20Sopenharmony_ci{ 14158c2ecf20Sopenharmony_ci struct mvneta_port *pp = arg; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci /* All the queue are cleared, but actually only the ones 14188c2ecf20Sopenharmony_ci * mapped to this CPU will be cleared 14198c2ecf20Sopenharmony_ci */ 14208c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_NEW_CAUSE, 0); 14218c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0); 14228c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_OLD_CAUSE, 0); 14238c2ecf20Sopenharmony_ci} 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci/* This method sets defaults to the NETA port: 14268c2ecf20Sopenharmony_ci * Clears interrupt Cause and Mask registers. 14278c2ecf20Sopenharmony_ci * Clears all MAC tables. 14288c2ecf20Sopenharmony_ci * Sets defaults to all registers. 14298c2ecf20Sopenharmony_ci * Resets RX and TX descriptor rings. 14308c2ecf20Sopenharmony_ci * Resets PHY. 14318c2ecf20Sopenharmony_ci * This method can be called after mvneta_port_down() to return the port 14328c2ecf20Sopenharmony_ci * settings to defaults. 14338c2ecf20Sopenharmony_ci */ 14348c2ecf20Sopenharmony_cistatic void mvneta_defaults_set(struct mvneta_port *pp) 14358c2ecf20Sopenharmony_ci{ 14368c2ecf20Sopenharmony_ci int cpu; 14378c2ecf20Sopenharmony_ci int queue; 14388c2ecf20Sopenharmony_ci u32 val; 14398c2ecf20Sopenharmony_ci int max_cpu = num_present_cpus(); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci /* Clear all Cause registers */ 14428c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_clear_intr_cause, pp, true); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci /* Mask all interrupts */ 14458c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_mask_interrupt, pp, true); 14468c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_ENABLE, 0); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci /* Enable MBUS Retry bit16 */ 14498c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_MBUS_RETRY, 0x20); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci /* Set CPU queue access map. CPUs are assigned to the RX and 14528c2ecf20Sopenharmony_ci * TX queues modulo their number. If there is only one TX 14538c2ecf20Sopenharmony_ci * queue then it is assigned to the CPU associated to the 14548c2ecf20Sopenharmony_ci * default RX queue. 14558c2ecf20Sopenharmony_ci */ 14568c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { 14578c2ecf20Sopenharmony_ci int rxq_map = 0, txq_map = 0; 14588c2ecf20Sopenharmony_ci int rxq, txq; 14598c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) { 14608c2ecf20Sopenharmony_ci for (rxq = 0; rxq < rxq_number; rxq++) 14618c2ecf20Sopenharmony_ci if ((rxq % max_cpu) == cpu) 14628c2ecf20Sopenharmony_ci rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci for (txq = 0; txq < txq_number; txq++) 14658c2ecf20Sopenharmony_ci if ((txq % max_cpu) == cpu) 14668c2ecf20Sopenharmony_ci txq_map |= MVNETA_CPU_TXQ_ACCESS(txq); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci /* With only one TX queue we configure a special case 14698c2ecf20Sopenharmony_ci * which will allow to get all the irq on a single 14708c2ecf20Sopenharmony_ci * CPU 14718c2ecf20Sopenharmony_ci */ 14728c2ecf20Sopenharmony_ci if (txq_number == 1) 14738c2ecf20Sopenharmony_ci txq_map = (cpu == pp->rxq_def) ? 14748c2ecf20Sopenharmony_ci MVNETA_CPU_TXQ_ACCESS(0) : 0; 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci } else { 14778c2ecf20Sopenharmony_ci txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK; 14788c2ecf20Sopenharmony_ci rxq_map = MVNETA_CPU_RXQ_ACCESS_ALL_MASK; 14798c2ecf20Sopenharmony_ci } 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map); 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci /* Reset RX and TX DMAs */ 14858c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET); 14868c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_TX_RESET, MVNETA_PORT_TX_DMA_RESET); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci /* Disable Legacy WRR, Disable EJP, Release from reset */ 14898c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_CMD_1, 0); 14908c2ecf20Sopenharmony_ci for (queue = 0; queue < txq_number; queue++) { 14918c2ecf20Sopenharmony_ci mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(queue), 0); 14928c2ecf20Sopenharmony_ci mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(queue), 0); 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_TX_RESET, 0); 14968c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_RX_RESET, 0); 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci /* Set Port Acceleration Mode */ 14998c2ecf20Sopenharmony_ci if (pp->bm_priv) 15008c2ecf20Sopenharmony_ci /* HW buffer management + legacy parser */ 15018c2ecf20Sopenharmony_ci val = MVNETA_ACC_MODE_EXT2; 15028c2ecf20Sopenharmony_ci else 15038c2ecf20Sopenharmony_ci /* SW buffer management + legacy parser */ 15048c2ecf20Sopenharmony_ci val = MVNETA_ACC_MODE_EXT1; 15058c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_ACC_MODE, val); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci if (pp->bm_priv) 15088c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_BM_ADDRESS, pp->bm_priv->bppi_phys_addr); 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci /* Update val of portCfg register accordingly with all RxQueue types */ 15118c2ecf20Sopenharmony_ci val = MVNETA_PORT_CONFIG_DEFL_VALUE(pp->rxq_def); 15128c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_CONFIG, val); 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci val = 0; 15158c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_CONFIG_EXTEND, val); 15168c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RX_MIN_FRAME_SIZE, 64); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci /* Build PORT_SDMA_CONFIG_REG */ 15198c2ecf20Sopenharmony_ci val = 0; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci /* Default burst size */ 15228c2ecf20Sopenharmony_ci val |= MVNETA_TX_BRST_SZ_MASK(MVNETA_SDMA_BRST_SIZE_16); 15238c2ecf20Sopenharmony_ci val |= MVNETA_RX_BRST_SZ_MASK(MVNETA_SDMA_BRST_SIZE_16); 15248c2ecf20Sopenharmony_ci val |= MVNETA_RX_NO_DATA_SWAP | MVNETA_TX_NO_DATA_SWAP; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci#if defined(__BIG_ENDIAN) 15278c2ecf20Sopenharmony_ci val |= MVNETA_DESC_SWAP; 15288c2ecf20Sopenharmony_ci#endif 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci /* Assign port SDMA configuration */ 15318c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_SDMA_CONFIG, val); 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci /* Disable PHY polling in hardware, since we're using the 15348c2ecf20Sopenharmony_ci * kernel phylib to do this. 15358c2ecf20Sopenharmony_ci */ 15368c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_UNIT_CONTROL); 15378c2ecf20Sopenharmony_ci val &= ~MVNETA_PHY_POLLING_ENABLE; 15388c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_UNIT_CONTROL, val); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci mvneta_set_ucast_table(pp, -1); 15418c2ecf20Sopenharmony_ci mvneta_set_special_mcast_table(pp, -1); 15428c2ecf20Sopenharmony_ci mvneta_set_other_mcast_table(pp, -1); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci /* Set port interrupt enable register - default enable all */ 15458c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_ENABLE, 15468c2ecf20Sopenharmony_ci (MVNETA_RXQ_INTR_ENABLE_ALL_MASK 15478c2ecf20Sopenharmony_ci | MVNETA_TXQ_INTR_ENABLE_ALL_MASK)); 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci mvneta_mib_counters_clear(pp); 15508c2ecf20Sopenharmony_ci} 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci/* Set max sizes for tx queues */ 15538c2ecf20Sopenharmony_cistatic void mvneta_txq_max_tx_size_set(struct mvneta_port *pp, int max_tx_size) 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci{ 15568c2ecf20Sopenharmony_ci u32 val, size, mtu; 15578c2ecf20Sopenharmony_ci int queue; 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci mtu = max_tx_size * 8; 15608c2ecf20Sopenharmony_ci if (mtu > MVNETA_TX_MTU_MAX) 15618c2ecf20Sopenharmony_ci mtu = MVNETA_TX_MTU_MAX; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci /* Set MTU */ 15648c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_TX_MTU); 15658c2ecf20Sopenharmony_ci val &= ~MVNETA_TX_MTU_MAX; 15668c2ecf20Sopenharmony_ci val |= mtu; 15678c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TX_MTU, val); 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci /* TX token size and all TXQs token size must be larger that MTU */ 15708c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_TX_TOKEN_SIZE); 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci size = val & MVNETA_TX_TOKEN_SIZE_MAX; 15738c2ecf20Sopenharmony_ci if (size < mtu) { 15748c2ecf20Sopenharmony_ci size = mtu; 15758c2ecf20Sopenharmony_ci val &= ~MVNETA_TX_TOKEN_SIZE_MAX; 15768c2ecf20Sopenharmony_ci val |= size; 15778c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TX_TOKEN_SIZE, val); 15788c2ecf20Sopenharmony_ci } 15798c2ecf20Sopenharmony_ci for (queue = 0; queue < txq_number; queue++) { 15808c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_TXQ_TOKEN_SIZE_REG(queue)); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci size = val & MVNETA_TXQ_TOKEN_SIZE_MAX; 15838c2ecf20Sopenharmony_ci if (size < mtu) { 15848c2ecf20Sopenharmony_ci size = mtu; 15858c2ecf20Sopenharmony_ci val &= ~MVNETA_TXQ_TOKEN_SIZE_MAX; 15868c2ecf20Sopenharmony_ci val |= size; 15878c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_TOKEN_SIZE_REG(queue), val); 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci } 15908c2ecf20Sopenharmony_ci} 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci/* Set unicast address */ 15938c2ecf20Sopenharmony_cistatic void mvneta_set_ucast_addr(struct mvneta_port *pp, u8 last_nibble, 15948c2ecf20Sopenharmony_ci int queue) 15958c2ecf20Sopenharmony_ci{ 15968c2ecf20Sopenharmony_ci unsigned int unicast_reg; 15978c2ecf20Sopenharmony_ci unsigned int tbl_offset; 15988c2ecf20Sopenharmony_ci unsigned int reg_offset; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci /* Locate the Unicast table entry */ 16018c2ecf20Sopenharmony_ci last_nibble = (0xf & last_nibble); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci /* offset from unicast tbl base */ 16048c2ecf20Sopenharmony_ci tbl_offset = (last_nibble / 4) * 4; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci /* offset within the above reg */ 16078c2ecf20Sopenharmony_ci reg_offset = last_nibble % 4; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci unicast_reg = mvreg_read(pp, (MVNETA_DA_FILT_UCAST_BASE + tbl_offset)); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci if (queue == -1) { 16128c2ecf20Sopenharmony_ci /* Clear accepts frame bit at specified unicast DA tbl entry */ 16138c2ecf20Sopenharmony_ci unicast_reg &= ~(0xff << (8 * reg_offset)); 16148c2ecf20Sopenharmony_ci } else { 16158c2ecf20Sopenharmony_ci unicast_reg &= ~(0xff << (8 * reg_offset)); 16168c2ecf20Sopenharmony_ci unicast_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset)); 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci mvreg_write(pp, (MVNETA_DA_FILT_UCAST_BASE + tbl_offset), unicast_reg); 16208c2ecf20Sopenharmony_ci} 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci/* Set mac address */ 16238c2ecf20Sopenharmony_cistatic void mvneta_mac_addr_set(struct mvneta_port *pp, unsigned char *addr, 16248c2ecf20Sopenharmony_ci int queue) 16258c2ecf20Sopenharmony_ci{ 16268c2ecf20Sopenharmony_ci unsigned int mac_h; 16278c2ecf20Sopenharmony_ci unsigned int mac_l; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci if (queue != -1) { 16308c2ecf20Sopenharmony_ci mac_l = (addr[4] << 8) | (addr[5]); 16318c2ecf20Sopenharmony_ci mac_h = (addr[0] << 24) | (addr[1] << 16) | 16328c2ecf20Sopenharmony_ci (addr[2] << 8) | (addr[3] << 0); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_MAC_ADDR_LOW, mac_l); 16358c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_MAC_ADDR_HIGH, mac_h); 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci /* Accept frames of this address */ 16398c2ecf20Sopenharmony_ci mvneta_set_ucast_addr(pp, addr[5], queue); 16408c2ecf20Sopenharmony_ci} 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci/* Set the number of packets that will be received before RX interrupt 16438c2ecf20Sopenharmony_ci * will be generated by HW. 16448c2ecf20Sopenharmony_ci */ 16458c2ecf20Sopenharmony_cistatic void mvneta_rx_pkts_coal_set(struct mvneta_port *pp, 16468c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq, u32 value) 16478c2ecf20Sopenharmony_ci{ 16488c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_THRESHOLD_REG(rxq->id), 16498c2ecf20Sopenharmony_ci value | MVNETA_RXQ_NON_OCCUPIED(0)); 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci/* Set the time delay in usec before RX interrupt will be generated by 16538c2ecf20Sopenharmony_ci * HW. 16548c2ecf20Sopenharmony_ci */ 16558c2ecf20Sopenharmony_cistatic void mvneta_rx_time_coal_set(struct mvneta_port *pp, 16568c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq, u32 value) 16578c2ecf20Sopenharmony_ci{ 16588c2ecf20Sopenharmony_ci u32 val; 16598c2ecf20Sopenharmony_ci unsigned long clk_rate; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci clk_rate = clk_get_rate(pp->clk); 16628c2ecf20Sopenharmony_ci val = (clk_rate / 1000000) * value; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_TIME_COAL_REG(rxq->id), val); 16658c2ecf20Sopenharmony_ci} 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci/* Set threshold for TX_DONE pkts coalescing */ 16688c2ecf20Sopenharmony_cistatic void mvneta_tx_done_pkts_coal_set(struct mvneta_port *pp, 16698c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq, u32 value) 16708c2ecf20Sopenharmony_ci{ 16718c2ecf20Sopenharmony_ci u32 val; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_TXQ_SIZE_REG(txq->id)); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci val &= ~MVNETA_TXQ_SENT_THRESH_ALL_MASK; 16768c2ecf20Sopenharmony_ci val |= MVNETA_TXQ_SENT_THRESH_MASK(value); 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), val); 16798c2ecf20Sopenharmony_ci} 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci/* Handle rx descriptor fill by setting buf_cookie and buf_phys_addr */ 16828c2ecf20Sopenharmony_cistatic void mvneta_rx_desc_fill(struct mvneta_rx_desc *rx_desc, 16838c2ecf20Sopenharmony_ci u32 phys_addr, void *virt_addr, 16848c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 16858c2ecf20Sopenharmony_ci{ 16868c2ecf20Sopenharmony_ci int i; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci rx_desc->buf_phys_addr = phys_addr; 16898c2ecf20Sopenharmony_ci i = rx_desc - rxq->descs; 16908c2ecf20Sopenharmony_ci rxq->buf_virt_addr[i] = virt_addr; 16918c2ecf20Sopenharmony_ci} 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci/* Decrement sent descriptors counter */ 16948c2ecf20Sopenharmony_cistatic void mvneta_txq_sent_desc_dec(struct mvneta_port *pp, 16958c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq, 16968c2ecf20Sopenharmony_ci int sent_desc) 16978c2ecf20Sopenharmony_ci{ 16988c2ecf20Sopenharmony_ci u32 val; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci /* Only 255 TX descriptors can be updated at once */ 17018c2ecf20Sopenharmony_ci while (sent_desc > 0xff) { 17028c2ecf20Sopenharmony_ci val = 0xff << MVNETA_TXQ_DEC_SENT_SHIFT; 17038c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val); 17048c2ecf20Sopenharmony_ci sent_desc = sent_desc - 0xff; 17058c2ecf20Sopenharmony_ci } 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci val = sent_desc << MVNETA_TXQ_DEC_SENT_SHIFT; 17088c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val); 17098c2ecf20Sopenharmony_ci} 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci/* Get number of TX descriptors already sent by HW */ 17128c2ecf20Sopenharmony_cistatic int mvneta_txq_sent_desc_num_get(struct mvneta_port *pp, 17138c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 17148c2ecf20Sopenharmony_ci{ 17158c2ecf20Sopenharmony_ci u32 val; 17168c2ecf20Sopenharmony_ci int sent_desc; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_TXQ_STATUS_REG(txq->id)); 17198c2ecf20Sopenharmony_ci sent_desc = (val & MVNETA_TXQ_SENT_DESC_MASK) >> 17208c2ecf20Sopenharmony_ci MVNETA_TXQ_SENT_DESC_SHIFT; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci return sent_desc; 17238c2ecf20Sopenharmony_ci} 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci/* Get number of sent descriptors and decrement counter. 17268c2ecf20Sopenharmony_ci * The number of sent descriptors is returned. 17278c2ecf20Sopenharmony_ci */ 17288c2ecf20Sopenharmony_cistatic int mvneta_txq_sent_desc_proc(struct mvneta_port *pp, 17298c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 17308c2ecf20Sopenharmony_ci{ 17318c2ecf20Sopenharmony_ci int sent_desc; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci /* Get number of sent descriptors */ 17348c2ecf20Sopenharmony_ci sent_desc = mvneta_txq_sent_desc_num_get(pp, txq); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci /* Decrement sent descriptors counter */ 17378c2ecf20Sopenharmony_ci if (sent_desc) 17388c2ecf20Sopenharmony_ci mvneta_txq_sent_desc_dec(pp, txq, sent_desc); 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci return sent_desc; 17418c2ecf20Sopenharmony_ci} 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci/* Set TXQ descriptors fields relevant for CSUM calculation */ 17448c2ecf20Sopenharmony_cistatic u32 mvneta_txq_desc_csum(int l3_offs, int l3_proto, 17458c2ecf20Sopenharmony_ci int ip_hdr_len, int l4_proto) 17468c2ecf20Sopenharmony_ci{ 17478c2ecf20Sopenharmony_ci u32 command; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci /* Fields: L3_offset, IP_hdrlen, L3_type, G_IPv4_chk, 17508c2ecf20Sopenharmony_ci * G_L4_chk, L4_type; required only for checksum 17518c2ecf20Sopenharmony_ci * calculation 17528c2ecf20Sopenharmony_ci */ 17538c2ecf20Sopenharmony_ci command = l3_offs << MVNETA_TX_L3_OFF_SHIFT; 17548c2ecf20Sopenharmony_ci command |= ip_hdr_len << MVNETA_TX_IP_HLEN_SHIFT; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci if (l3_proto == htons(ETH_P_IP)) 17578c2ecf20Sopenharmony_ci command |= MVNETA_TXD_IP_CSUM; 17588c2ecf20Sopenharmony_ci else 17598c2ecf20Sopenharmony_ci command |= MVNETA_TX_L3_IP6; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci if (l4_proto == IPPROTO_TCP) 17628c2ecf20Sopenharmony_ci command |= MVNETA_TX_L4_CSUM_FULL; 17638c2ecf20Sopenharmony_ci else if (l4_proto == IPPROTO_UDP) 17648c2ecf20Sopenharmony_ci command |= MVNETA_TX_L4_UDP | MVNETA_TX_L4_CSUM_FULL; 17658c2ecf20Sopenharmony_ci else 17668c2ecf20Sopenharmony_ci command |= MVNETA_TX_L4_CSUM_NOT; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci return command; 17698c2ecf20Sopenharmony_ci} 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci/* Display more error info */ 17738c2ecf20Sopenharmony_cistatic void mvneta_rx_error(struct mvneta_port *pp, 17748c2ecf20Sopenharmony_ci struct mvneta_rx_desc *rx_desc) 17758c2ecf20Sopenharmony_ci{ 17768c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); 17778c2ecf20Sopenharmony_ci u32 status = rx_desc->status; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci /* update per-cpu counter */ 17808c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 17818c2ecf20Sopenharmony_ci stats->rx_errors++; 17828c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci switch (status & MVNETA_RXD_ERR_CODE_MASK) { 17858c2ecf20Sopenharmony_ci case MVNETA_RXD_ERR_CRC: 17868c2ecf20Sopenharmony_ci netdev_err(pp->dev, "bad rx status %08x (crc error), size=%d\n", 17878c2ecf20Sopenharmony_ci status, rx_desc->data_size); 17888c2ecf20Sopenharmony_ci break; 17898c2ecf20Sopenharmony_ci case MVNETA_RXD_ERR_OVERRUN: 17908c2ecf20Sopenharmony_ci netdev_err(pp->dev, "bad rx status %08x (overrun error), size=%d\n", 17918c2ecf20Sopenharmony_ci status, rx_desc->data_size); 17928c2ecf20Sopenharmony_ci break; 17938c2ecf20Sopenharmony_ci case MVNETA_RXD_ERR_LEN: 17948c2ecf20Sopenharmony_ci netdev_err(pp->dev, "bad rx status %08x (max frame length error), size=%d\n", 17958c2ecf20Sopenharmony_ci status, rx_desc->data_size); 17968c2ecf20Sopenharmony_ci break; 17978c2ecf20Sopenharmony_ci case MVNETA_RXD_ERR_RESOURCE: 17988c2ecf20Sopenharmony_ci netdev_err(pp->dev, "bad rx status %08x (resource error), size=%d\n", 17998c2ecf20Sopenharmony_ci status, rx_desc->data_size); 18008c2ecf20Sopenharmony_ci break; 18018c2ecf20Sopenharmony_ci } 18028c2ecf20Sopenharmony_ci} 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci/* Handle RX checksum offload based on the descriptor's status */ 18058c2ecf20Sopenharmony_cistatic void mvneta_rx_csum(struct mvneta_port *pp, u32 status, 18068c2ecf20Sopenharmony_ci struct sk_buff *skb) 18078c2ecf20Sopenharmony_ci{ 18088c2ecf20Sopenharmony_ci if ((pp->dev->features & NETIF_F_RXCSUM) && 18098c2ecf20Sopenharmony_ci (status & MVNETA_RXD_L3_IP4) && 18108c2ecf20Sopenharmony_ci (status & MVNETA_RXD_L4_CSUM_OK)) { 18118c2ecf20Sopenharmony_ci skb->csum = 0; 18128c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 18138c2ecf20Sopenharmony_ci return; 18148c2ecf20Sopenharmony_ci } 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 18178c2ecf20Sopenharmony_ci} 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci/* Return tx queue pointer (find last set bit) according to <cause> returned 18208c2ecf20Sopenharmony_ci * form tx_done reg. <cause> must not be null. The return value is always a 18218c2ecf20Sopenharmony_ci * valid queue for matching the first one found in <cause>. 18228c2ecf20Sopenharmony_ci */ 18238c2ecf20Sopenharmony_cistatic struct mvneta_tx_queue *mvneta_tx_done_policy(struct mvneta_port *pp, 18248c2ecf20Sopenharmony_ci u32 cause) 18258c2ecf20Sopenharmony_ci{ 18268c2ecf20Sopenharmony_ci int queue = fls(cause) - 1; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci return &pp->txqs[queue]; 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci/* Free tx queue skbuffs */ 18328c2ecf20Sopenharmony_cistatic void mvneta_txq_bufs_free(struct mvneta_port *pp, 18338c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq, int num, 18348c2ecf20Sopenharmony_ci struct netdev_queue *nq, bool napi) 18358c2ecf20Sopenharmony_ci{ 18368c2ecf20Sopenharmony_ci unsigned int bytes_compl = 0, pkts_compl = 0; 18378c2ecf20Sopenharmony_ci int i; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 18408c2ecf20Sopenharmony_ci struct mvneta_tx_buf *buf = &txq->buf[txq->txq_get_index]; 18418c2ecf20Sopenharmony_ci struct mvneta_tx_desc *tx_desc = txq->descs + 18428c2ecf20Sopenharmony_ci txq->txq_get_index; 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci mvneta_txq_inc_get(txq); 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr) && 18478c2ecf20Sopenharmony_ci buf->type != MVNETA_TYPE_XDP_TX) 18488c2ecf20Sopenharmony_ci dma_unmap_single(pp->dev->dev.parent, 18498c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr, 18508c2ecf20Sopenharmony_ci tx_desc->data_size, DMA_TO_DEVICE); 18518c2ecf20Sopenharmony_ci if (buf->type == MVNETA_TYPE_SKB && buf->skb) { 18528c2ecf20Sopenharmony_ci bytes_compl += buf->skb->len; 18538c2ecf20Sopenharmony_ci pkts_compl++; 18548c2ecf20Sopenharmony_ci dev_kfree_skb_any(buf->skb); 18558c2ecf20Sopenharmony_ci } else if (buf->type == MVNETA_TYPE_XDP_TX || 18568c2ecf20Sopenharmony_ci buf->type == MVNETA_TYPE_XDP_NDO) { 18578c2ecf20Sopenharmony_ci if (napi && buf->type == MVNETA_TYPE_XDP_TX) 18588c2ecf20Sopenharmony_ci xdp_return_frame_rx_napi(buf->xdpf); 18598c2ecf20Sopenharmony_ci else 18608c2ecf20Sopenharmony_ci xdp_return_frame(buf->xdpf); 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci netdev_tx_completed_queue(nq, pkts_compl, bytes_compl); 18658c2ecf20Sopenharmony_ci} 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci/* Handle end of transmission */ 18688c2ecf20Sopenharmony_cistatic void mvneta_txq_done(struct mvneta_port *pp, 18698c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 18708c2ecf20Sopenharmony_ci{ 18718c2ecf20Sopenharmony_ci struct netdev_queue *nq = netdev_get_tx_queue(pp->dev, txq->id); 18728c2ecf20Sopenharmony_ci int tx_done; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci tx_done = mvneta_txq_sent_desc_proc(pp, txq); 18758c2ecf20Sopenharmony_ci if (!tx_done) 18768c2ecf20Sopenharmony_ci return; 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci mvneta_txq_bufs_free(pp, txq, tx_done, nq, true); 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci txq->count -= tx_done; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci if (netif_tx_queue_stopped(nq)) { 18838c2ecf20Sopenharmony_ci if (txq->count <= txq->tx_wake_threshold) 18848c2ecf20Sopenharmony_ci netif_tx_wake_queue(nq); 18858c2ecf20Sopenharmony_ci } 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci/* Refill processing for SW buffer management */ 18898c2ecf20Sopenharmony_ci/* Allocate page per descriptor */ 18908c2ecf20Sopenharmony_cistatic int mvneta_rx_refill(struct mvneta_port *pp, 18918c2ecf20Sopenharmony_ci struct mvneta_rx_desc *rx_desc, 18928c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq, 18938c2ecf20Sopenharmony_ci gfp_t gfp_mask) 18948c2ecf20Sopenharmony_ci{ 18958c2ecf20Sopenharmony_ci dma_addr_t phys_addr; 18968c2ecf20Sopenharmony_ci struct page *page; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci page = page_pool_alloc_pages(rxq->page_pool, 18998c2ecf20Sopenharmony_ci gfp_mask | __GFP_NOWARN); 19008c2ecf20Sopenharmony_ci if (!page) 19018c2ecf20Sopenharmony_ci return -ENOMEM; 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci phys_addr = page_pool_get_dma_addr(page) + pp->rx_offset_correction; 19048c2ecf20Sopenharmony_ci mvneta_rx_desc_fill(rx_desc, phys_addr, page, rxq); 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci return 0; 19078c2ecf20Sopenharmony_ci} 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci/* Handle tx checksum */ 19108c2ecf20Sopenharmony_cistatic u32 mvneta_skb_tx_csum(struct mvneta_port *pp, struct sk_buff *skb) 19118c2ecf20Sopenharmony_ci{ 19128c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 19138c2ecf20Sopenharmony_ci int ip_hdr_len = 0; 19148c2ecf20Sopenharmony_ci __be16 l3_proto = vlan_get_protocol(skb); 19158c2ecf20Sopenharmony_ci u8 l4_proto; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci if (l3_proto == htons(ETH_P_IP)) { 19188c2ecf20Sopenharmony_ci struct iphdr *ip4h = ip_hdr(skb); 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci /* Calculate IPv4 checksum and L4 checksum */ 19218c2ecf20Sopenharmony_ci ip_hdr_len = ip4h->ihl; 19228c2ecf20Sopenharmony_ci l4_proto = ip4h->protocol; 19238c2ecf20Sopenharmony_ci } else if (l3_proto == htons(ETH_P_IPV6)) { 19248c2ecf20Sopenharmony_ci struct ipv6hdr *ip6h = ipv6_hdr(skb); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci /* Read l4_protocol from one of IPv6 extra headers */ 19278c2ecf20Sopenharmony_ci if (skb_network_header_len(skb) > 0) 19288c2ecf20Sopenharmony_ci ip_hdr_len = (skb_network_header_len(skb) >> 2); 19298c2ecf20Sopenharmony_ci l4_proto = ip6h->nexthdr; 19308c2ecf20Sopenharmony_ci } else 19318c2ecf20Sopenharmony_ci return MVNETA_TX_L4_CSUM_NOT; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci return mvneta_txq_desc_csum(skb_network_offset(skb), 19348c2ecf20Sopenharmony_ci l3_proto, ip_hdr_len, l4_proto); 19358c2ecf20Sopenharmony_ci } 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci return MVNETA_TX_L4_CSUM_NOT; 19388c2ecf20Sopenharmony_ci} 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci/* Drop packets received by the RXQ and free buffers */ 19418c2ecf20Sopenharmony_cistatic void mvneta_rxq_drop_pkts(struct mvneta_port *pp, 19428c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 19438c2ecf20Sopenharmony_ci{ 19448c2ecf20Sopenharmony_ci int rx_done, i; 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq); 19478c2ecf20Sopenharmony_ci if (rx_done) 19488c2ecf20Sopenharmony_ci mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_done); 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci if (pp->bm_priv) { 19518c2ecf20Sopenharmony_ci for (i = 0; i < rx_done; i++) { 19528c2ecf20Sopenharmony_ci struct mvneta_rx_desc *rx_desc = 19538c2ecf20Sopenharmony_ci mvneta_rxq_next_desc_get(rxq); 19548c2ecf20Sopenharmony_ci u8 pool_id = MVNETA_RX_GET_BM_POOL_ID(rx_desc); 19558c2ecf20Sopenharmony_ci struct mvneta_bm_pool *bm_pool; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci bm_pool = &pp->bm_priv->bm_pools[pool_id]; 19588c2ecf20Sopenharmony_ci /* Return dropped buffer to the pool */ 19598c2ecf20Sopenharmony_ci mvneta_bm_pool_put_bp(pp->bm_priv, bm_pool, 19608c2ecf20Sopenharmony_ci rx_desc->buf_phys_addr); 19618c2ecf20Sopenharmony_ci } 19628c2ecf20Sopenharmony_ci return; 19638c2ecf20Sopenharmony_ci } 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci for (i = 0; i < rxq->size; i++) { 19668c2ecf20Sopenharmony_ci struct mvneta_rx_desc *rx_desc = rxq->descs + i; 19678c2ecf20Sopenharmony_ci void *data = rxq->buf_virt_addr[i]; 19688c2ecf20Sopenharmony_ci if (!data || !(rx_desc->buf_phys_addr)) 19698c2ecf20Sopenharmony_ci continue; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci page_pool_put_full_page(rxq->page_pool, data, false); 19728c2ecf20Sopenharmony_ci } 19738c2ecf20Sopenharmony_ci if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) 19748c2ecf20Sopenharmony_ci xdp_rxq_info_unreg(&rxq->xdp_rxq); 19758c2ecf20Sopenharmony_ci page_pool_destroy(rxq->page_pool); 19768c2ecf20Sopenharmony_ci rxq->page_pool = NULL; 19778c2ecf20Sopenharmony_ci} 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_cistatic void 19808c2ecf20Sopenharmony_cimvneta_update_stats(struct mvneta_port *pp, 19818c2ecf20Sopenharmony_ci struct mvneta_stats *ps) 19828c2ecf20Sopenharmony_ci{ 19838c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 19868c2ecf20Sopenharmony_ci stats->es.ps.rx_packets += ps->rx_packets; 19878c2ecf20Sopenharmony_ci stats->es.ps.rx_bytes += ps->rx_bytes; 19888c2ecf20Sopenharmony_ci /* xdp */ 19898c2ecf20Sopenharmony_ci stats->es.ps.xdp_redirect += ps->xdp_redirect; 19908c2ecf20Sopenharmony_ci stats->es.ps.xdp_pass += ps->xdp_pass; 19918c2ecf20Sopenharmony_ci stats->es.ps.xdp_drop += ps->xdp_drop; 19928c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 19938c2ecf20Sopenharmony_ci} 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_cistatic inline 19968c2ecf20Sopenharmony_ciint mvneta_rx_refill_queue(struct mvneta_port *pp, struct mvneta_rx_queue *rxq) 19978c2ecf20Sopenharmony_ci{ 19988c2ecf20Sopenharmony_ci struct mvneta_rx_desc *rx_desc; 19998c2ecf20Sopenharmony_ci int curr_desc = rxq->first_to_refill; 20008c2ecf20Sopenharmony_ci int i; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci for (i = 0; (i < rxq->refill_num) && (i < 64); i++) { 20038c2ecf20Sopenharmony_ci rx_desc = rxq->descs + curr_desc; 20048c2ecf20Sopenharmony_ci if (!(rx_desc->buf_phys_addr)) { 20058c2ecf20Sopenharmony_ci if (mvneta_rx_refill(pp, rx_desc, rxq, GFP_ATOMIC)) { 20068c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats *stats; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci pr_err("Can't refill queue %d. Done %d from %d\n", 20098c2ecf20Sopenharmony_ci rxq->id, i, rxq->refill_num); 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci stats = this_cpu_ptr(pp->stats); 20128c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 20138c2ecf20Sopenharmony_ci stats->es.refill_error++; 20148c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 20158c2ecf20Sopenharmony_ci break; 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci } 20188c2ecf20Sopenharmony_ci curr_desc = MVNETA_QUEUE_NEXT_DESC(rxq, curr_desc); 20198c2ecf20Sopenharmony_ci } 20208c2ecf20Sopenharmony_ci rxq->refill_num -= i; 20218c2ecf20Sopenharmony_ci rxq->first_to_refill = curr_desc; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci return i; 20248c2ecf20Sopenharmony_ci} 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_cistatic void 20278c2ecf20Sopenharmony_cimvneta_xdp_put_buff(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, 20288c2ecf20Sopenharmony_ci struct xdp_buff *xdp, int sync_len, bool napi) 20298c2ecf20Sopenharmony_ci{ 20308c2ecf20Sopenharmony_ci struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); 20318c2ecf20Sopenharmony_ci int i; 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci for (i = 0; i < sinfo->nr_frags; i++) 20348c2ecf20Sopenharmony_ci page_pool_put_full_page(rxq->page_pool, 20358c2ecf20Sopenharmony_ci skb_frag_page(&sinfo->frags[i]), napi); 20368c2ecf20Sopenharmony_ci page_pool_put_page(rxq->page_pool, virt_to_head_page(xdp->data), 20378c2ecf20Sopenharmony_ci sync_len, napi); 20388c2ecf20Sopenharmony_ci} 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_cistatic int 20418c2ecf20Sopenharmony_cimvneta_xdp_submit_frame(struct mvneta_port *pp, struct mvneta_tx_queue *txq, 20428c2ecf20Sopenharmony_ci struct xdp_frame *xdpf, bool dma_map) 20438c2ecf20Sopenharmony_ci{ 20448c2ecf20Sopenharmony_ci struct mvneta_tx_desc *tx_desc; 20458c2ecf20Sopenharmony_ci struct mvneta_tx_buf *buf; 20468c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci if (txq->count >= txq->tx_stop_threshold) 20498c2ecf20Sopenharmony_ci return MVNETA_XDP_DROPPED; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci tx_desc = mvneta_txq_next_desc_get(txq); 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci buf = &txq->buf[txq->txq_put_index]; 20548c2ecf20Sopenharmony_ci if (dma_map) { 20558c2ecf20Sopenharmony_ci /* ndo_xdp_xmit */ 20568c2ecf20Sopenharmony_ci dma_addr = dma_map_single(pp->dev->dev.parent, xdpf->data, 20578c2ecf20Sopenharmony_ci xdpf->len, DMA_TO_DEVICE); 20588c2ecf20Sopenharmony_ci if (dma_mapping_error(pp->dev->dev.parent, dma_addr)) { 20598c2ecf20Sopenharmony_ci mvneta_txq_desc_put(txq); 20608c2ecf20Sopenharmony_ci return MVNETA_XDP_DROPPED; 20618c2ecf20Sopenharmony_ci } 20628c2ecf20Sopenharmony_ci buf->type = MVNETA_TYPE_XDP_NDO; 20638c2ecf20Sopenharmony_ci } else { 20648c2ecf20Sopenharmony_ci struct page *page = virt_to_page(xdpf->data); 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci dma_addr = page_pool_get_dma_addr(page) + 20678c2ecf20Sopenharmony_ci sizeof(*xdpf) + xdpf->headroom; 20688c2ecf20Sopenharmony_ci dma_sync_single_for_device(pp->dev->dev.parent, dma_addr, 20698c2ecf20Sopenharmony_ci xdpf->len, DMA_BIDIRECTIONAL); 20708c2ecf20Sopenharmony_ci buf->type = MVNETA_TYPE_XDP_TX; 20718c2ecf20Sopenharmony_ci } 20728c2ecf20Sopenharmony_ci buf->xdpf = xdpf; 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci tx_desc->command = MVNETA_TXD_FLZ_DESC; 20758c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr = dma_addr; 20768c2ecf20Sopenharmony_ci tx_desc->data_size = xdpf->len; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci mvneta_txq_inc_put(txq); 20798c2ecf20Sopenharmony_ci txq->pending++; 20808c2ecf20Sopenharmony_ci txq->count++; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci return MVNETA_XDP_TX; 20838c2ecf20Sopenharmony_ci} 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_cistatic int 20868c2ecf20Sopenharmony_cimvneta_xdp_xmit_back(struct mvneta_port *pp, struct xdp_buff *xdp) 20878c2ecf20Sopenharmony_ci{ 20888c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); 20898c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq; 20908c2ecf20Sopenharmony_ci struct netdev_queue *nq; 20918c2ecf20Sopenharmony_ci struct xdp_frame *xdpf; 20928c2ecf20Sopenharmony_ci int cpu; 20938c2ecf20Sopenharmony_ci u32 ret; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci xdpf = xdp_convert_buff_to_frame(xdp); 20968c2ecf20Sopenharmony_ci if (unlikely(!xdpf)) 20978c2ecf20Sopenharmony_ci return MVNETA_XDP_DROPPED; 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci cpu = smp_processor_id(); 21008c2ecf20Sopenharmony_ci txq = &pp->txqs[cpu % txq_number]; 21018c2ecf20Sopenharmony_ci nq = netdev_get_tx_queue(pp->dev, txq->id); 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci __netif_tx_lock(nq, cpu); 21048c2ecf20Sopenharmony_ci ret = mvneta_xdp_submit_frame(pp, txq, xdpf, false); 21058c2ecf20Sopenharmony_ci if (ret == MVNETA_XDP_TX) { 21068c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 21078c2ecf20Sopenharmony_ci stats->es.ps.tx_bytes += xdpf->len; 21088c2ecf20Sopenharmony_ci stats->es.ps.tx_packets++; 21098c2ecf20Sopenharmony_ci stats->es.ps.xdp_tx++; 21108c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci mvneta_txq_pend_desc_add(pp, txq, 0); 21138c2ecf20Sopenharmony_ci } else { 21148c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 21158c2ecf20Sopenharmony_ci stats->es.ps.xdp_tx_err++; 21168c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 21178c2ecf20Sopenharmony_ci } 21188c2ecf20Sopenharmony_ci __netif_tx_unlock(nq); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci return ret; 21218c2ecf20Sopenharmony_ci} 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_cistatic int 21248c2ecf20Sopenharmony_cimvneta_xdp_xmit(struct net_device *dev, int num_frame, 21258c2ecf20Sopenharmony_ci struct xdp_frame **frames, u32 flags) 21268c2ecf20Sopenharmony_ci{ 21278c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 21288c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); 21298c2ecf20Sopenharmony_ci int i, nxmit_byte = 0, nxmit = num_frame; 21308c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 21318c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq; 21328c2ecf20Sopenharmony_ci struct netdev_queue *nq; 21338c2ecf20Sopenharmony_ci u32 ret; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci if (unlikely(test_bit(__MVNETA_DOWN, &pp->state))) 21368c2ecf20Sopenharmony_ci return -ENETDOWN; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) 21398c2ecf20Sopenharmony_ci return -EINVAL; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci txq = &pp->txqs[cpu % txq_number]; 21428c2ecf20Sopenharmony_ci nq = netdev_get_tx_queue(pp->dev, txq->id); 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci __netif_tx_lock(nq, cpu); 21458c2ecf20Sopenharmony_ci for (i = 0; i < num_frame; i++) { 21468c2ecf20Sopenharmony_ci ret = mvneta_xdp_submit_frame(pp, txq, frames[i], true); 21478c2ecf20Sopenharmony_ci if (ret == MVNETA_XDP_TX) { 21488c2ecf20Sopenharmony_ci nxmit_byte += frames[i]->len; 21498c2ecf20Sopenharmony_ci } else { 21508c2ecf20Sopenharmony_ci xdp_return_frame_rx_napi(frames[i]); 21518c2ecf20Sopenharmony_ci nxmit--; 21528c2ecf20Sopenharmony_ci } 21538c2ecf20Sopenharmony_ci } 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci if (unlikely(flags & XDP_XMIT_FLUSH)) 21568c2ecf20Sopenharmony_ci mvneta_txq_pend_desc_add(pp, txq, 0); 21578c2ecf20Sopenharmony_ci __netif_tx_unlock(nq); 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 21608c2ecf20Sopenharmony_ci stats->es.ps.tx_bytes += nxmit_byte; 21618c2ecf20Sopenharmony_ci stats->es.ps.tx_packets += nxmit; 21628c2ecf20Sopenharmony_ci stats->es.ps.xdp_xmit += nxmit; 21638c2ecf20Sopenharmony_ci stats->es.ps.xdp_xmit_err += num_frame - nxmit; 21648c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci return nxmit; 21678c2ecf20Sopenharmony_ci} 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_cistatic int 21708c2ecf20Sopenharmony_cimvneta_run_xdp(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, 21718c2ecf20Sopenharmony_ci struct bpf_prog *prog, struct xdp_buff *xdp, 21728c2ecf20Sopenharmony_ci u32 frame_sz, struct mvneta_stats *stats) 21738c2ecf20Sopenharmony_ci{ 21748c2ecf20Sopenharmony_ci unsigned int len, data_len, sync; 21758c2ecf20Sopenharmony_ci u32 ret, act; 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci len = xdp->data_end - xdp->data_hard_start - pp->rx_offset_correction; 21788c2ecf20Sopenharmony_ci data_len = xdp->data_end - xdp->data; 21798c2ecf20Sopenharmony_ci act = bpf_prog_run_xdp(prog, xdp); 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci /* Due xdp_adjust_tail: DMA sync for_device cover max len CPU touch */ 21828c2ecf20Sopenharmony_ci sync = xdp->data_end - xdp->data_hard_start - pp->rx_offset_correction; 21838c2ecf20Sopenharmony_ci sync = max(sync, len); 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci switch (act) { 21868c2ecf20Sopenharmony_ci case XDP_PASS: 21878c2ecf20Sopenharmony_ci stats->xdp_pass++; 21888c2ecf20Sopenharmony_ci return MVNETA_XDP_PASS; 21898c2ecf20Sopenharmony_ci case XDP_REDIRECT: { 21908c2ecf20Sopenharmony_ci int err; 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci err = xdp_do_redirect(pp->dev, xdp, prog); 21938c2ecf20Sopenharmony_ci if (unlikely(err)) { 21948c2ecf20Sopenharmony_ci mvneta_xdp_put_buff(pp, rxq, xdp, sync, true); 21958c2ecf20Sopenharmony_ci ret = MVNETA_XDP_DROPPED; 21968c2ecf20Sopenharmony_ci } else { 21978c2ecf20Sopenharmony_ci ret = MVNETA_XDP_REDIR; 21988c2ecf20Sopenharmony_ci stats->xdp_redirect++; 21998c2ecf20Sopenharmony_ci } 22008c2ecf20Sopenharmony_ci break; 22018c2ecf20Sopenharmony_ci } 22028c2ecf20Sopenharmony_ci case XDP_TX: 22038c2ecf20Sopenharmony_ci ret = mvneta_xdp_xmit_back(pp, xdp); 22048c2ecf20Sopenharmony_ci if (ret != MVNETA_XDP_TX) 22058c2ecf20Sopenharmony_ci mvneta_xdp_put_buff(pp, rxq, xdp, sync, true); 22068c2ecf20Sopenharmony_ci break; 22078c2ecf20Sopenharmony_ci default: 22088c2ecf20Sopenharmony_ci bpf_warn_invalid_xdp_action(act); 22098c2ecf20Sopenharmony_ci fallthrough; 22108c2ecf20Sopenharmony_ci case XDP_ABORTED: 22118c2ecf20Sopenharmony_ci trace_xdp_exception(pp->dev, prog, act); 22128c2ecf20Sopenharmony_ci fallthrough; 22138c2ecf20Sopenharmony_ci case XDP_DROP: 22148c2ecf20Sopenharmony_ci mvneta_xdp_put_buff(pp, rxq, xdp, sync, true); 22158c2ecf20Sopenharmony_ci ret = MVNETA_XDP_DROPPED; 22168c2ecf20Sopenharmony_ci stats->xdp_drop++; 22178c2ecf20Sopenharmony_ci break; 22188c2ecf20Sopenharmony_ci } 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci stats->rx_bytes += frame_sz + xdp->data_end - xdp->data - data_len; 22218c2ecf20Sopenharmony_ci stats->rx_packets++; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci return ret; 22248c2ecf20Sopenharmony_ci} 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_cistatic void 22278c2ecf20Sopenharmony_cimvneta_swbm_rx_frame(struct mvneta_port *pp, 22288c2ecf20Sopenharmony_ci struct mvneta_rx_desc *rx_desc, 22298c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq, 22308c2ecf20Sopenharmony_ci struct xdp_buff *xdp, int *size, 22318c2ecf20Sopenharmony_ci struct page *page) 22328c2ecf20Sopenharmony_ci{ 22338c2ecf20Sopenharmony_ci unsigned char *data = page_address(page); 22348c2ecf20Sopenharmony_ci int data_len = -MVNETA_MH_SIZE, len; 22358c2ecf20Sopenharmony_ci struct net_device *dev = pp->dev; 22368c2ecf20Sopenharmony_ci enum dma_data_direction dma_dir; 22378c2ecf20Sopenharmony_ci struct skb_shared_info *sinfo; 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci if (*size > MVNETA_MAX_RX_BUF_SIZE) { 22408c2ecf20Sopenharmony_ci len = MVNETA_MAX_RX_BUF_SIZE; 22418c2ecf20Sopenharmony_ci data_len += len; 22428c2ecf20Sopenharmony_ci } else { 22438c2ecf20Sopenharmony_ci len = *size; 22448c2ecf20Sopenharmony_ci data_len += len - ETH_FCS_LEN; 22458c2ecf20Sopenharmony_ci } 22468c2ecf20Sopenharmony_ci *size = *size - len; 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci dma_dir = page_pool_get_dma_dir(rxq->page_pool); 22498c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(dev->dev.parent, 22508c2ecf20Sopenharmony_ci rx_desc->buf_phys_addr, 22518c2ecf20Sopenharmony_ci len, dma_dir); 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci rx_desc->buf_phys_addr = 0; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci /* Prefetch header */ 22568c2ecf20Sopenharmony_ci prefetch(data); 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci xdp->data_hard_start = data; 22598c2ecf20Sopenharmony_ci xdp->data = data + pp->rx_offset_correction + MVNETA_MH_SIZE; 22608c2ecf20Sopenharmony_ci xdp->data_end = xdp->data + data_len; 22618c2ecf20Sopenharmony_ci xdp_set_data_meta_invalid(xdp); 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci sinfo = xdp_get_shared_info_from_buff(xdp); 22648c2ecf20Sopenharmony_ci sinfo->nr_frags = 0; 22658c2ecf20Sopenharmony_ci} 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_cistatic void 22688c2ecf20Sopenharmony_cimvneta_swbm_add_rx_fragment(struct mvneta_port *pp, 22698c2ecf20Sopenharmony_ci struct mvneta_rx_desc *rx_desc, 22708c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq, 22718c2ecf20Sopenharmony_ci struct xdp_buff *xdp, int *size, 22728c2ecf20Sopenharmony_ci struct page *page) 22738c2ecf20Sopenharmony_ci{ 22748c2ecf20Sopenharmony_ci struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); 22758c2ecf20Sopenharmony_ci struct net_device *dev = pp->dev; 22768c2ecf20Sopenharmony_ci enum dma_data_direction dma_dir; 22778c2ecf20Sopenharmony_ci int data_len, len; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci if (*size > MVNETA_MAX_RX_BUF_SIZE) { 22808c2ecf20Sopenharmony_ci len = MVNETA_MAX_RX_BUF_SIZE; 22818c2ecf20Sopenharmony_ci data_len = len; 22828c2ecf20Sopenharmony_ci } else { 22838c2ecf20Sopenharmony_ci len = *size; 22848c2ecf20Sopenharmony_ci data_len = len - ETH_FCS_LEN; 22858c2ecf20Sopenharmony_ci } 22868c2ecf20Sopenharmony_ci dma_dir = page_pool_get_dma_dir(rxq->page_pool); 22878c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(dev->dev.parent, 22888c2ecf20Sopenharmony_ci rx_desc->buf_phys_addr, 22898c2ecf20Sopenharmony_ci len, dma_dir); 22908c2ecf20Sopenharmony_ci rx_desc->buf_phys_addr = 0; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci if (data_len > 0 && sinfo->nr_frags < MAX_SKB_FRAGS) { 22938c2ecf20Sopenharmony_ci skb_frag_t *frag = &sinfo->frags[sinfo->nr_frags]; 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci skb_frag_off_set(frag, pp->rx_offset_correction); 22968c2ecf20Sopenharmony_ci skb_frag_size_set(frag, data_len); 22978c2ecf20Sopenharmony_ci __skb_frag_set_page(frag, page); 22988c2ecf20Sopenharmony_ci sinfo->nr_frags++; 22998c2ecf20Sopenharmony_ci } else { 23008c2ecf20Sopenharmony_ci page_pool_put_full_page(rxq->page_pool, page, true); 23018c2ecf20Sopenharmony_ci } 23028c2ecf20Sopenharmony_ci *size -= len; 23038c2ecf20Sopenharmony_ci} 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_cistatic struct sk_buff * 23068c2ecf20Sopenharmony_cimvneta_swbm_build_skb(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, 23078c2ecf20Sopenharmony_ci struct xdp_buff *xdp, u32 desc_status) 23088c2ecf20Sopenharmony_ci{ 23098c2ecf20Sopenharmony_ci struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); 23108c2ecf20Sopenharmony_ci int i, num_frags = sinfo->nr_frags; 23118c2ecf20Sopenharmony_ci struct sk_buff *skb; 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci skb = build_skb(xdp->data_hard_start, PAGE_SIZE); 23148c2ecf20Sopenharmony_ci if (!skb) 23158c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci page_pool_release_page(rxq->page_pool, virt_to_page(xdp->data)); 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci skb_reserve(skb, xdp->data - xdp->data_hard_start); 23208c2ecf20Sopenharmony_ci skb_put(skb, xdp->data_end - xdp->data); 23218c2ecf20Sopenharmony_ci mvneta_rx_csum(pp, desc_status, skb); 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci for (i = 0; i < num_frags; i++) { 23248c2ecf20Sopenharmony_ci skb_frag_t *frag = &sinfo->frags[i]; 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, 23278c2ecf20Sopenharmony_ci skb_frag_page(frag), skb_frag_off(frag), 23288c2ecf20Sopenharmony_ci skb_frag_size(frag), PAGE_SIZE); 23298c2ecf20Sopenharmony_ci page_pool_release_page(rxq->page_pool, skb_frag_page(frag)); 23308c2ecf20Sopenharmony_ci } 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci return skb; 23338c2ecf20Sopenharmony_ci} 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci/* Main rx processing when using software buffer management */ 23368c2ecf20Sopenharmony_cistatic int mvneta_rx_swbm(struct napi_struct *napi, 23378c2ecf20Sopenharmony_ci struct mvneta_port *pp, int budget, 23388c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 23398c2ecf20Sopenharmony_ci{ 23408c2ecf20Sopenharmony_ci int rx_proc = 0, rx_todo, refill, size = 0; 23418c2ecf20Sopenharmony_ci struct net_device *dev = pp->dev; 23428c2ecf20Sopenharmony_ci struct xdp_buff xdp_buf = { 23438c2ecf20Sopenharmony_ci .frame_sz = PAGE_SIZE, 23448c2ecf20Sopenharmony_ci .rxq = &rxq->xdp_rxq, 23458c2ecf20Sopenharmony_ci }; 23468c2ecf20Sopenharmony_ci struct mvneta_stats ps = {}; 23478c2ecf20Sopenharmony_ci struct bpf_prog *xdp_prog; 23488c2ecf20Sopenharmony_ci u32 desc_status, frame_sz; 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci /* Get number of received packets */ 23518c2ecf20Sopenharmony_ci rx_todo = mvneta_rxq_busy_desc_num_get(pp, rxq); 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci rcu_read_lock(); 23548c2ecf20Sopenharmony_ci xdp_prog = READ_ONCE(pp->xdp_prog); 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_ci /* Fairness NAPI loop */ 23578c2ecf20Sopenharmony_ci while (rx_proc < budget && rx_proc < rx_todo) { 23588c2ecf20Sopenharmony_ci struct mvneta_rx_desc *rx_desc = mvneta_rxq_next_desc_get(rxq); 23598c2ecf20Sopenharmony_ci u32 rx_status, index; 23608c2ecf20Sopenharmony_ci struct sk_buff *skb; 23618c2ecf20Sopenharmony_ci struct page *page; 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci index = rx_desc - rxq->descs; 23648c2ecf20Sopenharmony_ci page = (struct page *)rxq->buf_virt_addr[index]; 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci rx_status = rx_desc->status; 23678c2ecf20Sopenharmony_ci rx_proc++; 23688c2ecf20Sopenharmony_ci rxq->refill_num++; 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci if (rx_status & MVNETA_RXD_FIRST_DESC) { 23718c2ecf20Sopenharmony_ci /* Check errors only for FIRST descriptor */ 23728c2ecf20Sopenharmony_ci if (rx_status & MVNETA_RXD_ERR_SUMMARY) { 23738c2ecf20Sopenharmony_ci mvneta_rx_error(pp, rx_desc); 23748c2ecf20Sopenharmony_ci goto next; 23758c2ecf20Sopenharmony_ci } 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci size = rx_desc->data_size; 23788c2ecf20Sopenharmony_ci frame_sz = size - ETH_FCS_LEN; 23798c2ecf20Sopenharmony_ci desc_status = rx_status; 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci mvneta_swbm_rx_frame(pp, rx_desc, rxq, &xdp_buf, 23828c2ecf20Sopenharmony_ci &size, page); 23838c2ecf20Sopenharmony_ci } else { 23848c2ecf20Sopenharmony_ci if (unlikely(!xdp_buf.data_hard_start)) { 23858c2ecf20Sopenharmony_ci rx_desc->buf_phys_addr = 0; 23868c2ecf20Sopenharmony_ci page_pool_put_full_page(rxq->page_pool, page, 23878c2ecf20Sopenharmony_ci true); 23888c2ecf20Sopenharmony_ci continue; 23898c2ecf20Sopenharmony_ci } 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_ci mvneta_swbm_add_rx_fragment(pp, rx_desc, rxq, &xdp_buf, 23928c2ecf20Sopenharmony_ci &size, page); 23938c2ecf20Sopenharmony_ci } /* Middle or Last descriptor */ 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci if (!(rx_status & MVNETA_RXD_LAST_DESC)) 23968c2ecf20Sopenharmony_ci /* no last descriptor this time */ 23978c2ecf20Sopenharmony_ci continue; 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci if (size) { 24008c2ecf20Sopenharmony_ci mvneta_xdp_put_buff(pp, rxq, &xdp_buf, -1, true); 24018c2ecf20Sopenharmony_ci goto next; 24028c2ecf20Sopenharmony_ci } 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci if (xdp_prog && 24058c2ecf20Sopenharmony_ci mvneta_run_xdp(pp, rxq, xdp_prog, &xdp_buf, frame_sz, &ps)) 24068c2ecf20Sopenharmony_ci goto next; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci skb = mvneta_swbm_build_skb(pp, rxq, &xdp_buf, desc_status); 24098c2ecf20Sopenharmony_ci if (IS_ERR(skb)) { 24108c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci mvneta_xdp_put_buff(pp, rxq, &xdp_buf, -1, true); 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 24158c2ecf20Sopenharmony_ci stats->es.skb_alloc_error++; 24168c2ecf20Sopenharmony_ci stats->rx_dropped++; 24178c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci goto next; 24208c2ecf20Sopenharmony_ci } 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci ps.rx_bytes += skb->len; 24238c2ecf20Sopenharmony_ci ps.rx_packets++; 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 24268c2ecf20Sopenharmony_ci napi_gro_receive(napi, skb); 24278c2ecf20Sopenharmony_cinext: 24288c2ecf20Sopenharmony_ci xdp_buf.data_hard_start = NULL; 24298c2ecf20Sopenharmony_ci } 24308c2ecf20Sopenharmony_ci rcu_read_unlock(); 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci if (xdp_buf.data_hard_start) 24338c2ecf20Sopenharmony_ci mvneta_xdp_put_buff(pp, rxq, &xdp_buf, -1, true); 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci if (ps.xdp_redirect) 24368c2ecf20Sopenharmony_ci xdp_do_flush_map(); 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci if (ps.rx_packets) 24398c2ecf20Sopenharmony_ci mvneta_update_stats(pp, &ps); 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci /* return some buffers to hardware queue, one at a time is too slow */ 24428c2ecf20Sopenharmony_ci refill = mvneta_rx_refill_queue(pp, rxq); 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci /* Update rxq management counters */ 24458c2ecf20Sopenharmony_ci mvneta_rxq_desc_num_update(pp, rxq, rx_proc, refill); 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci return ps.rx_packets; 24488c2ecf20Sopenharmony_ci} 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci/* Main rx processing when using hardware buffer management */ 24518c2ecf20Sopenharmony_cistatic int mvneta_rx_hwbm(struct napi_struct *napi, 24528c2ecf20Sopenharmony_ci struct mvneta_port *pp, int rx_todo, 24538c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 24548c2ecf20Sopenharmony_ci{ 24558c2ecf20Sopenharmony_ci struct net_device *dev = pp->dev; 24568c2ecf20Sopenharmony_ci int rx_done; 24578c2ecf20Sopenharmony_ci u32 rcvd_pkts = 0; 24588c2ecf20Sopenharmony_ci u32 rcvd_bytes = 0; 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci /* Get number of received packets */ 24618c2ecf20Sopenharmony_ci rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq); 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci if (rx_todo > rx_done) 24648c2ecf20Sopenharmony_ci rx_todo = rx_done; 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_ci rx_done = 0; 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci /* Fairness NAPI loop */ 24698c2ecf20Sopenharmony_ci while (rx_done < rx_todo) { 24708c2ecf20Sopenharmony_ci struct mvneta_rx_desc *rx_desc = mvneta_rxq_next_desc_get(rxq); 24718c2ecf20Sopenharmony_ci struct mvneta_bm_pool *bm_pool = NULL; 24728c2ecf20Sopenharmony_ci struct sk_buff *skb; 24738c2ecf20Sopenharmony_ci unsigned char *data; 24748c2ecf20Sopenharmony_ci dma_addr_t phys_addr; 24758c2ecf20Sopenharmony_ci u32 rx_status, frag_size; 24768c2ecf20Sopenharmony_ci int rx_bytes, err; 24778c2ecf20Sopenharmony_ci u8 pool_id; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci rx_done++; 24808c2ecf20Sopenharmony_ci rx_status = rx_desc->status; 24818c2ecf20Sopenharmony_ci rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE); 24828c2ecf20Sopenharmony_ci data = (u8 *)(uintptr_t)rx_desc->buf_cookie; 24838c2ecf20Sopenharmony_ci phys_addr = rx_desc->buf_phys_addr; 24848c2ecf20Sopenharmony_ci pool_id = MVNETA_RX_GET_BM_POOL_ID(rx_desc); 24858c2ecf20Sopenharmony_ci bm_pool = &pp->bm_priv->bm_pools[pool_id]; 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci if (!mvneta_rxq_desc_is_first_last(rx_status) || 24888c2ecf20Sopenharmony_ci (rx_status & MVNETA_RXD_ERR_SUMMARY)) { 24898c2ecf20Sopenharmony_cierr_drop_frame_ret_pool: 24908c2ecf20Sopenharmony_ci /* Return the buffer to the pool */ 24918c2ecf20Sopenharmony_ci mvneta_bm_pool_put_bp(pp->bm_priv, bm_pool, 24928c2ecf20Sopenharmony_ci rx_desc->buf_phys_addr); 24938c2ecf20Sopenharmony_cierr_drop_frame: 24948c2ecf20Sopenharmony_ci mvneta_rx_error(pp, rx_desc); 24958c2ecf20Sopenharmony_ci /* leave the descriptor untouched */ 24968c2ecf20Sopenharmony_ci continue; 24978c2ecf20Sopenharmony_ci } 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci if (rx_bytes <= rx_copybreak) { 25008c2ecf20Sopenharmony_ci /* better copy a small frame and not unmap the DMA region */ 25018c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(dev, rx_bytes); 25028c2ecf20Sopenharmony_ci if (unlikely(!skb)) 25038c2ecf20Sopenharmony_ci goto err_drop_frame_ret_pool; 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci dma_sync_single_range_for_cpu(&pp->bm_priv->pdev->dev, 25068c2ecf20Sopenharmony_ci rx_desc->buf_phys_addr, 25078c2ecf20Sopenharmony_ci MVNETA_MH_SIZE + NET_SKB_PAD, 25088c2ecf20Sopenharmony_ci rx_bytes, 25098c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 25108c2ecf20Sopenharmony_ci skb_put_data(skb, data + MVNETA_MH_SIZE + NET_SKB_PAD, 25118c2ecf20Sopenharmony_ci rx_bytes); 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 25148c2ecf20Sopenharmony_ci mvneta_rx_csum(pp, rx_status, skb); 25158c2ecf20Sopenharmony_ci napi_gro_receive(napi, skb); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci rcvd_pkts++; 25188c2ecf20Sopenharmony_ci rcvd_bytes += rx_bytes; 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci /* Return the buffer to the pool */ 25218c2ecf20Sopenharmony_ci mvneta_bm_pool_put_bp(pp->bm_priv, bm_pool, 25228c2ecf20Sopenharmony_ci rx_desc->buf_phys_addr); 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci /* leave the descriptor and buffer untouched */ 25258c2ecf20Sopenharmony_ci continue; 25268c2ecf20Sopenharmony_ci } 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci /* Refill processing */ 25298c2ecf20Sopenharmony_ci err = hwbm_pool_refill(&bm_pool->hwbm_pool, GFP_ATOMIC); 25308c2ecf20Sopenharmony_ci if (err) { 25318c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats *stats; 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci netdev_err(dev, "Linux processing - Can't refill\n"); 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci stats = this_cpu_ptr(pp->stats); 25368c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 25378c2ecf20Sopenharmony_ci stats->es.refill_error++; 25388c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci goto err_drop_frame_ret_pool; 25418c2ecf20Sopenharmony_ci } 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_ci frag_size = bm_pool->hwbm_pool.frag_size; 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci skb = build_skb(data, frag_size > PAGE_SIZE ? 0 : frag_size); 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci /* After refill old buffer has to be unmapped regardless 25488c2ecf20Sopenharmony_ci * the skb is successfully built or not. 25498c2ecf20Sopenharmony_ci */ 25508c2ecf20Sopenharmony_ci dma_unmap_single(&pp->bm_priv->pdev->dev, phys_addr, 25518c2ecf20Sopenharmony_ci bm_pool->buf_size, DMA_FROM_DEVICE); 25528c2ecf20Sopenharmony_ci if (!skb) 25538c2ecf20Sopenharmony_ci goto err_drop_frame; 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci rcvd_pkts++; 25568c2ecf20Sopenharmony_ci rcvd_bytes += rx_bytes; 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci /* Linux processing */ 25598c2ecf20Sopenharmony_ci skb_reserve(skb, MVNETA_MH_SIZE + NET_SKB_PAD); 25608c2ecf20Sopenharmony_ci skb_put(skb, rx_bytes); 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci mvneta_rx_csum(pp, rx_status, skb); 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci napi_gro_receive(napi, skb); 25678c2ecf20Sopenharmony_ci } 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci if (rcvd_pkts) { 25708c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 25738c2ecf20Sopenharmony_ci stats->es.ps.rx_packets += rcvd_pkts; 25748c2ecf20Sopenharmony_ci stats->es.ps.rx_bytes += rcvd_bytes; 25758c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 25768c2ecf20Sopenharmony_ci } 25778c2ecf20Sopenharmony_ci 25788c2ecf20Sopenharmony_ci /* Update rxq management counters */ 25798c2ecf20Sopenharmony_ci mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_done); 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci return rx_done; 25828c2ecf20Sopenharmony_ci} 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_cistatic inline void 25858c2ecf20Sopenharmony_cimvneta_tso_put_hdr(struct sk_buff *skb, 25868c2ecf20Sopenharmony_ci struct mvneta_port *pp, struct mvneta_tx_queue *txq) 25878c2ecf20Sopenharmony_ci{ 25888c2ecf20Sopenharmony_ci int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 25898c2ecf20Sopenharmony_ci struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index]; 25908c2ecf20Sopenharmony_ci struct mvneta_tx_desc *tx_desc; 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci tx_desc = mvneta_txq_next_desc_get(txq); 25938c2ecf20Sopenharmony_ci tx_desc->data_size = hdr_len; 25948c2ecf20Sopenharmony_ci tx_desc->command = mvneta_skb_tx_csum(pp, skb); 25958c2ecf20Sopenharmony_ci tx_desc->command |= MVNETA_TXD_F_DESC; 25968c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr = txq->tso_hdrs_phys + 25978c2ecf20Sopenharmony_ci txq->txq_put_index * TSO_HEADER_SIZE; 25988c2ecf20Sopenharmony_ci buf->type = MVNETA_TYPE_SKB; 25998c2ecf20Sopenharmony_ci buf->skb = NULL; 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci mvneta_txq_inc_put(txq); 26028c2ecf20Sopenharmony_ci} 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_cistatic inline int 26058c2ecf20Sopenharmony_cimvneta_tso_put_data(struct net_device *dev, struct mvneta_tx_queue *txq, 26068c2ecf20Sopenharmony_ci struct sk_buff *skb, char *data, int size, 26078c2ecf20Sopenharmony_ci bool last_tcp, bool is_last) 26088c2ecf20Sopenharmony_ci{ 26098c2ecf20Sopenharmony_ci struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index]; 26108c2ecf20Sopenharmony_ci struct mvneta_tx_desc *tx_desc; 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci tx_desc = mvneta_txq_next_desc_get(txq); 26138c2ecf20Sopenharmony_ci tx_desc->data_size = size; 26148c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr = dma_map_single(dev->dev.parent, data, 26158c2ecf20Sopenharmony_ci size, DMA_TO_DEVICE); 26168c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(dev->dev.parent, 26178c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr))) { 26188c2ecf20Sopenharmony_ci mvneta_txq_desc_put(txq); 26198c2ecf20Sopenharmony_ci return -ENOMEM; 26208c2ecf20Sopenharmony_ci } 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci tx_desc->command = 0; 26238c2ecf20Sopenharmony_ci buf->type = MVNETA_TYPE_SKB; 26248c2ecf20Sopenharmony_ci buf->skb = NULL; 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci if (last_tcp) { 26278c2ecf20Sopenharmony_ci /* last descriptor in the TCP packet */ 26288c2ecf20Sopenharmony_ci tx_desc->command = MVNETA_TXD_L_DESC; 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci /* last descriptor in SKB */ 26318c2ecf20Sopenharmony_ci if (is_last) 26328c2ecf20Sopenharmony_ci buf->skb = skb; 26338c2ecf20Sopenharmony_ci } 26348c2ecf20Sopenharmony_ci mvneta_txq_inc_put(txq); 26358c2ecf20Sopenharmony_ci return 0; 26368c2ecf20Sopenharmony_ci} 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_cistatic int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev, 26398c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 26408c2ecf20Sopenharmony_ci{ 26418c2ecf20Sopenharmony_ci int hdr_len, total_len, data_left; 26428c2ecf20Sopenharmony_ci int desc_count = 0; 26438c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 26448c2ecf20Sopenharmony_ci struct tso_t tso; 26458c2ecf20Sopenharmony_ci int i; 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci /* Count needed descriptors */ 26488c2ecf20Sopenharmony_ci if ((txq->count + tso_count_descs(skb)) >= txq->size) 26498c2ecf20Sopenharmony_ci return 0; 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci if (skb_headlen(skb) < (skb_transport_offset(skb) + tcp_hdrlen(skb))) { 26528c2ecf20Sopenharmony_ci pr_info("*** Is this even possible???!?!?\n"); 26538c2ecf20Sopenharmony_ci return 0; 26548c2ecf20Sopenharmony_ci } 26558c2ecf20Sopenharmony_ci 26568c2ecf20Sopenharmony_ci /* Initialize the TSO handler, and prepare the first payload */ 26578c2ecf20Sopenharmony_ci hdr_len = tso_start(skb, &tso); 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci total_len = skb->len - hdr_len; 26608c2ecf20Sopenharmony_ci while (total_len > 0) { 26618c2ecf20Sopenharmony_ci char *hdr; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 26648c2ecf20Sopenharmony_ci total_len -= data_left; 26658c2ecf20Sopenharmony_ci desc_count++; 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci /* prepare packet headers: MAC + IP + TCP */ 26688c2ecf20Sopenharmony_ci hdr = txq->tso_hdrs + txq->txq_put_index * TSO_HEADER_SIZE; 26698c2ecf20Sopenharmony_ci tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci mvneta_tso_put_hdr(skb, pp, txq); 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci while (data_left > 0) { 26748c2ecf20Sopenharmony_ci int size; 26758c2ecf20Sopenharmony_ci desc_count++; 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci size = min_t(int, tso.size, data_left); 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci if (mvneta_tso_put_data(dev, txq, skb, 26808c2ecf20Sopenharmony_ci tso.data, size, 26818c2ecf20Sopenharmony_ci size == data_left, 26828c2ecf20Sopenharmony_ci total_len == 0)) 26838c2ecf20Sopenharmony_ci goto err_release; 26848c2ecf20Sopenharmony_ci data_left -= size; 26858c2ecf20Sopenharmony_ci 26868c2ecf20Sopenharmony_ci tso_build_data(skb, &tso, size); 26878c2ecf20Sopenharmony_ci } 26888c2ecf20Sopenharmony_ci } 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci return desc_count; 26918c2ecf20Sopenharmony_ci 26928c2ecf20Sopenharmony_cierr_release: 26938c2ecf20Sopenharmony_ci /* Release all used data descriptors; header descriptors must not 26948c2ecf20Sopenharmony_ci * be DMA-unmapped. 26958c2ecf20Sopenharmony_ci */ 26968c2ecf20Sopenharmony_ci for (i = desc_count - 1; i >= 0; i--) { 26978c2ecf20Sopenharmony_ci struct mvneta_tx_desc *tx_desc = txq->descs + i; 26988c2ecf20Sopenharmony_ci if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr)) 26998c2ecf20Sopenharmony_ci dma_unmap_single(pp->dev->dev.parent, 27008c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr, 27018c2ecf20Sopenharmony_ci tx_desc->data_size, 27028c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 27038c2ecf20Sopenharmony_ci mvneta_txq_desc_put(txq); 27048c2ecf20Sopenharmony_ci } 27058c2ecf20Sopenharmony_ci return 0; 27068c2ecf20Sopenharmony_ci} 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci/* Handle tx fragmentation processing */ 27098c2ecf20Sopenharmony_cistatic int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb, 27108c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 27118c2ecf20Sopenharmony_ci{ 27128c2ecf20Sopenharmony_ci struct mvneta_tx_desc *tx_desc; 27138c2ecf20Sopenharmony_ci int i, nr_frags = skb_shinfo(skb)->nr_frags; 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_ci for (i = 0; i < nr_frags; i++) { 27168c2ecf20Sopenharmony_ci struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index]; 27178c2ecf20Sopenharmony_ci skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 27188c2ecf20Sopenharmony_ci void *addr = skb_frag_address(frag); 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci tx_desc = mvneta_txq_next_desc_get(txq); 27218c2ecf20Sopenharmony_ci tx_desc->data_size = skb_frag_size(frag); 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr = 27248c2ecf20Sopenharmony_ci dma_map_single(pp->dev->dev.parent, addr, 27258c2ecf20Sopenharmony_ci tx_desc->data_size, DMA_TO_DEVICE); 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci if (dma_mapping_error(pp->dev->dev.parent, 27288c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr)) { 27298c2ecf20Sopenharmony_ci mvneta_txq_desc_put(txq); 27308c2ecf20Sopenharmony_ci goto error; 27318c2ecf20Sopenharmony_ci } 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci if (i == nr_frags - 1) { 27348c2ecf20Sopenharmony_ci /* Last descriptor */ 27358c2ecf20Sopenharmony_ci tx_desc->command = MVNETA_TXD_L_DESC | MVNETA_TXD_Z_PAD; 27368c2ecf20Sopenharmony_ci buf->skb = skb; 27378c2ecf20Sopenharmony_ci } else { 27388c2ecf20Sopenharmony_ci /* Descriptor in the middle: Not First, Not Last */ 27398c2ecf20Sopenharmony_ci tx_desc->command = 0; 27408c2ecf20Sopenharmony_ci buf->skb = NULL; 27418c2ecf20Sopenharmony_ci } 27428c2ecf20Sopenharmony_ci buf->type = MVNETA_TYPE_SKB; 27438c2ecf20Sopenharmony_ci mvneta_txq_inc_put(txq); 27448c2ecf20Sopenharmony_ci } 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci return 0; 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_cierror: 27498c2ecf20Sopenharmony_ci /* Release all descriptors that were used to map fragments of 27508c2ecf20Sopenharmony_ci * this packet, as well as the corresponding DMA mappings 27518c2ecf20Sopenharmony_ci */ 27528c2ecf20Sopenharmony_ci for (i = i - 1; i >= 0; i--) { 27538c2ecf20Sopenharmony_ci tx_desc = txq->descs + i; 27548c2ecf20Sopenharmony_ci dma_unmap_single(pp->dev->dev.parent, 27558c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr, 27568c2ecf20Sopenharmony_ci tx_desc->data_size, 27578c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 27588c2ecf20Sopenharmony_ci mvneta_txq_desc_put(txq); 27598c2ecf20Sopenharmony_ci } 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci return -ENOMEM; 27628c2ecf20Sopenharmony_ci} 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci/* Main tx processing */ 27658c2ecf20Sopenharmony_cistatic netdev_tx_t mvneta_tx(struct sk_buff *skb, struct net_device *dev) 27668c2ecf20Sopenharmony_ci{ 27678c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 27688c2ecf20Sopenharmony_ci u16 txq_id = skb_get_queue_mapping(skb); 27698c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq = &pp->txqs[txq_id]; 27708c2ecf20Sopenharmony_ci struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index]; 27718c2ecf20Sopenharmony_ci struct mvneta_tx_desc *tx_desc; 27728c2ecf20Sopenharmony_ci int len = skb->len; 27738c2ecf20Sopenharmony_ci int frags = 0; 27748c2ecf20Sopenharmony_ci u32 tx_cmd; 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci if (!netif_running(dev)) 27778c2ecf20Sopenharmony_ci goto out; 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 27808c2ecf20Sopenharmony_ci frags = mvneta_tx_tso(skb, dev, txq); 27818c2ecf20Sopenharmony_ci goto out; 27828c2ecf20Sopenharmony_ci } 27838c2ecf20Sopenharmony_ci 27848c2ecf20Sopenharmony_ci frags = skb_shinfo(skb)->nr_frags + 1; 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci /* Get a descriptor for the first part of the packet */ 27878c2ecf20Sopenharmony_ci tx_desc = mvneta_txq_next_desc_get(txq); 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci tx_cmd = mvneta_skb_tx_csum(pp, skb); 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci tx_desc->data_size = skb_headlen(skb); 27928c2ecf20Sopenharmony_ci 27938c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr = dma_map_single(dev->dev.parent, skb->data, 27948c2ecf20Sopenharmony_ci tx_desc->data_size, 27958c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 27968c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(dev->dev.parent, 27978c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr))) { 27988c2ecf20Sopenharmony_ci mvneta_txq_desc_put(txq); 27998c2ecf20Sopenharmony_ci frags = 0; 28008c2ecf20Sopenharmony_ci goto out; 28018c2ecf20Sopenharmony_ci } 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci buf->type = MVNETA_TYPE_SKB; 28048c2ecf20Sopenharmony_ci if (frags == 1) { 28058c2ecf20Sopenharmony_ci /* First and Last descriptor */ 28068c2ecf20Sopenharmony_ci tx_cmd |= MVNETA_TXD_FLZ_DESC; 28078c2ecf20Sopenharmony_ci tx_desc->command = tx_cmd; 28088c2ecf20Sopenharmony_ci buf->skb = skb; 28098c2ecf20Sopenharmony_ci mvneta_txq_inc_put(txq); 28108c2ecf20Sopenharmony_ci } else { 28118c2ecf20Sopenharmony_ci /* First but not Last */ 28128c2ecf20Sopenharmony_ci tx_cmd |= MVNETA_TXD_F_DESC; 28138c2ecf20Sopenharmony_ci buf->skb = NULL; 28148c2ecf20Sopenharmony_ci mvneta_txq_inc_put(txq); 28158c2ecf20Sopenharmony_ci tx_desc->command = tx_cmd; 28168c2ecf20Sopenharmony_ci /* Continue with other skb fragments */ 28178c2ecf20Sopenharmony_ci if (mvneta_tx_frag_process(pp, skb, txq)) { 28188c2ecf20Sopenharmony_ci dma_unmap_single(dev->dev.parent, 28198c2ecf20Sopenharmony_ci tx_desc->buf_phys_addr, 28208c2ecf20Sopenharmony_ci tx_desc->data_size, 28218c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 28228c2ecf20Sopenharmony_ci mvneta_txq_desc_put(txq); 28238c2ecf20Sopenharmony_ci frags = 0; 28248c2ecf20Sopenharmony_ci goto out; 28258c2ecf20Sopenharmony_ci } 28268c2ecf20Sopenharmony_ci } 28278c2ecf20Sopenharmony_ci 28288c2ecf20Sopenharmony_ciout: 28298c2ecf20Sopenharmony_ci if (frags > 0) { 28308c2ecf20Sopenharmony_ci struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id); 28318c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci netdev_tx_sent_queue(nq, len); 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci txq->count += frags; 28368c2ecf20Sopenharmony_ci if (txq->count >= txq->tx_stop_threshold) 28378c2ecf20Sopenharmony_ci netif_tx_stop_queue(nq); 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_ci if (!netdev_xmit_more() || netif_xmit_stopped(nq) || 28408c2ecf20Sopenharmony_ci txq->pending + frags > MVNETA_TXQ_DEC_SENT_MASK) 28418c2ecf20Sopenharmony_ci mvneta_txq_pend_desc_add(pp, txq, frags); 28428c2ecf20Sopenharmony_ci else 28438c2ecf20Sopenharmony_ci txq->pending += frags; 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->syncp); 28468c2ecf20Sopenharmony_ci stats->es.ps.tx_bytes += len; 28478c2ecf20Sopenharmony_ci stats->es.ps.tx_packets++; 28488c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->syncp); 28498c2ecf20Sopenharmony_ci } else { 28508c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 28518c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 28528c2ecf20Sopenharmony_ci } 28538c2ecf20Sopenharmony_ci 28548c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 28558c2ecf20Sopenharmony_ci} 28568c2ecf20Sopenharmony_ci 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci/* Free tx resources, when resetting a port */ 28598c2ecf20Sopenharmony_cistatic void mvneta_txq_done_force(struct mvneta_port *pp, 28608c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci{ 28638c2ecf20Sopenharmony_ci struct netdev_queue *nq = netdev_get_tx_queue(pp->dev, txq->id); 28648c2ecf20Sopenharmony_ci int tx_done = txq->count; 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_ci mvneta_txq_bufs_free(pp, txq, tx_done, nq, false); 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci /* reset txq */ 28698c2ecf20Sopenharmony_ci txq->count = 0; 28708c2ecf20Sopenharmony_ci txq->txq_put_index = 0; 28718c2ecf20Sopenharmony_ci txq->txq_get_index = 0; 28728c2ecf20Sopenharmony_ci} 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci/* Handle tx done - called in softirq context. The <cause_tx_done> argument 28758c2ecf20Sopenharmony_ci * must be a valid cause according to MVNETA_TXQ_INTR_MASK_ALL. 28768c2ecf20Sopenharmony_ci */ 28778c2ecf20Sopenharmony_cistatic void mvneta_tx_done_gbe(struct mvneta_port *pp, u32 cause_tx_done) 28788c2ecf20Sopenharmony_ci{ 28798c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq; 28808c2ecf20Sopenharmony_ci struct netdev_queue *nq; 28818c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci while (cause_tx_done) { 28848c2ecf20Sopenharmony_ci txq = mvneta_tx_done_policy(pp, cause_tx_done); 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_ci nq = netdev_get_tx_queue(pp->dev, txq->id); 28878c2ecf20Sopenharmony_ci __netif_tx_lock(nq, cpu); 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ci if (txq->count) 28908c2ecf20Sopenharmony_ci mvneta_txq_done(pp, txq); 28918c2ecf20Sopenharmony_ci 28928c2ecf20Sopenharmony_ci __netif_tx_unlock(nq); 28938c2ecf20Sopenharmony_ci cause_tx_done &= ~((1 << txq->id)); 28948c2ecf20Sopenharmony_ci } 28958c2ecf20Sopenharmony_ci} 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci/* Compute crc8 of the specified address, using a unique algorithm , 28988c2ecf20Sopenharmony_ci * according to hw spec, different than generic crc8 algorithm 28998c2ecf20Sopenharmony_ci */ 29008c2ecf20Sopenharmony_cistatic int mvneta_addr_crc(unsigned char *addr) 29018c2ecf20Sopenharmony_ci{ 29028c2ecf20Sopenharmony_ci int crc = 0; 29038c2ecf20Sopenharmony_ci int i; 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) { 29068c2ecf20Sopenharmony_ci int j; 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci crc = (crc ^ addr[i]) << 8; 29098c2ecf20Sopenharmony_ci for (j = 7; j >= 0; j--) { 29108c2ecf20Sopenharmony_ci if (crc & (0x100 << j)) 29118c2ecf20Sopenharmony_ci crc ^= 0x107 << j; 29128c2ecf20Sopenharmony_ci } 29138c2ecf20Sopenharmony_ci } 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_ci return crc; 29168c2ecf20Sopenharmony_ci} 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci/* This method controls the net device special MAC multicast support. 29198c2ecf20Sopenharmony_ci * The Special Multicast Table for MAC addresses supports MAC of the form 29208c2ecf20Sopenharmony_ci * 0x01-00-5E-00-00-XX (where XX is between 0x00 and 0xFF). 29218c2ecf20Sopenharmony_ci * The MAC DA[7:0] bits are used as a pointer to the Special Multicast 29228c2ecf20Sopenharmony_ci * Table entries in the DA-Filter table. This method set the Special 29238c2ecf20Sopenharmony_ci * Multicast Table appropriate entry. 29248c2ecf20Sopenharmony_ci */ 29258c2ecf20Sopenharmony_cistatic void mvneta_set_special_mcast_addr(struct mvneta_port *pp, 29268c2ecf20Sopenharmony_ci unsigned char last_byte, 29278c2ecf20Sopenharmony_ci int queue) 29288c2ecf20Sopenharmony_ci{ 29298c2ecf20Sopenharmony_ci unsigned int smc_table_reg; 29308c2ecf20Sopenharmony_ci unsigned int tbl_offset; 29318c2ecf20Sopenharmony_ci unsigned int reg_offset; 29328c2ecf20Sopenharmony_ci 29338c2ecf20Sopenharmony_ci /* Register offset from SMC table base */ 29348c2ecf20Sopenharmony_ci tbl_offset = (last_byte / 4); 29358c2ecf20Sopenharmony_ci /* Entry offset within the above reg */ 29368c2ecf20Sopenharmony_ci reg_offset = last_byte % 4; 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci smc_table_reg = mvreg_read(pp, (MVNETA_DA_FILT_SPEC_MCAST 29398c2ecf20Sopenharmony_ci + tbl_offset * 4)); 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_ci if (queue == -1) 29428c2ecf20Sopenharmony_ci smc_table_reg &= ~(0xff << (8 * reg_offset)); 29438c2ecf20Sopenharmony_ci else { 29448c2ecf20Sopenharmony_ci smc_table_reg &= ~(0xff << (8 * reg_offset)); 29458c2ecf20Sopenharmony_ci smc_table_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset)); 29468c2ecf20Sopenharmony_ci } 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_DA_FILT_SPEC_MCAST + tbl_offset * 4, 29498c2ecf20Sopenharmony_ci smc_table_reg); 29508c2ecf20Sopenharmony_ci} 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_ci/* This method controls the network device Other MAC multicast support. 29538c2ecf20Sopenharmony_ci * The Other Multicast Table is used for multicast of another type. 29548c2ecf20Sopenharmony_ci * A CRC-8 is used as an index to the Other Multicast Table entries 29558c2ecf20Sopenharmony_ci * in the DA-Filter table. 29568c2ecf20Sopenharmony_ci * The method gets the CRC-8 value from the calling routine and 29578c2ecf20Sopenharmony_ci * sets the Other Multicast Table appropriate entry according to the 29588c2ecf20Sopenharmony_ci * specified CRC-8 . 29598c2ecf20Sopenharmony_ci */ 29608c2ecf20Sopenharmony_cistatic void mvneta_set_other_mcast_addr(struct mvneta_port *pp, 29618c2ecf20Sopenharmony_ci unsigned char crc8, 29628c2ecf20Sopenharmony_ci int queue) 29638c2ecf20Sopenharmony_ci{ 29648c2ecf20Sopenharmony_ci unsigned int omc_table_reg; 29658c2ecf20Sopenharmony_ci unsigned int tbl_offset; 29668c2ecf20Sopenharmony_ci unsigned int reg_offset; 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_ci tbl_offset = (crc8 / 4) * 4; /* Register offset from OMC table base */ 29698c2ecf20Sopenharmony_ci reg_offset = crc8 % 4; /* Entry offset within the above reg */ 29708c2ecf20Sopenharmony_ci 29718c2ecf20Sopenharmony_ci omc_table_reg = mvreg_read(pp, MVNETA_DA_FILT_OTH_MCAST + tbl_offset); 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ci if (queue == -1) { 29748c2ecf20Sopenharmony_ci /* Clear accepts frame bit at specified Other DA table entry */ 29758c2ecf20Sopenharmony_ci omc_table_reg &= ~(0xff << (8 * reg_offset)); 29768c2ecf20Sopenharmony_ci } else { 29778c2ecf20Sopenharmony_ci omc_table_reg &= ~(0xff << (8 * reg_offset)); 29788c2ecf20Sopenharmony_ci omc_table_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset)); 29798c2ecf20Sopenharmony_ci } 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + tbl_offset, omc_table_reg); 29828c2ecf20Sopenharmony_ci} 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_ci/* The network device supports multicast using two tables: 29858c2ecf20Sopenharmony_ci * 1) Special Multicast Table for MAC addresses of the form 29868c2ecf20Sopenharmony_ci * 0x01-00-5E-00-00-XX (where XX is between 0x00 and 0xFF). 29878c2ecf20Sopenharmony_ci * The MAC DA[7:0] bits are used as a pointer to the Special Multicast 29888c2ecf20Sopenharmony_ci * Table entries in the DA-Filter table. 29898c2ecf20Sopenharmony_ci * 2) Other Multicast Table for multicast of another type. A CRC-8 value 29908c2ecf20Sopenharmony_ci * is used as an index to the Other Multicast Table entries in the 29918c2ecf20Sopenharmony_ci * DA-Filter table. 29928c2ecf20Sopenharmony_ci */ 29938c2ecf20Sopenharmony_cistatic int mvneta_mcast_addr_set(struct mvneta_port *pp, unsigned char *p_addr, 29948c2ecf20Sopenharmony_ci int queue) 29958c2ecf20Sopenharmony_ci{ 29968c2ecf20Sopenharmony_ci unsigned char crc_result = 0; 29978c2ecf20Sopenharmony_ci 29988c2ecf20Sopenharmony_ci if (memcmp(p_addr, "\x01\x00\x5e\x00\x00", 5) == 0) { 29998c2ecf20Sopenharmony_ci mvneta_set_special_mcast_addr(pp, p_addr[5], queue); 30008c2ecf20Sopenharmony_ci return 0; 30018c2ecf20Sopenharmony_ci } 30028c2ecf20Sopenharmony_ci 30038c2ecf20Sopenharmony_ci crc_result = mvneta_addr_crc(p_addr); 30048c2ecf20Sopenharmony_ci if (queue == -1) { 30058c2ecf20Sopenharmony_ci if (pp->mcast_count[crc_result] == 0) { 30068c2ecf20Sopenharmony_ci netdev_info(pp->dev, "No valid Mcast for crc8=0x%02x\n", 30078c2ecf20Sopenharmony_ci crc_result); 30088c2ecf20Sopenharmony_ci return -EINVAL; 30098c2ecf20Sopenharmony_ci } 30108c2ecf20Sopenharmony_ci 30118c2ecf20Sopenharmony_ci pp->mcast_count[crc_result]--; 30128c2ecf20Sopenharmony_ci if (pp->mcast_count[crc_result] != 0) { 30138c2ecf20Sopenharmony_ci netdev_info(pp->dev, 30148c2ecf20Sopenharmony_ci "After delete there are %d valid Mcast for crc8=0x%02x\n", 30158c2ecf20Sopenharmony_ci pp->mcast_count[crc_result], crc_result); 30168c2ecf20Sopenharmony_ci return -EINVAL; 30178c2ecf20Sopenharmony_ci } 30188c2ecf20Sopenharmony_ci } else 30198c2ecf20Sopenharmony_ci pp->mcast_count[crc_result]++; 30208c2ecf20Sopenharmony_ci 30218c2ecf20Sopenharmony_ci mvneta_set_other_mcast_addr(pp, crc_result, queue); 30228c2ecf20Sopenharmony_ci 30238c2ecf20Sopenharmony_ci return 0; 30248c2ecf20Sopenharmony_ci} 30258c2ecf20Sopenharmony_ci 30268c2ecf20Sopenharmony_ci/* Configure Fitering mode of Ethernet port */ 30278c2ecf20Sopenharmony_cistatic void mvneta_rx_unicast_promisc_set(struct mvneta_port *pp, 30288c2ecf20Sopenharmony_ci int is_promisc) 30298c2ecf20Sopenharmony_ci{ 30308c2ecf20Sopenharmony_ci u32 port_cfg_reg, val; 30318c2ecf20Sopenharmony_ci 30328c2ecf20Sopenharmony_ci port_cfg_reg = mvreg_read(pp, MVNETA_PORT_CONFIG); 30338c2ecf20Sopenharmony_ci 30348c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_TYPE_PRIO); 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci /* Set / Clear UPM bit in port configuration register */ 30378c2ecf20Sopenharmony_ci if (is_promisc) { 30388c2ecf20Sopenharmony_ci /* Accept all Unicast addresses */ 30398c2ecf20Sopenharmony_ci port_cfg_reg |= MVNETA_UNI_PROMISC_MODE; 30408c2ecf20Sopenharmony_ci val |= MVNETA_FORCE_UNI; 30418c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_MAC_ADDR_LOW, 0xffff); 30428c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_MAC_ADDR_HIGH, 0xffffffff); 30438c2ecf20Sopenharmony_ci } else { 30448c2ecf20Sopenharmony_ci /* Reject all Unicast addresses */ 30458c2ecf20Sopenharmony_ci port_cfg_reg &= ~MVNETA_UNI_PROMISC_MODE; 30468c2ecf20Sopenharmony_ci val &= ~MVNETA_FORCE_UNI; 30478c2ecf20Sopenharmony_ci } 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_CONFIG, port_cfg_reg); 30508c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TYPE_PRIO, val); 30518c2ecf20Sopenharmony_ci} 30528c2ecf20Sopenharmony_ci 30538c2ecf20Sopenharmony_ci/* register unicast and multicast addresses */ 30548c2ecf20Sopenharmony_cistatic void mvneta_set_rx_mode(struct net_device *dev) 30558c2ecf20Sopenharmony_ci{ 30568c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 30578c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 30608c2ecf20Sopenharmony_ci /* Accept all: Multicast + Unicast */ 30618c2ecf20Sopenharmony_ci mvneta_rx_unicast_promisc_set(pp, 1); 30628c2ecf20Sopenharmony_ci mvneta_set_ucast_table(pp, pp->rxq_def); 30638c2ecf20Sopenharmony_ci mvneta_set_special_mcast_table(pp, pp->rxq_def); 30648c2ecf20Sopenharmony_ci mvneta_set_other_mcast_table(pp, pp->rxq_def); 30658c2ecf20Sopenharmony_ci } else { 30668c2ecf20Sopenharmony_ci /* Accept single Unicast */ 30678c2ecf20Sopenharmony_ci mvneta_rx_unicast_promisc_set(pp, 0); 30688c2ecf20Sopenharmony_ci mvneta_set_ucast_table(pp, -1); 30698c2ecf20Sopenharmony_ci mvneta_mac_addr_set(pp, dev->dev_addr, pp->rxq_def); 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_ci if (dev->flags & IFF_ALLMULTI) { 30728c2ecf20Sopenharmony_ci /* Accept all multicast */ 30738c2ecf20Sopenharmony_ci mvneta_set_special_mcast_table(pp, pp->rxq_def); 30748c2ecf20Sopenharmony_ci mvneta_set_other_mcast_table(pp, pp->rxq_def); 30758c2ecf20Sopenharmony_ci } else { 30768c2ecf20Sopenharmony_ci /* Accept only initialized multicast */ 30778c2ecf20Sopenharmony_ci mvneta_set_special_mcast_table(pp, -1); 30788c2ecf20Sopenharmony_ci mvneta_set_other_mcast_table(pp, -1); 30798c2ecf20Sopenharmony_ci 30808c2ecf20Sopenharmony_ci if (!netdev_mc_empty(dev)) { 30818c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 30828c2ecf20Sopenharmony_ci mvneta_mcast_addr_set(pp, ha->addr, 30838c2ecf20Sopenharmony_ci pp->rxq_def); 30848c2ecf20Sopenharmony_ci } 30858c2ecf20Sopenharmony_ci } 30868c2ecf20Sopenharmony_ci } 30878c2ecf20Sopenharmony_ci } 30888c2ecf20Sopenharmony_ci} 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_ci/* Interrupt handling - the callback for request_irq() */ 30918c2ecf20Sopenharmony_cistatic irqreturn_t mvneta_isr(int irq, void *dev_id) 30928c2ecf20Sopenharmony_ci{ 30938c2ecf20Sopenharmony_ci struct mvneta_port *pp = (struct mvneta_port *)dev_id; 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); 30968c2ecf20Sopenharmony_ci napi_schedule(&pp->napi); 30978c2ecf20Sopenharmony_ci 30988c2ecf20Sopenharmony_ci return IRQ_HANDLED; 30998c2ecf20Sopenharmony_ci} 31008c2ecf20Sopenharmony_ci 31018c2ecf20Sopenharmony_ci/* Interrupt handling - the callback for request_percpu_irq() */ 31028c2ecf20Sopenharmony_cistatic irqreturn_t mvneta_percpu_isr(int irq, void *dev_id) 31038c2ecf20Sopenharmony_ci{ 31048c2ecf20Sopenharmony_ci struct mvneta_pcpu_port *port = (struct mvneta_pcpu_port *)dev_id; 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci disable_percpu_irq(port->pp->dev->irq); 31078c2ecf20Sopenharmony_ci napi_schedule(&port->napi); 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci return IRQ_HANDLED; 31108c2ecf20Sopenharmony_ci} 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_cistatic void mvneta_link_change(struct mvneta_port *pp) 31138c2ecf20Sopenharmony_ci{ 31148c2ecf20Sopenharmony_ci u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ci phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP)); 31178c2ecf20Sopenharmony_ci} 31188c2ecf20Sopenharmony_ci 31198c2ecf20Sopenharmony_ci/* NAPI handler 31208c2ecf20Sopenharmony_ci * Bits 0 - 7 of the causeRxTx register indicate that are transmitted 31218c2ecf20Sopenharmony_ci * packets on the corresponding TXQ (Bit 0 is for TX queue 1). 31228c2ecf20Sopenharmony_ci * Bits 8 -15 of the cause Rx Tx register indicate that are received 31238c2ecf20Sopenharmony_ci * packets on the corresponding RXQ (Bit 8 is for RX queue 0). 31248c2ecf20Sopenharmony_ci * Each CPU has its own causeRxTx register 31258c2ecf20Sopenharmony_ci */ 31268c2ecf20Sopenharmony_cistatic int mvneta_poll(struct napi_struct *napi, int budget) 31278c2ecf20Sopenharmony_ci{ 31288c2ecf20Sopenharmony_ci int rx_done = 0; 31298c2ecf20Sopenharmony_ci u32 cause_rx_tx; 31308c2ecf20Sopenharmony_ci int rx_queue; 31318c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(napi->dev); 31328c2ecf20Sopenharmony_ci struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports); 31338c2ecf20Sopenharmony_ci 31348c2ecf20Sopenharmony_ci if (!netif_running(pp->dev)) { 31358c2ecf20Sopenharmony_ci napi_complete(napi); 31368c2ecf20Sopenharmony_ci return rx_done; 31378c2ecf20Sopenharmony_ci } 31388c2ecf20Sopenharmony_ci 31398c2ecf20Sopenharmony_ci /* Read cause register */ 31408c2ecf20Sopenharmony_ci cause_rx_tx = mvreg_read(pp, MVNETA_INTR_NEW_CAUSE); 31418c2ecf20Sopenharmony_ci if (cause_rx_tx & MVNETA_MISCINTR_INTR_MASK) { 31428c2ecf20Sopenharmony_ci u32 cause_misc = mvreg_read(pp, MVNETA_INTR_MISC_CAUSE); 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0); 31458c2ecf20Sopenharmony_ci 31468c2ecf20Sopenharmony_ci if (cause_misc & (MVNETA_CAUSE_PHY_STATUS_CHANGE | 31478c2ecf20Sopenharmony_ci MVNETA_CAUSE_LINK_CHANGE)) 31488c2ecf20Sopenharmony_ci mvneta_link_change(pp); 31498c2ecf20Sopenharmony_ci } 31508c2ecf20Sopenharmony_ci 31518c2ecf20Sopenharmony_ci /* Release Tx descriptors */ 31528c2ecf20Sopenharmony_ci if (cause_rx_tx & MVNETA_TX_INTR_MASK_ALL) { 31538c2ecf20Sopenharmony_ci mvneta_tx_done_gbe(pp, (cause_rx_tx & MVNETA_TX_INTR_MASK_ALL)); 31548c2ecf20Sopenharmony_ci cause_rx_tx &= ~MVNETA_TX_INTR_MASK_ALL; 31558c2ecf20Sopenharmony_ci } 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci /* For the case where the last mvneta_poll did not process all 31588c2ecf20Sopenharmony_ci * RX packets 31598c2ecf20Sopenharmony_ci */ 31608c2ecf20Sopenharmony_ci cause_rx_tx |= pp->neta_armada3700 ? pp->cause_rx_tx : 31618c2ecf20Sopenharmony_ci port->cause_rx_tx; 31628c2ecf20Sopenharmony_ci 31638c2ecf20Sopenharmony_ci rx_queue = fls(((cause_rx_tx >> 8) & 0xff)); 31648c2ecf20Sopenharmony_ci if (rx_queue) { 31658c2ecf20Sopenharmony_ci rx_queue = rx_queue - 1; 31668c2ecf20Sopenharmony_ci if (pp->bm_priv) 31678c2ecf20Sopenharmony_ci rx_done = mvneta_rx_hwbm(napi, pp, budget, 31688c2ecf20Sopenharmony_ci &pp->rxqs[rx_queue]); 31698c2ecf20Sopenharmony_ci else 31708c2ecf20Sopenharmony_ci rx_done = mvneta_rx_swbm(napi, pp, budget, 31718c2ecf20Sopenharmony_ci &pp->rxqs[rx_queue]); 31728c2ecf20Sopenharmony_ci } 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci if (rx_done < budget) { 31758c2ecf20Sopenharmony_ci cause_rx_tx = 0; 31768c2ecf20Sopenharmony_ci napi_complete_done(napi, rx_done); 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_ci if (pp->neta_armada3700) { 31798c2ecf20Sopenharmony_ci unsigned long flags; 31808c2ecf20Sopenharmony_ci 31818c2ecf20Sopenharmony_ci local_irq_save(flags); 31828c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_NEW_MASK, 31838c2ecf20Sopenharmony_ci MVNETA_RX_INTR_MASK(rxq_number) | 31848c2ecf20Sopenharmony_ci MVNETA_TX_INTR_MASK(txq_number) | 31858c2ecf20Sopenharmony_ci MVNETA_MISCINTR_INTR_MASK); 31868c2ecf20Sopenharmony_ci local_irq_restore(flags); 31878c2ecf20Sopenharmony_ci } else { 31888c2ecf20Sopenharmony_ci enable_percpu_irq(pp->dev->irq, 0); 31898c2ecf20Sopenharmony_ci } 31908c2ecf20Sopenharmony_ci } 31918c2ecf20Sopenharmony_ci 31928c2ecf20Sopenharmony_ci if (pp->neta_armada3700) 31938c2ecf20Sopenharmony_ci pp->cause_rx_tx = cause_rx_tx; 31948c2ecf20Sopenharmony_ci else 31958c2ecf20Sopenharmony_ci port->cause_rx_tx = cause_rx_tx; 31968c2ecf20Sopenharmony_ci 31978c2ecf20Sopenharmony_ci return rx_done; 31988c2ecf20Sopenharmony_ci} 31998c2ecf20Sopenharmony_ci 32008c2ecf20Sopenharmony_cistatic int mvneta_create_page_pool(struct mvneta_port *pp, 32018c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq, int size) 32028c2ecf20Sopenharmony_ci{ 32038c2ecf20Sopenharmony_ci struct bpf_prog *xdp_prog = READ_ONCE(pp->xdp_prog); 32048c2ecf20Sopenharmony_ci struct page_pool_params pp_params = { 32058c2ecf20Sopenharmony_ci .order = 0, 32068c2ecf20Sopenharmony_ci .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, 32078c2ecf20Sopenharmony_ci .pool_size = size, 32088c2ecf20Sopenharmony_ci .nid = NUMA_NO_NODE, 32098c2ecf20Sopenharmony_ci .dev = pp->dev->dev.parent, 32108c2ecf20Sopenharmony_ci .dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE, 32118c2ecf20Sopenharmony_ci .offset = pp->rx_offset_correction, 32128c2ecf20Sopenharmony_ci .max_len = MVNETA_MAX_RX_BUF_SIZE, 32138c2ecf20Sopenharmony_ci }; 32148c2ecf20Sopenharmony_ci int err; 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci rxq->page_pool = page_pool_create(&pp_params); 32178c2ecf20Sopenharmony_ci if (IS_ERR(rxq->page_pool)) { 32188c2ecf20Sopenharmony_ci err = PTR_ERR(rxq->page_pool); 32198c2ecf20Sopenharmony_ci rxq->page_pool = NULL; 32208c2ecf20Sopenharmony_ci return err; 32218c2ecf20Sopenharmony_ci } 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_ci err = xdp_rxq_info_reg(&rxq->xdp_rxq, pp->dev, rxq->id); 32248c2ecf20Sopenharmony_ci if (err < 0) 32258c2ecf20Sopenharmony_ci goto err_free_pp; 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci err = xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq, MEM_TYPE_PAGE_POOL, 32288c2ecf20Sopenharmony_ci rxq->page_pool); 32298c2ecf20Sopenharmony_ci if (err) 32308c2ecf20Sopenharmony_ci goto err_unregister_rxq; 32318c2ecf20Sopenharmony_ci 32328c2ecf20Sopenharmony_ci return 0; 32338c2ecf20Sopenharmony_ci 32348c2ecf20Sopenharmony_cierr_unregister_rxq: 32358c2ecf20Sopenharmony_ci xdp_rxq_info_unreg(&rxq->xdp_rxq); 32368c2ecf20Sopenharmony_cierr_free_pp: 32378c2ecf20Sopenharmony_ci page_pool_destroy(rxq->page_pool); 32388c2ecf20Sopenharmony_ci rxq->page_pool = NULL; 32398c2ecf20Sopenharmony_ci return err; 32408c2ecf20Sopenharmony_ci} 32418c2ecf20Sopenharmony_ci 32428c2ecf20Sopenharmony_ci/* Handle rxq fill: allocates rxq skbs; called when initializing a port */ 32438c2ecf20Sopenharmony_cistatic int mvneta_rxq_fill(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, 32448c2ecf20Sopenharmony_ci int num) 32458c2ecf20Sopenharmony_ci{ 32468c2ecf20Sopenharmony_ci int i, err; 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci err = mvneta_create_page_pool(pp, rxq, num); 32498c2ecf20Sopenharmony_ci if (err < 0) 32508c2ecf20Sopenharmony_ci return err; 32518c2ecf20Sopenharmony_ci 32528c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 32538c2ecf20Sopenharmony_ci memset(rxq->descs + i, 0, sizeof(struct mvneta_rx_desc)); 32548c2ecf20Sopenharmony_ci if (mvneta_rx_refill(pp, rxq->descs + i, rxq, 32558c2ecf20Sopenharmony_ci GFP_KERNEL) != 0) { 32568c2ecf20Sopenharmony_ci netdev_err(pp->dev, 32578c2ecf20Sopenharmony_ci "%s:rxq %d, %d of %d buffs filled\n", 32588c2ecf20Sopenharmony_ci __func__, rxq->id, i, num); 32598c2ecf20Sopenharmony_ci break; 32608c2ecf20Sopenharmony_ci } 32618c2ecf20Sopenharmony_ci } 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_ci /* Add this number of RX descriptors as non occupied (ready to 32648c2ecf20Sopenharmony_ci * get packets) 32658c2ecf20Sopenharmony_ci */ 32668c2ecf20Sopenharmony_ci mvneta_rxq_non_occup_desc_add(pp, rxq, i); 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ci return i; 32698c2ecf20Sopenharmony_ci} 32708c2ecf20Sopenharmony_ci 32718c2ecf20Sopenharmony_ci/* Free all packets pending transmit from all TXQs and reset TX port */ 32728c2ecf20Sopenharmony_cistatic void mvneta_tx_reset(struct mvneta_port *pp) 32738c2ecf20Sopenharmony_ci{ 32748c2ecf20Sopenharmony_ci int queue; 32758c2ecf20Sopenharmony_ci 32768c2ecf20Sopenharmony_ci /* free the skb's in the tx ring */ 32778c2ecf20Sopenharmony_ci for (queue = 0; queue < txq_number; queue++) 32788c2ecf20Sopenharmony_ci mvneta_txq_done_force(pp, &pp->txqs[queue]); 32798c2ecf20Sopenharmony_ci 32808c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_TX_RESET, MVNETA_PORT_TX_DMA_RESET); 32818c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_TX_RESET, 0); 32828c2ecf20Sopenharmony_ci} 32838c2ecf20Sopenharmony_ci 32848c2ecf20Sopenharmony_cistatic void mvneta_rx_reset(struct mvneta_port *pp) 32858c2ecf20Sopenharmony_ci{ 32868c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET); 32878c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_RX_RESET, 0); 32888c2ecf20Sopenharmony_ci} 32898c2ecf20Sopenharmony_ci 32908c2ecf20Sopenharmony_ci/* Rx/Tx queue initialization/cleanup methods */ 32918c2ecf20Sopenharmony_ci 32928c2ecf20Sopenharmony_cistatic int mvneta_rxq_sw_init(struct mvneta_port *pp, 32938c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 32948c2ecf20Sopenharmony_ci{ 32958c2ecf20Sopenharmony_ci rxq->size = pp->rx_ring_size; 32968c2ecf20Sopenharmony_ci 32978c2ecf20Sopenharmony_ci /* Allocate memory for RX descriptors */ 32988c2ecf20Sopenharmony_ci rxq->descs = dma_alloc_coherent(pp->dev->dev.parent, 32998c2ecf20Sopenharmony_ci rxq->size * MVNETA_DESC_ALIGNED_SIZE, 33008c2ecf20Sopenharmony_ci &rxq->descs_phys, GFP_KERNEL); 33018c2ecf20Sopenharmony_ci if (!rxq->descs) 33028c2ecf20Sopenharmony_ci return -ENOMEM; 33038c2ecf20Sopenharmony_ci 33048c2ecf20Sopenharmony_ci rxq->last_desc = rxq->size - 1; 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_ci return 0; 33078c2ecf20Sopenharmony_ci} 33088c2ecf20Sopenharmony_ci 33098c2ecf20Sopenharmony_cistatic void mvneta_rxq_hw_init(struct mvneta_port *pp, 33108c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 33118c2ecf20Sopenharmony_ci{ 33128c2ecf20Sopenharmony_ci /* Set Rx descriptors queue starting address */ 33138c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_BASE_ADDR_REG(rxq->id), rxq->descs_phys); 33148c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), rxq->size); 33158c2ecf20Sopenharmony_ci 33168c2ecf20Sopenharmony_ci /* Set coalescing pkts and time */ 33178c2ecf20Sopenharmony_ci mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal); 33188c2ecf20Sopenharmony_ci mvneta_rx_time_coal_set(pp, rxq, rxq->time_coal); 33198c2ecf20Sopenharmony_ci 33208c2ecf20Sopenharmony_ci if (!pp->bm_priv) { 33218c2ecf20Sopenharmony_ci /* Set Offset */ 33228c2ecf20Sopenharmony_ci mvneta_rxq_offset_set(pp, rxq, 0); 33238c2ecf20Sopenharmony_ci mvneta_rxq_buf_size_set(pp, rxq, PAGE_SIZE < SZ_64K ? 33248c2ecf20Sopenharmony_ci MVNETA_MAX_RX_BUF_SIZE : 33258c2ecf20Sopenharmony_ci MVNETA_RX_BUF_SIZE(pp->pkt_size)); 33268c2ecf20Sopenharmony_ci mvneta_rxq_bm_disable(pp, rxq); 33278c2ecf20Sopenharmony_ci mvneta_rxq_fill(pp, rxq, rxq->size); 33288c2ecf20Sopenharmony_ci } else { 33298c2ecf20Sopenharmony_ci /* Set Offset */ 33308c2ecf20Sopenharmony_ci mvneta_rxq_offset_set(pp, rxq, 33318c2ecf20Sopenharmony_ci NET_SKB_PAD - pp->rx_offset_correction); 33328c2ecf20Sopenharmony_ci 33338c2ecf20Sopenharmony_ci mvneta_rxq_bm_enable(pp, rxq); 33348c2ecf20Sopenharmony_ci /* Fill RXQ with buffers from RX pool */ 33358c2ecf20Sopenharmony_ci mvneta_rxq_long_pool_set(pp, rxq); 33368c2ecf20Sopenharmony_ci mvneta_rxq_short_pool_set(pp, rxq); 33378c2ecf20Sopenharmony_ci mvneta_rxq_non_occup_desc_add(pp, rxq, rxq->size); 33388c2ecf20Sopenharmony_ci } 33398c2ecf20Sopenharmony_ci} 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_ci/* Create a specified RX queue */ 33428c2ecf20Sopenharmony_cistatic int mvneta_rxq_init(struct mvneta_port *pp, 33438c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 33448c2ecf20Sopenharmony_ci 33458c2ecf20Sopenharmony_ci{ 33468c2ecf20Sopenharmony_ci int ret; 33478c2ecf20Sopenharmony_ci 33488c2ecf20Sopenharmony_ci ret = mvneta_rxq_sw_init(pp, rxq); 33498c2ecf20Sopenharmony_ci if (ret < 0) 33508c2ecf20Sopenharmony_ci return ret; 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci mvneta_rxq_hw_init(pp, rxq); 33538c2ecf20Sopenharmony_ci 33548c2ecf20Sopenharmony_ci return 0; 33558c2ecf20Sopenharmony_ci} 33568c2ecf20Sopenharmony_ci 33578c2ecf20Sopenharmony_ci/* Cleanup Rx queue */ 33588c2ecf20Sopenharmony_cistatic void mvneta_rxq_deinit(struct mvneta_port *pp, 33598c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq) 33608c2ecf20Sopenharmony_ci{ 33618c2ecf20Sopenharmony_ci mvneta_rxq_drop_pkts(pp, rxq); 33628c2ecf20Sopenharmony_ci 33638c2ecf20Sopenharmony_ci if (rxq->descs) 33648c2ecf20Sopenharmony_ci dma_free_coherent(pp->dev->dev.parent, 33658c2ecf20Sopenharmony_ci rxq->size * MVNETA_DESC_ALIGNED_SIZE, 33668c2ecf20Sopenharmony_ci rxq->descs, 33678c2ecf20Sopenharmony_ci rxq->descs_phys); 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci rxq->descs = NULL; 33708c2ecf20Sopenharmony_ci rxq->last_desc = 0; 33718c2ecf20Sopenharmony_ci rxq->next_desc_to_proc = 0; 33728c2ecf20Sopenharmony_ci rxq->descs_phys = 0; 33738c2ecf20Sopenharmony_ci rxq->first_to_refill = 0; 33748c2ecf20Sopenharmony_ci rxq->refill_num = 0; 33758c2ecf20Sopenharmony_ci} 33768c2ecf20Sopenharmony_ci 33778c2ecf20Sopenharmony_cistatic int mvneta_txq_sw_init(struct mvneta_port *pp, 33788c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 33798c2ecf20Sopenharmony_ci{ 33808c2ecf20Sopenharmony_ci int cpu; 33818c2ecf20Sopenharmony_ci 33828c2ecf20Sopenharmony_ci txq->size = pp->tx_ring_size; 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci /* A queue must always have room for at least one skb. 33858c2ecf20Sopenharmony_ci * Therefore, stop the queue when the free entries reaches 33868c2ecf20Sopenharmony_ci * the maximum number of descriptors per skb. 33878c2ecf20Sopenharmony_ci */ 33888c2ecf20Sopenharmony_ci txq->tx_stop_threshold = txq->size - MVNETA_MAX_SKB_DESCS; 33898c2ecf20Sopenharmony_ci txq->tx_wake_threshold = txq->tx_stop_threshold / 2; 33908c2ecf20Sopenharmony_ci 33918c2ecf20Sopenharmony_ci /* Allocate memory for TX descriptors */ 33928c2ecf20Sopenharmony_ci txq->descs = dma_alloc_coherent(pp->dev->dev.parent, 33938c2ecf20Sopenharmony_ci txq->size * MVNETA_DESC_ALIGNED_SIZE, 33948c2ecf20Sopenharmony_ci &txq->descs_phys, GFP_KERNEL); 33958c2ecf20Sopenharmony_ci if (!txq->descs) 33968c2ecf20Sopenharmony_ci return -ENOMEM; 33978c2ecf20Sopenharmony_ci 33988c2ecf20Sopenharmony_ci txq->last_desc = txq->size - 1; 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_ci txq->buf = kmalloc_array(txq->size, sizeof(*txq->buf), GFP_KERNEL); 34018c2ecf20Sopenharmony_ci if (!txq->buf) 34028c2ecf20Sopenharmony_ci return -ENOMEM; 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci /* Allocate DMA buffers for TSO MAC/IP/TCP headers */ 34058c2ecf20Sopenharmony_ci txq->tso_hdrs = dma_alloc_coherent(pp->dev->dev.parent, 34068c2ecf20Sopenharmony_ci txq->size * TSO_HEADER_SIZE, 34078c2ecf20Sopenharmony_ci &txq->tso_hdrs_phys, GFP_KERNEL); 34088c2ecf20Sopenharmony_ci if (!txq->tso_hdrs) 34098c2ecf20Sopenharmony_ci return -ENOMEM; 34108c2ecf20Sopenharmony_ci 34118c2ecf20Sopenharmony_ci /* Setup XPS mapping */ 34128c2ecf20Sopenharmony_ci if (pp->neta_armada3700) 34138c2ecf20Sopenharmony_ci cpu = 0; 34148c2ecf20Sopenharmony_ci else if (txq_number > 1) 34158c2ecf20Sopenharmony_ci cpu = txq->id % num_present_cpus(); 34168c2ecf20Sopenharmony_ci else 34178c2ecf20Sopenharmony_ci cpu = pp->rxq_def % num_present_cpus(); 34188c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, &txq->affinity_mask); 34198c2ecf20Sopenharmony_ci netif_set_xps_queue(pp->dev, &txq->affinity_mask, txq->id); 34208c2ecf20Sopenharmony_ci 34218c2ecf20Sopenharmony_ci return 0; 34228c2ecf20Sopenharmony_ci} 34238c2ecf20Sopenharmony_ci 34248c2ecf20Sopenharmony_cistatic void mvneta_txq_hw_init(struct mvneta_port *pp, 34258c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 34268c2ecf20Sopenharmony_ci{ 34278c2ecf20Sopenharmony_ci /* Set maximum bandwidth for enabled TXQs */ 34288c2ecf20Sopenharmony_ci mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(txq->id), 0x03ffffff); 34298c2ecf20Sopenharmony_ci mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(txq->id), 0x3fffffff); 34308c2ecf20Sopenharmony_ci 34318c2ecf20Sopenharmony_ci /* Set Tx descriptors queue starting address */ 34328c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_BASE_ADDR_REG(txq->id), txq->descs_phys); 34338c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), txq->size); 34348c2ecf20Sopenharmony_ci 34358c2ecf20Sopenharmony_ci mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal); 34368c2ecf20Sopenharmony_ci} 34378c2ecf20Sopenharmony_ci 34388c2ecf20Sopenharmony_ci/* Create and initialize a tx queue */ 34398c2ecf20Sopenharmony_cistatic int mvneta_txq_init(struct mvneta_port *pp, 34408c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 34418c2ecf20Sopenharmony_ci{ 34428c2ecf20Sopenharmony_ci int ret; 34438c2ecf20Sopenharmony_ci 34448c2ecf20Sopenharmony_ci ret = mvneta_txq_sw_init(pp, txq); 34458c2ecf20Sopenharmony_ci if (ret < 0) 34468c2ecf20Sopenharmony_ci return ret; 34478c2ecf20Sopenharmony_ci 34488c2ecf20Sopenharmony_ci mvneta_txq_hw_init(pp, txq); 34498c2ecf20Sopenharmony_ci 34508c2ecf20Sopenharmony_ci return 0; 34518c2ecf20Sopenharmony_ci} 34528c2ecf20Sopenharmony_ci 34538c2ecf20Sopenharmony_ci/* Free allocated resources when mvneta_txq_init() fails to allocate memory*/ 34548c2ecf20Sopenharmony_cistatic void mvneta_txq_sw_deinit(struct mvneta_port *pp, 34558c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 34568c2ecf20Sopenharmony_ci{ 34578c2ecf20Sopenharmony_ci struct netdev_queue *nq = netdev_get_tx_queue(pp->dev, txq->id); 34588c2ecf20Sopenharmony_ci 34598c2ecf20Sopenharmony_ci kfree(txq->buf); 34608c2ecf20Sopenharmony_ci 34618c2ecf20Sopenharmony_ci if (txq->tso_hdrs) 34628c2ecf20Sopenharmony_ci dma_free_coherent(pp->dev->dev.parent, 34638c2ecf20Sopenharmony_ci txq->size * TSO_HEADER_SIZE, 34648c2ecf20Sopenharmony_ci txq->tso_hdrs, txq->tso_hdrs_phys); 34658c2ecf20Sopenharmony_ci if (txq->descs) 34668c2ecf20Sopenharmony_ci dma_free_coherent(pp->dev->dev.parent, 34678c2ecf20Sopenharmony_ci txq->size * MVNETA_DESC_ALIGNED_SIZE, 34688c2ecf20Sopenharmony_ci txq->descs, txq->descs_phys); 34698c2ecf20Sopenharmony_ci 34708c2ecf20Sopenharmony_ci netdev_tx_reset_queue(nq); 34718c2ecf20Sopenharmony_ci 34728c2ecf20Sopenharmony_ci txq->descs = NULL; 34738c2ecf20Sopenharmony_ci txq->last_desc = 0; 34748c2ecf20Sopenharmony_ci txq->next_desc_to_proc = 0; 34758c2ecf20Sopenharmony_ci txq->descs_phys = 0; 34768c2ecf20Sopenharmony_ci} 34778c2ecf20Sopenharmony_ci 34788c2ecf20Sopenharmony_cistatic void mvneta_txq_hw_deinit(struct mvneta_port *pp, 34798c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 34808c2ecf20Sopenharmony_ci{ 34818c2ecf20Sopenharmony_ci /* Set minimum bandwidth for disabled TXQs */ 34828c2ecf20Sopenharmony_ci mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(txq->id), 0); 34838c2ecf20Sopenharmony_ci mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(txq->id), 0); 34848c2ecf20Sopenharmony_ci 34858c2ecf20Sopenharmony_ci /* Set Tx descriptors queue starting address and size */ 34868c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_BASE_ADDR_REG(txq->id), 0); 34878c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), 0); 34888c2ecf20Sopenharmony_ci} 34898c2ecf20Sopenharmony_ci 34908c2ecf20Sopenharmony_cistatic void mvneta_txq_deinit(struct mvneta_port *pp, 34918c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq) 34928c2ecf20Sopenharmony_ci{ 34938c2ecf20Sopenharmony_ci mvneta_txq_sw_deinit(pp, txq); 34948c2ecf20Sopenharmony_ci mvneta_txq_hw_deinit(pp, txq); 34958c2ecf20Sopenharmony_ci} 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_ci/* Cleanup all Tx queues */ 34988c2ecf20Sopenharmony_cistatic void mvneta_cleanup_txqs(struct mvneta_port *pp) 34998c2ecf20Sopenharmony_ci{ 35008c2ecf20Sopenharmony_ci int queue; 35018c2ecf20Sopenharmony_ci 35028c2ecf20Sopenharmony_ci for (queue = 0; queue < txq_number; queue++) 35038c2ecf20Sopenharmony_ci mvneta_txq_deinit(pp, &pp->txqs[queue]); 35048c2ecf20Sopenharmony_ci} 35058c2ecf20Sopenharmony_ci 35068c2ecf20Sopenharmony_ci/* Cleanup all Rx queues */ 35078c2ecf20Sopenharmony_cistatic void mvneta_cleanup_rxqs(struct mvneta_port *pp) 35088c2ecf20Sopenharmony_ci{ 35098c2ecf20Sopenharmony_ci int queue; 35108c2ecf20Sopenharmony_ci 35118c2ecf20Sopenharmony_ci for (queue = 0; queue < rxq_number; queue++) 35128c2ecf20Sopenharmony_ci mvneta_rxq_deinit(pp, &pp->rxqs[queue]); 35138c2ecf20Sopenharmony_ci} 35148c2ecf20Sopenharmony_ci 35158c2ecf20Sopenharmony_ci 35168c2ecf20Sopenharmony_ci/* Init all Rx queues */ 35178c2ecf20Sopenharmony_cistatic int mvneta_setup_rxqs(struct mvneta_port *pp) 35188c2ecf20Sopenharmony_ci{ 35198c2ecf20Sopenharmony_ci int queue; 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci for (queue = 0; queue < rxq_number; queue++) { 35228c2ecf20Sopenharmony_ci int err = mvneta_rxq_init(pp, &pp->rxqs[queue]); 35238c2ecf20Sopenharmony_ci 35248c2ecf20Sopenharmony_ci if (err) { 35258c2ecf20Sopenharmony_ci netdev_err(pp->dev, "%s: can't create rxq=%d\n", 35268c2ecf20Sopenharmony_ci __func__, queue); 35278c2ecf20Sopenharmony_ci mvneta_cleanup_rxqs(pp); 35288c2ecf20Sopenharmony_ci return err; 35298c2ecf20Sopenharmony_ci } 35308c2ecf20Sopenharmony_ci } 35318c2ecf20Sopenharmony_ci 35328c2ecf20Sopenharmony_ci return 0; 35338c2ecf20Sopenharmony_ci} 35348c2ecf20Sopenharmony_ci 35358c2ecf20Sopenharmony_ci/* Init all tx queues */ 35368c2ecf20Sopenharmony_cistatic int mvneta_setup_txqs(struct mvneta_port *pp) 35378c2ecf20Sopenharmony_ci{ 35388c2ecf20Sopenharmony_ci int queue; 35398c2ecf20Sopenharmony_ci 35408c2ecf20Sopenharmony_ci for (queue = 0; queue < txq_number; queue++) { 35418c2ecf20Sopenharmony_ci int err = mvneta_txq_init(pp, &pp->txqs[queue]); 35428c2ecf20Sopenharmony_ci if (err) { 35438c2ecf20Sopenharmony_ci netdev_err(pp->dev, "%s: can't create txq=%d\n", 35448c2ecf20Sopenharmony_ci __func__, queue); 35458c2ecf20Sopenharmony_ci mvneta_cleanup_txqs(pp); 35468c2ecf20Sopenharmony_ci return err; 35478c2ecf20Sopenharmony_ci } 35488c2ecf20Sopenharmony_ci } 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci return 0; 35518c2ecf20Sopenharmony_ci} 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_cistatic int mvneta_comphy_init(struct mvneta_port *pp, phy_interface_t interface) 35548c2ecf20Sopenharmony_ci{ 35558c2ecf20Sopenharmony_ci int ret; 35568c2ecf20Sopenharmony_ci 35578c2ecf20Sopenharmony_ci ret = phy_set_mode_ext(pp->comphy, PHY_MODE_ETHERNET, interface); 35588c2ecf20Sopenharmony_ci if (ret) 35598c2ecf20Sopenharmony_ci return ret; 35608c2ecf20Sopenharmony_ci 35618c2ecf20Sopenharmony_ci return phy_power_on(pp->comphy); 35628c2ecf20Sopenharmony_ci} 35638c2ecf20Sopenharmony_ci 35648c2ecf20Sopenharmony_cistatic int mvneta_config_interface(struct mvneta_port *pp, 35658c2ecf20Sopenharmony_ci phy_interface_t interface) 35668c2ecf20Sopenharmony_ci{ 35678c2ecf20Sopenharmony_ci int ret = 0; 35688c2ecf20Sopenharmony_ci 35698c2ecf20Sopenharmony_ci if (pp->comphy) { 35708c2ecf20Sopenharmony_ci if (interface == PHY_INTERFACE_MODE_SGMII || 35718c2ecf20Sopenharmony_ci interface == PHY_INTERFACE_MODE_1000BASEX || 35728c2ecf20Sopenharmony_ci interface == PHY_INTERFACE_MODE_2500BASEX) { 35738c2ecf20Sopenharmony_ci ret = mvneta_comphy_init(pp, interface); 35748c2ecf20Sopenharmony_ci } 35758c2ecf20Sopenharmony_ci } else { 35768c2ecf20Sopenharmony_ci switch (interface) { 35778c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_QSGMII: 35788c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_SERDES_CFG, 35798c2ecf20Sopenharmony_ci MVNETA_QSGMII_SERDES_PROTO); 35808c2ecf20Sopenharmony_ci break; 35818c2ecf20Sopenharmony_ci 35828c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_SGMII: 35838c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_1000BASEX: 35848c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_SERDES_CFG, 35858c2ecf20Sopenharmony_ci MVNETA_SGMII_SERDES_PROTO); 35868c2ecf20Sopenharmony_ci break; 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_2500BASEX: 35898c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_SERDES_CFG, 35908c2ecf20Sopenharmony_ci MVNETA_HSGMII_SERDES_PROTO); 35918c2ecf20Sopenharmony_ci break; 35928c2ecf20Sopenharmony_ci default: 35938c2ecf20Sopenharmony_ci break; 35948c2ecf20Sopenharmony_ci } 35958c2ecf20Sopenharmony_ci } 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_ci pp->phy_interface = interface; 35988c2ecf20Sopenharmony_ci 35998c2ecf20Sopenharmony_ci return ret; 36008c2ecf20Sopenharmony_ci} 36018c2ecf20Sopenharmony_ci 36028c2ecf20Sopenharmony_cistatic void mvneta_start_dev(struct mvneta_port *pp) 36038c2ecf20Sopenharmony_ci{ 36048c2ecf20Sopenharmony_ci int cpu; 36058c2ecf20Sopenharmony_ci 36068c2ecf20Sopenharmony_ci WARN_ON(mvneta_config_interface(pp, pp->phy_interface)); 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ci mvneta_max_rx_size_set(pp, pp->pkt_size); 36098c2ecf20Sopenharmony_ci mvneta_txq_max_tx_size_set(pp, pp->pkt_size); 36108c2ecf20Sopenharmony_ci 36118c2ecf20Sopenharmony_ci /* start the Rx/Tx activity */ 36128c2ecf20Sopenharmony_ci mvneta_port_enable(pp); 36138c2ecf20Sopenharmony_ci 36148c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) { 36158c2ecf20Sopenharmony_ci /* Enable polling on the port */ 36168c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) { 36178c2ecf20Sopenharmony_ci struct mvneta_pcpu_port *port = 36188c2ecf20Sopenharmony_ci per_cpu_ptr(pp->ports, cpu); 36198c2ecf20Sopenharmony_ci 36208c2ecf20Sopenharmony_ci napi_enable(&port->napi); 36218c2ecf20Sopenharmony_ci } 36228c2ecf20Sopenharmony_ci } else { 36238c2ecf20Sopenharmony_ci napi_enable(&pp->napi); 36248c2ecf20Sopenharmony_ci } 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci /* Unmask interrupts. It has to be done from each CPU */ 36278c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true); 36288c2ecf20Sopenharmony_ci 36298c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_MISC_MASK, 36308c2ecf20Sopenharmony_ci MVNETA_CAUSE_PHY_STATUS_CHANGE | 36318c2ecf20Sopenharmony_ci MVNETA_CAUSE_LINK_CHANGE); 36328c2ecf20Sopenharmony_ci 36338c2ecf20Sopenharmony_ci phylink_start(pp->phylink); 36348c2ecf20Sopenharmony_ci 36358c2ecf20Sopenharmony_ci /* We may have called phylink_speed_down before */ 36368c2ecf20Sopenharmony_ci phylink_speed_up(pp->phylink); 36378c2ecf20Sopenharmony_ci 36388c2ecf20Sopenharmony_ci netif_tx_start_all_queues(pp->dev); 36398c2ecf20Sopenharmony_ci 36408c2ecf20Sopenharmony_ci clear_bit(__MVNETA_DOWN, &pp->state); 36418c2ecf20Sopenharmony_ci} 36428c2ecf20Sopenharmony_ci 36438c2ecf20Sopenharmony_cistatic void mvneta_stop_dev(struct mvneta_port *pp) 36448c2ecf20Sopenharmony_ci{ 36458c2ecf20Sopenharmony_ci unsigned int cpu; 36468c2ecf20Sopenharmony_ci 36478c2ecf20Sopenharmony_ci set_bit(__MVNETA_DOWN, &pp->state); 36488c2ecf20Sopenharmony_ci 36498c2ecf20Sopenharmony_ci if (device_may_wakeup(&pp->dev->dev)) 36508c2ecf20Sopenharmony_ci phylink_speed_down(pp->phylink, false); 36518c2ecf20Sopenharmony_ci 36528c2ecf20Sopenharmony_ci phylink_stop(pp->phylink); 36538c2ecf20Sopenharmony_ci 36548c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) { 36558c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) { 36568c2ecf20Sopenharmony_ci struct mvneta_pcpu_port *port = 36578c2ecf20Sopenharmony_ci per_cpu_ptr(pp->ports, cpu); 36588c2ecf20Sopenharmony_ci 36598c2ecf20Sopenharmony_ci napi_disable(&port->napi); 36608c2ecf20Sopenharmony_ci } 36618c2ecf20Sopenharmony_ci } else { 36628c2ecf20Sopenharmony_ci napi_disable(&pp->napi); 36638c2ecf20Sopenharmony_ci } 36648c2ecf20Sopenharmony_ci 36658c2ecf20Sopenharmony_ci netif_carrier_off(pp->dev); 36668c2ecf20Sopenharmony_ci 36678c2ecf20Sopenharmony_ci mvneta_port_down(pp); 36688c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(pp->dev); 36698c2ecf20Sopenharmony_ci 36708c2ecf20Sopenharmony_ci /* Stop the port activity */ 36718c2ecf20Sopenharmony_ci mvneta_port_disable(pp); 36728c2ecf20Sopenharmony_ci 36738c2ecf20Sopenharmony_ci /* Clear all ethernet port interrupts */ 36748c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_clear_intr_cause, pp, true); 36758c2ecf20Sopenharmony_ci 36768c2ecf20Sopenharmony_ci /* Mask all ethernet port interrupts */ 36778c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_mask_interrupt, pp, true); 36788c2ecf20Sopenharmony_ci 36798c2ecf20Sopenharmony_ci mvneta_tx_reset(pp); 36808c2ecf20Sopenharmony_ci mvneta_rx_reset(pp); 36818c2ecf20Sopenharmony_ci 36828c2ecf20Sopenharmony_ci WARN_ON(phy_power_off(pp->comphy)); 36838c2ecf20Sopenharmony_ci} 36848c2ecf20Sopenharmony_ci 36858c2ecf20Sopenharmony_cistatic void mvneta_percpu_enable(void *arg) 36868c2ecf20Sopenharmony_ci{ 36878c2ecf20Sopenharmony_ci struct mvneta_port *pp = arg; 36888c2ecf20Sopenharmony_ci 36898c2ecf20Sopenharmony_ci enable_percpu_irq(pp->dev->irq, IRQ_TYPE_NONE); 36908c2ecf20Sopenharmony_ci} 36918c2ecf20Sopenharmony_ci 36928c2ecf20Sopenharmony_cistatic void mvneta_percpu_disable(void *arg) 36938c2ecf20Sopenharmony_ci{ 36948c2ecf20Sopenharmony_ci struct mvneta_port *pp = arg; 36958c2ecf20Sopenharmony_ci 36968c2ecf20Sopenharmony_ci disable_percpu_irq(pp->dev->irq); 36978c2ecf20Sopenharmony_ci} 36988c2ecf20Sopenharmony_ci 36998c2ecf20Sopenharmony_ci/* Change the device mtu */ 37008c2ecf20Sopenharmony_cistatic int mvneta_change_mtu(struct net_device *dev, int mtu) 37018c2ecf20Sopenharmony_ci{ 37028c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 37038c2ecf20Sopenharmony_ci int ret; 37048c2ecf20Sopenharmony_ci 37058c2ecf20Sopenharmony_ci if (!IS_ALIGNED(MVNETA_RX_PKT_SIZE(mtu), 8)) { 37068c2ecf20Sopenharmony_ci netdev_info(dev, "Illegal MTU value %d, rounding to %d\n", 37078c2ecf20Sopenharmony_ci mtu, ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8)); 37088c2ecf20Sopenharmony_ci mtu = ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8); 37098c2ecf20Sopenharmony_ci } 37108c2ecf20Sopenharmony_ci 37118c2ecf20Sopenharmony_ci if (pp->xdp_prog && mtu > MVNETA_MAX_RX_BUF_SIZE) { 37128c2ecf20Sopenharmony_ci netdev_info(dev, "Illegal MTU value %d for XDP mode\n", mtu); 37138c2ecf20Sopenharmony_ci return -EINVAL; 37148c2ecf20Sopenharmony_ci } 37158c2ecf20Sopenharmony_ci 37168c2ecf20Sopenharmony_ci dev->mtu = mtu; 37178c2ecf20Sopenharmony_ci 37188c2ecf20Sopenharmony_ci if (!netif_running(dev)) { 37198c2ecf20Sopenharmony_ci if (pp->bm_priv) 37208c2ecf20Sopenharmony_ci mvneta_bm_update_mtu(pp, mtu); 37218c2ecf20Sopenharmony_ci 37228c2ecf20Sopenharmony_ci netdev_update_features(dev); 37238c2ecf20Sopenharmony_ci return 0; 37248c2ecf20Sopenharmony_ci } 37258c2ecf20Sopenharmony_ci 37268c2ecf20Sopenharmony_ci /* The interface is running, so we have to force a 37278c2ecf20Sopenharmony_ci * reallocation of the queues 37288c2ecf20Sopenharmony_ci */ 37298c2ecf20Sopenharmony_ci mvneta_stop_dev(pp); 37308c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_disable, pp, true); 37318c2ecf20Sopenharmony_ci 37328c2ecf20Sopenharmony_ci mvneta_cleanup_txqs(pp); 37338c2ecf20Sopenharmony_ci mvneta_cleanup_rxqs(pp); 37348c2ecf20Sopenharmony_ci 37358c2ecf20Sopenharmony_ci if (pp->bm_priv) 37368c2ecf20Sopenharmony_ci mvneta_bm_update_mtu(pp, mtu); 37378c2ecf20Sopenharmony_ci 37388c2ecf20Sopenharmony_ci pp->pkt_size = MVNETA_RX_PKT_SIZE(dev->mtu); 37398c2ecf20Sopenharmony_ci 37408c2ecf20Sopenharmony_ci ret = mvneta_setup_rxqs(pp); 37418c2ecf20Sopenharmony_ci if (ret) { 37428c2ecf20Sopenharmony_ci netdev_err(dev, "unable to setup rxqs after MTU change\n"); 37438c2ecf20Sopenharmony_ci return ret; 37448c2ecf20Sopenharmony_ci } 37458c2ecf20Sopenharmony_ci 37468c2ecf20Sopenharmony_ci ret = mvneta_setup_txqs(pp); 37478c2ecf20Sopenharmony_ci if (ret) { 37488c2ecf20Sopenharmony_ci netdev_err(dev, "unable to setup txqs after MTU change\n"); 37498c2ecf20Sopenharmony_ci return ret; 37508c2ecf20Sopenharmony_ci } 37518c2ecf20Sopenharmony_ci 37528c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_enable, pp, true); 37538c2ecf20Sopenharmony_ci mvneta_start_dev(pp); 37548c2ecf20Sopenharmony_ci 37558c2ecf20Sopenharmony_ci netdev_update_features(dev); 37568c2ecf20Sopenharmony_ci 37578c2ecf20Sopenharmony_ci return 0; 37588c2ecf20Sopenharmony_ci} 37598c2ecf20Sopenharmony_ci 37608c2ecf20Sopenharmony_cistatic netdev_features_t mvneta_fix_features(struct net_device *dev, 37618c2ecf20Sopenharmony_ci netdev_features_t features) 37628c2ecf20Sopenharmony_ci{ 37638c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 37648c2ecf20Sopenharmony_ci 37658c2ecf20Sopenharmony_ci if (pp->tx_csum_limit && dev->mtu > pp->tx_csum_limit) { 37668c2ecf20Sopenharmony_ci features &= ~(NETIF_F_IP_CSUM | NETIF_F_TSO); 37678c2ecf20Sopenharmony_ci netdev_info(dev, 37688c2ecf20Sopenharmony_ci "Disable IP checksum for MTU greater than %dB\n", 37698c2ecf20Sopenharmony_ci pp->tx_csum_limit); 37708c2ecf20Sopenharmony_ci } 37718c2ecf20Sopenharmony_ci 37728c2ecf20Sopenharmony_ci return features; 37738c2ecf20Sopenharmony_ci} 37748c2ecf20Sopenharmony_ci 37758c2ecf20Sopenharmony_ci/* Get mac address */ 37768c2ecf20Sopenharmony_cistatic void mvneta_get_mac_addr(struct mvneta_port *pp, unsigned char *addr) 37778c2ecf20Sopenharmony_ci{ 37788c2ecf20Sopenharmony_ci u32 mac_addr_l, mac_addr_h; 37798c2ecf20Sopenharmony_ci 37808c2ecf20Sopenharmony_ci mac_addr_l = mvreg_read(pp, MVNETA_MAC_ADDR_LOW); 37818c2ecf20Sopenharmony_ci mac_addr_h = mvreg_read(pp, MVNETA_MAC_ADDR_HIGH); 37828c2ecf20Sopenharmony_ci addr[0] = (mac_addr_h >> 24) & 0xFF; 37838c2ecf20Sopenharmony_ci addr[1] = (mac_addr_h >> 16) & 0xFF; 37848c2ecf20Sopenharmony_ci addr[2] = (mac_addr_h >> 8) & 0xFF; 37858c2ecf20Sopenharmony_ci addr[3] = mac_addr_h & 0xFF; 37868c2ecf20Sopenharmony_ci addr[4] = (mac_addr_l >> 8) & 0xFF; 37878c2ecf20Sopenharmony_ci addr[5] = mac_addr_l & 0xFF; 37888c2ecf20Sopenharmony_ci} 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_ci/* Handle setting mac address */ 37918c2ecf20Sopenharmony_cistatic int mvneta_set_mac_addr(struct net_device *dev, void *addr) 37928c2ecf20Sopenharmony_ci{ 37938c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 37948c2ecf20Sopenharmony_ci struct sockaddr *sockaddr = addr; 37958c2ecf20Sopenharmony_ci int ret; 37968c2ecf20Sopenharmony_ci 37978c2ecf20Sopenharmony_ci ret = eth_prepare_mac_addr_change(dev, addr); 37988c2ecf20Sopenharmony_ci if (ret < 0) 37998c2ecf20Sopenharmony_ci return ret; 38008c2ecf20Sopenharmony_ci /* Remove previous address table entry */ 38018c2ecf20Sopenharmony_ci mvneta_mac_addr_set(pp, dev->dev_addr, -1); 38028c2ecf20Sopenharmony_ci 38038c2ecf20Sopenharmony_ci /* Set new addr in hw */ 38048c2ecf20Sopenharmony_ci mvneta_mac_addr_set(pp, sockaddr->sa_data, pp->rxq_def); 38058c2ecf20Sopenharmony_ci 38068c2ecf20Sopenharmony_ci eth_commit_mac_addr_change(dev, addr); 38078c2ecf20Sopenharmony_ci return 0; 38088c2ecf20Sopenharmony_ci} 38098c2ecf20Sopenharmony_ci 38108c2ecf20Sopenharmony_cistatic void mvneta_validate(struct phylink_config *config, 38118c2ecf20Sopenharmony_ci unsigned long *supported, 38128c2ecf20Sopenharmony_ci struct phylink_link_state *state) 38138c2ecf20Sopenharmony_ci{ 38148c2ecf20Sopenharmony_ci struct net_device *ndev = to_net_dev(config->dev); 38158c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(ndev); 38168c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; 38178c2ecf20Sopenharmony_ci 38188c2ecf20Sopenharmony_ci /* We only support QSGMII, SGMII, 802.3z and RGMII modes */ 38198c2ecf20Sopenharmony_ci if (state->interface != PHY_INTERFACE_MODE_NA && 38208c2ecf20Sopenharmony_ci state->interface != PHY_INTERFACE_MODE_QSGMII && 38218c2ecf20Sopenharmony_ci state->interface != PHY_INTERFACE_MODE_SGMII && 38228c2ecf20Sopenharmony_ci !phy_interface_mode_is_8023z(state->interface) && 38238c2ecf20Sopenharmony_ci !phy_interface_mode_is_rgmii(state->interface)) { 38248c2ecf20Sopenharmony_ci bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); 38258c2ecf20Sopenharmony_ci return; 38268c2ecf20Sopenharmony_ci } 38278c2ecf20Sopenharmony_ci 38288c2ecf20Sopenharmony_ci /* Allow all the expected bits */ 38298c2ecf20Sopenharmony_ci phylink_set(mask, Autoneg); 38308c2ecf20Sopenharmony_ci phylink_set_port_modes(mask); 38318c2ecf20Sopenharmony_ci 38328c2ecf20Sopenharmony_ci /* Asymmetric pause is unsupported */ 38338c2ecf20Sopenharmony_ci phylink_set(mask, Pause); 38348c2ecf20Sopenharmony_ci 38358c2ecf20Sopenharmony_ci /* Half-duplex at speeds higher than 100Mbit is unsupported */ 38368c2ecf20Sopenharmony_ci if (pp->comphy || state->interface != PHY_INTERFACE_MODE_2500BASEX) { 38378c2ecf20Sopenharmony_ci phylink_set(mask, 1000baseT_Full); 38388c2ecf20Sopenharmony_ci phylink_set(mask, 1000baseX_Full); 38398c2ecf20Sopenharmony_ci } 38408c2ecf20Sopenharmony_ci if (pp->comphy || state->interface == PHY_INTERFACE_MODE_2500BASEX) { 38418c2ecf20Sopenharmony_ci phylink_set(mask, 2500baseT_Full); 38428c2ecf20Sopenharmony_ci phylink_set(mask, 2500baseX_Full); 38438c2ecf20Sopenharmony_ci } 38448c2ecf20Sopenharmony_ci 38458c2ecf20Sopenharmony_ci if (!phy_interface_mode_is_8023z(state->interface)) { 38468c2ecf20Sopenharmony_ci /* 10M and 100M are only supported in non-802.3z mode */ 38478c2ecf20Sopenharmony_ci phylink_set(mask, 10baseT_Half); 38488c2ecf20Sopenharmony_ci phylink_set(mask, 10baseT_Full); 38498c2ecf20Sopenharmony_ci phylink_set(mask, 100baseT_Half); 38508c2ecf20Sopenharmony_ci phylink_set(mask, 100baseT_Full); 38518c2ecf20Sopenharmony_ci } 38528c2ecf20Sopenharmony_ci 38538c2ecf20Sopenharmony_ci bitmap_and(supported, supported, mask, 38548c2ecf20Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS); 38558c2ecf20Sopenharmony_ci bitmap_and(state->advertising, state->advertising, mask, 38568c2ecf20Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS); 38578c2ecf20Sopenharmony_ci 38588c2ecf20Sopenharmony_ci /* We can only operate at 2500BaseX or 1000BaseX. If requested 38598c2ecf20Sopenharmony_ci * to advertise both, only report advertising at 2500BaseX. 38608c2ecf20Sopenharmony_ci */ 38618c2ecf20Sopenharmony_ci phylink_helper_basex_speed(state); 38628c2ecf20Sopenharmony_ci} 38638c2ecf20Sopenharmony_ci 38648c2ecf20Sopenharmony_cistatic void mvneta_mac_pcs_get_state(struct phylink_config *config, 38658c2ecf20Sopenharmony_ci struct phylink_link_state *state) 38668c2ecf20Sopenharmony_ci{ 38678c2ecf20Sopenharmony_ci struct net_device *ndev = to_net_dev(config->dev); 38688c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(ndev); 38698c2ecf20Sopenharmony_ci u32 gmac_stat; 38708c2ecf20Sopenharmony_ci 38718c2ecf20Sopenharmony_ci gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); 38728c2ecf20Sopenharmony_ci 38738c2ecf20Sopenharmony_ci if (gmac_stat & MVNETA_GMAC_SPEED_1000) 38748c2ecf20Sopenharmony_ci state->speed = 38758c2ecf20Sopenharmony_ci state->interface == PHY_INTERFACE_MODE_2500BASEX ? 38768c2ecf20Sopenharmony_ci SPEED_2500 : SPEED_1000; 38778c2ecf20Sopenharmony_ci else if (gmac_stat & MVNETA_GMAC_SPEED_100) 38788c2ecf20Sopenharmony_ci state->speed = SPEED_100; 38798c2ecf20Sopenharmony_ci else 38808c2ecf20Sopenharmony_ci state->speed = SPEED_10; 38818c2ecf20Sopenharmony_ci 38828c2ecf20Sopenharmony_ci state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); 38838c2ecf20Sopenharmony_ci state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); 38848c2ecf20Sopenharmony_ci state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_ci state->pause = 0; 38878c2ecf20Sopenharmony_ci if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) 38888c2ecf20Sopenharmony_ci state->pause |= MLO_PAUSE_RX; 38898c2ecf20Sopenharmony_ci if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) 38908c2ecf20Sopenharmony_ci state->pause |= MLO_PAUSE_TX; 38918c2ecf20Sopenharmony_ci} 38928c2ecf20Sopenharmony_ci 38938c2ecf20Sopenharmony_cistatic void mvneta_mac_an_restart(struct phylink_config *config) 38948c2ecf20Sopenharmony_ci{ 38958c2ecf20Sopenharmony_ci struct net_device *ndev = to_net_dev(config->dev); 38968c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(ndev); 38978c2ecf20Sopenharmony_ci u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); 38988c2ecf20Sopenharmony_ci 38998c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, 39008c2ecf20Sopenharmony_ci gmac_an | MVNETA_GMAC_INBAND_RESTART_AN); 39018c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, 39028c2ecf20Sopenharmony_ci gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); 39038c2ecf20Sopenharmony_ci} 39048c2ecf20Sopenharmony_ci 39058c2ecf20Sopenharmony_cistatic void mvneta_mac_config(struct phylink_config *config, unsigned int mode, 39068c2ecf20Sopenharmony_ci const struct phylink_link_state *state) 39078c2ecf20Sopenharmony_ci{ 39088c2ecf20Sopenharmony_ci struct net_device *ndev = to_net_dev(config->dev); 39098c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(ndev); 39108c2ecf20Sopenharmony_ci u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); 39118c2ecf20Sopenharmony_ci u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); 39128c2ecf20Sopenharmony_ci u32 new_ctrl4, gmac_ctrl4 = mvreg_read(pp, MVNETA_GMAC_CTRL_4); 39138c2ecf20Sopenharmony_ci u32 new_clk, gmac_clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); 39148c2ecf20Sopenharmony_ci u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); 39158c2ecf20Sopenharmony_ci 39168c2ecf20Sopenharmony_ci new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; 39178c2ecf20Sopenharmony_ci new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE | 39188c2ecf20Sopenharmony_ci MVNETA_GMAC2_PORT_RESET); 39198c2ecf20Sopenharmony_ci new_ctrl4 = gmac_ctrl4 & ~(MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE); 39208c2ecf20Sopenharmony_ci new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE; 39218c2ecf20Sopenharmony_ci new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE | 39228c2ecf20Sopenharmony_ci MVNETA_GMAC_INBAND_RESTART_AN | 39238c2ecf20Sopenharmony_ci MVNETA_GMAC_AN_SPEED_EN | 39248c2ecf20Sopenharmony_ci MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL | 39258c2ecf20Sopenharmony_ci MVNETA_GMAC_AN_FLOW_CTRL_EN | 39268c2ecf20Sopenharmony_ci MVNETA_GMAC_AN_DUPLEX_EN); 39278c2ecf20Sopenharmony_ci 39288c2ecf20Sopenharmony_ci /* Even though it might look weird, when we're configured in 39298c2ecf20Sopenharmony_ci * SGMII or QSGMII mode, the RGMII bit needs to be set. 39308c2ecf20Sopenharmony_ci */ 39318c2ecf20Sopenharmony_ci new_ctrl2 |= MVNETA_GMAC2_PORT_RGMII; 39328c2ecf20Sopenharmony_ci 39338c2ecf20Sopenharmony_ci if (state->interface == PHY_INTERFACE_MODE_QSGMII || 39348c2ecf20Sopenharmony_ci state->interface == PHY_INTERFACE_MODE_SGMII || 39358c2ecf20Sopenharmony_ci phy_interface_mode_is_8023z(state->interface)) 39368c2ecf20Sopenharmony_ci new_ctrl2 |= MVNETA_GMAC2_PCS_ENABLE; 39378c2ecf20Sopenharmony_ci 39388c2ecf20Sopenharmony_ci if (phylink_test(state->advertising, Pause)) 39398c2ecf20Sopenharmony_ci new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; 39408c2ecf20Sopenharmony_ci 39418c2ecf20Sopenharmony_ci if (!phylink_autoneg_inband(mode)) { 39428c2ecf20Sopenharmony_ci /* Phy or fixed speed - nothing to do, leave the 39438c2ecf20Sopenharmony_ci * configured speed, duplex and flow control as-is. 39448c2ecf20Sopenharmony_ci */ 39458c2ecf20Sopenharmony_ci } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { 39468c2ecf20Sopenharmony_ci /* SGMII mode receives the state from the PHY */ 39478c2ecf20Sopenharmony_ci new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; 39488c2ecf20Sopenharmony_ci new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; 39498c2ecf20Sopenharmony_ci new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | 39508c2ecf20Sopenharmony_ci MVNETA_GMAC_FORCE_LINK_PASS | 39518c2ecf20Sopenharmony_ci MVNETA_GMAC_CONFIG_MII_SPEED | 39528c2ecf20Sopenharmony_ci MVNETA_GMAC_CONFIG_GMII_SPEED | 39538c2ecf20Sopenharmony_ci MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | 39548c2ecf20Sopenharmony_ci MVNETA_GMAC_INBAND_AN_ENABLE | 39558c2ecf20Sopenharmony_ci MVNETA_GMAC_AN_SPEED_EN | 39568c2ecf20Sopenharmony_ci MVNETA_GMAC_AN_DUPLEX_EN; 39578c2ecf20Sopenharmony_ci } else { 39588c2ecf20Sopenharmony_ci /* 802.3z negotiation - only 1000base-X */ 39598c2ecf20Sopenharmony_ci new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; 39608c2ecf20Sopenharmony_ci new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; 39618c2ecf20Sopenharmony_ci new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | 39628c2ecf20Sopenharmony_ci MVNETA_GMAC_FORCE_LINK_PASS | 39638c2ecf20Sopenharmony_ci MVNETA_GMAC_CONFIG_MII_SPEED)) | 39648c2ecf20Sopenharmony_ci MVNETA_GMAC_INBAND_AN_ENABLE | 39658c2ecf20Sopenharmony_ci MVNETA_GMAC_CONFIG_GMII_SPEED | 39668c2ecf20Sopenharmony_ci /* The MAC only supports FD mode */ 39678c2ecf20Sopenharmony_ci MVNETA_GMAC_CONFIG_FULL_DUPLEX; 39688c2ecf20Sopenharmony_ci 39698c2ecf20Sopenharmony_ci if (state->pause & MLO_PAUSE_AN && state->an_enabled) 39708c2ecf20Sopenharmony_ci new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; 39718c2ecf20Sopenharmony_ci } 39728c2ecf20Sopenharmony_ci 39738c2ecf20Sopenharmony_ci /* Armada 370 documentation says we can only change the port mode 39748c2ecf20Sopenharmony_ci * and in-band enable when the link is down, so force it down 39758c2ecf20Sopenharmony_ci * while making these changes. We also do this for GMAC_CTRL2 */ 39768c2ecf20Sopenharmony_ci if ((new_ctrl0 ^ gmac_ctrl0) & MVNETA_GMAC0_PORT_1000BASE_X || 39778c2ecf20Sopenharmony_ci (new_ctrl2 ^ gmac_ctrl2) & MVNETA_GMAC2_INBAND_AN_ENABLE || 39788c2ecf20Sopenharmony_ci (new_an ^ gmac_an) & MVNETA_GMAC_INBAND_AN_ENABLE) { 39798c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, 39808c2ecf20Sopenharmony_ci (gmac_an & ~MVNETA_GMAC_FORCE_LINK_PASS) | 39818c2ecf20Sopenharmony_ci MVNETA_GMAC_FORCE_LINK_DOWN); 39828c2ecf20Sopenharmony_ci } 39838c2ecf20Sopenharmony_ci 39848c2ecf20Sopenharmony_ci 39858c2ecf20Sopenharmony_ci /* When at 2.5G, the link partner can send frames with shortened 39868c2ecf20Sopenharmony_ci * preambles. 39878c2ecf20Sopenharmony_ci */ 39888c2ecf20Sopenharmony_ci if (state->interface == PHY_INTERFACE_MODE_2500BASEX) 39898c2ecf20Sopenharmony_ci new_ctrl4 |= MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE; 39908c2ecf20Sopenharmony_ci 39918c2ecf20Sopenharmony_ci if (pp->phy_interface != state->interface) { 39928c2ecf20Sopenharmony_ci if (pp->comphy) 39938c2ecf20Sopenharmony_ci WARN_ON(phy_power_off(pp->comphy)); 39948c2ecf20Sopenharmony_ci WARN_ON(mvneta_config_interface(pp, state->interface)); 39958c2ecf20Sopenharmony_ci } 39968c2ecf20Sopenharmony_ci 39978c2ecf20Sopenharmony_ci if (new_ctrl0 != gmac_ctrl0) 39988c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0); 39998c2ecf20Sopenharmony_ci if (new_ctrl2 != gmac_ctrl2) 40008c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); 40018c2ecf20Sopenharmony_ci if (new_ctrl4 != gmac_ctrl4) 40028c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_CTRL_4, new_ctrl4); 40038c2ecf20Sopenharmony_ci if (new_clk != gmac_clk) 40048c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, new_clk); 40058c2ecf20Sopenharmony_ci if (new_an != gmac_an) 40068c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an); 40078c2ecf20Sopenharmony_ci 40088c2ecf20Sopenharmony_ci if (gmac_ctrl2 & MVNETA_GMAC2_PORT_RESET) { 40098c2ecf20Sopenharmony_ci while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) & 40108c2ecf20Sopenharmony_ci MVNETA_GMAC2_PORT_RESET) != 0) 40118c2ecf20Sopenharmony_ci continue; 40128c2ecf20Sopenharmony_ci } 40138c2ecf20Sopenharmony_ci} 40148c2ecf20Sopenharmony_ci 40158c2ecf20Sopenharmony_cistatic void mvneta_set_eee(struct mvneta_port *pp, bool enable) 40168c2ecf20Sopenharmony_ci{ 40178c2ecf20Sopenharmony_ci u32 lpi_ctl1; 40188c2ecf20Sopenharmony_ci 40198c2ecf20Sopenharmony_ci lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1); 40208c2ecf20Sopenharmony_ci if (enable) 40218c2ecf20Sopenharmony_ci lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE; 40228c2ecf20Sopenharmony_ci else 40238c2ecf20Sopenharmony_ci lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE; 40248c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1); 40258c2ecf20Sopenharmony_ci} 40268c2ecf20Sopenharmony_ci 40278c2ecf20Sopenharmony_cistatic void mvneta_mac_link_down(struct phylink_config *config, 40288c2ecf20Sopenharmony_ci unsigned int mode, phy_interface_t interface) 40298c2ecf20Sopenharmony_ci{ 40308c2ecf20Sopenharmony_ci struct net_device *ndev = to_net_dev(config->dev); 40318c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(ndev); 40328c2ecf20Sopenharmony_ci u32 val; 40338c2ecf20Sopenharmony_ci 40348c2ecf20Sopenharmony_ci mvneta_port_down(pp); 40358c2ecf20Sopenharmony_ci 40368c2ecf20Sopenharmony_ci if (!phylink_autoneg_inband(mode)) { 40378c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); 40388c2ecf20Sopenharmony_ci val &= ~MVNETA_GMAC_FORCE_LINK_PASS; 40398c2ecf20Sopenharmony_ci val |= MVNETA_GMAC_FORCE_LINK_DOWN; 40408c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); 40418c2ecf20Sopenharmony_ci } 40428c2ecf20Sopenharmony_ci 40438c2ecf20Sopenharmony_ci pp->eee_active = false; 40448c2ecf20Sopenharmony_ci mvneta_set_eee(pp, false); 40458c2ecf20Sopenharmony_ci} 40468c2ecf20Sopenharmony_ci 40478c2ecf20Sopenharmony_cistatic void mvneta_mac_link_up(struct phylink_config *config, 40488c2ecf20Sopenharmony_ci struct phy_device *phy, 40498c2ecf20Sopenharmony_ci unsigned int mode, phy_interface_t interface, 40508c2ecf20Sopenharmony_ci int speed, int duplex, 40518c2ecf20Sopenharmony_ci bool tx_pause, bool rx_pause) 40528c2ecf20Sopenharmony_ci{ 40538c2ecf20Sopenharmony_ci struct net_device *ndev = to_net_dev(config->dev); 40548c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(ndev); 40558c2ecf20Sopenharmony_ci u32 val; 40568c2ecf20Sopenharmony_ci 40578c2ecf20Sopenharmony_ci if (!phylink_autoneg_inband(mode)) { 40588c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); 40598c2ecf20Sopenharmony_ci val &= ~(MVNETA_GMAC_FORCE_LINK_DOWN | 40608c2ecf20Sopenharmony_ci MVNETA_GMAC_CONFIG_MII_SPEED | 40618c2ecf20Sopenharmony_ci MVNETA_GMAC_CONFIG_GMII_SPEED | 40628c2ecf20Sopenharmony_ci MVNETA_GMAC_CONFIG_FLOW_CTRL | 40638c2ecf20Sopenharmony_ci MVNETA_GMAC_CONFIG_FULL_DUPLEX); 40648c2ecf20Sopenharmony_ci val |= MVNETA_GMAC_FORCE_LINK_PASS; 40658c2ecf20Sopenharmony_ci 40668c2ecf20Sopenharmony_ci if (speed == SPEED_1000 || speed == SPEED_2500) 40678c2ecf20Sopenharmony_ci val |= MVNETA_GMAC_CONFIG_GMII_SPEED; 40688c2ecf20Sopenharmony_ci else if (speed == SPEED_100) 40698c2ecf20Sopenharmony_ci val |= MVNETA_GMAC_CONFIG_MII_SPEED; 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_ci if (duplex == DUPLEX_FULL) 40728c2ecf20Sopenharmony_ci val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; 40738c2ecf20Sopenharmony_ci 40748c2ecf20Sopenharmony_ci if (tx_pause || rx_pause) 40758c2ecf20Sopenharmony_ci val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; 40768c2ecf20Sopenharmony_ci 40778c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); 40788c2ecf20Sopenharmony_ci } else { 40798c2ecf20Sopenharmony_ci /* When inband doesn't cover flow control or flow control is 40808c2ecf20Sopenharmony_ci * disabled, we need to manually configure it. This bit will 40818c2ecf20Sopenharmony_ci * only have effect if MVNETA_GMAC_AN_FLOW_CTRL_EN is unset. 40828c2ecf20Sopenharmony_ci */ 40838c2ecf20Sopenharmony_ci val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); 40848c2ecf20Sopenharmony_ci val &= ~MVNETA_GMAC_CONFIG_FLOW_CTRL; 40858c2ecf20Sopenharmony_ci 40868c2ecf20Sopenharmony_ci if (tx_pause || rx_pause) 40878c2ecf20Sopenharmony_ci val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; 40888c2ecf20Sopenharmony_ci 40898c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); 40908c2ecf20Sopenharmony_ci } 40918c2ecf20Sopenharmony_ci 40928c2ecf20Sopenharmony_ci mvneta_port_up(pp); 40938c2ecf20Sopenharmony_ci 40948c2ecf20Sopenharmony_ci if (phy && pp->eee_enabled) { 40958c2ecf20Sopenharmony_ci pp->eee_active = phy_init_eee(phy, 0) >= 0; 40968c2ecf20Sopenharmony_ci mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled); 40978c2ecf20Sopenharmony_ci } 40988c2ecf20Sopenharmony_ci} 40998c2ecf20Sopenharmony_ci 41008c2ecf20Sopenharmony_cistatic const struct phylink_mac_ops mvneta_phylink_ops = { 41018c2ecf20Sopenharmony_ci .validate = mvneta_validate, 41028c2ecf20Sopenharmony_ci .mac_pcs_get_state = mvneta_mac_pcs_get_state, 41038c2ecf20Sopenharmony_ci .mac_an_restart = mvneta_mac_an_restart, 41048c2ecf20Sopenharmony_ci .mac_config = mvneta_mac_config, 41058c2ecf20Sopenharmony_ci .mac_link_down = mvneta_mac_link_down, 41068c2ecf20Sopenharmony_ci .mac_link_up = mvneta_mac_link_up, 41078c2ecf20Sopenharmony_ci}; 41088c2ecf20Sopenharmony_ci 41098c2ecf20Sopenharmony_cistatic int mvneta_mdio_probe(struct mvneta_port *pp) 41108c2ecf20Sopenharmony_ci{ 41118c2ecf20Sopenharmony_ci struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; 41128c2ecf20Sopenharmony_ci int err = phylink_of_phy_connect(pp->phylink, pp->dn, 0); 41138c2ecf20Sopenharmony_ci 41148c2ecf20Sopenharmony_ci if (err) 41158c2ecf20Sopenharmony_ci netdev_err(pp->dev, "could not attach PHY: %d\n", err); 41168c2ecf20Sopenharmony_ci 41178c2ecf20Sopenharmony_ci phylink_ethtool_get_wol(pp->phylink, &wol); 41188c2ecf20Sopenharmony_ci device_set_wakeup_capable(&pp->dev->dev, !!wol.supported); 41198c2ecf20Sopenharmony_ci 41208c2ecf20Sopenharmony_ci /* PHY WoL may be enabled but device wakeup disabled */ 41218c2ecf20Sopenharmony_ci if (wol.supported) 41228c2ecf20Sopenharmony_ci device_set_wakeup_enable(&pp->dev->dev, !!wol.wolopts); 41238c2ecf20Sopenharmony_ci 41248c2ecf20Sopenharmony_ci return err; 41258c2ecf20Sopenharmony_ci} 41268c2ecf20Sopenharmony_ci 41278c2ecf20Sopenharmony_cistatic void mvneta_mdio_remove(struct mvneta_port *pp) 41288c2ecf20Sopenharmony_ci{ 41298c2ecf20Sopenharmony_ci phylink_disconnect_phy(pp->phylink); 41308c2ecf20Sopenharmony_ci} 41318c2ecf20Sopenharmony_ci 41328c2ecf20Sopenharmony_ci/* Electing a CPU must be done in an atomic way: it should be done 41338c2ecf20Sopenharmony_ci * after or before the removal/insertion of a CPU and this function is 41348c2ecf20Sopenharmony_ci * not reentrant. 41358c2ecf20Sopenharmony_ci */ 41368c2ecf20Sopenharmony_cistatic void mvneta_percpu_elect(struct mvneta_port *pp) 41378c2ecf20Sopenharmony_ci{ 41388c2ecf20Sopenharmony_ci int elected_cpu = 0, max_cpu, cpu, i = 0; 41398c2ecf20Sopenharmony_ci 41408c2ecf20Sopenharmony_ci /* Use the cpu associated to the rxq when it is online, in all 41418c2ecf20Sopenharmony_ci * the other cases, use the cpu 0 which can't be offline. 41428c2ecf20Sopenharmony_ci */ 41438c2ecf20Sopenharmony_ci if (pp->rxq_def < nr_cpu_ids && cpu_online(pp->rxq_def)) 41448c2ecf20Sopenharmony_ci elected_cpu = pp->rxq_def; 41458c2ecf20Sopenharmony_ci 41468c2ecf20Sopenharmony_ci max_cpu = num_present_cpus(); 41478c2ecf20Sopenharmony_ci 41488c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) { 41498c2ecf20Sopenharmony_ci int rxq_map = 0, txq_map = 0; 41508c2ecf20Sopenharmony_ci int rxq; 41518c2ecf20Sopenharmony_ci 41528c2ecf20Sopenharmony_ci for (rxq = 0; rxq < rxq_number; rxq++) 41538c2ecf20Sopenharmony_ci if ((rxq % max_cpu) == cpu) 41548c2ecf20Sopenharmony_ci rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq); 41558c2ecf20Sopenharmony_ci 41568c2ecf20Sopenharmony_ci if (cpu == elected_cpu) 41578c2ecf20Sopenharmony_ci /* Map the default receive queue queue to the 41588c2ecf20Sopenharmony_ci * elected CPU 41598c2ecf20Sopenharmony_ci */ 41608c2ecf20Sopenharmony_ci rxq_map |= MVNETA_CPU_RXQ_ACCESS(pp->rxq_def); 41618c2ecf20Sopenharmony_ci 41628c2ecf20Sopenharmony_ci /* We update the TX queue map only if we have one 41638c2ecf20Sopenharmony_ci * queue. In this case we associate the TX queue to 41648c2ecf20Sopenharmony_ci * the CPU bound to the default RX queue 41658c2ecf20Sopenharmony_ci */ 41668c2ecf20Sopenharmony_ci if (txq_number == 1) 41678c2ecf20Sopenharmony_ci txq_map = (cpu == elected_cpu) ? 41688c2ecf20Sopenharmony_ci MVNETA_CPU_TXQ_ACCESS(0) : 0; 41698c2ecf20Sopenharmony_ci else 41708c2ecf20Sopenharmony_ci txq_map = mvreg_read(pp, MVNETA_CPU_MAP(cpu)) & 41718c2ecf20Sopenharmony_ci MVNETA_CPU_TXQ_ACCESS_ALL_MASK; 41728c2ecf20Sopenharmony_ci 41738c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map); 41748c2ecf20Sopenharmony_ci 41758c2ecf20Sopenharmony_ci /* Update the interrupt mask on each CPU according the 41768c2ecf20Sopenharmony_ci * new mapping 41778c2ecf20Sopenharmony_ci */ 41788c2ecf20Sopenharmony_ci smp_call_function_single(cpu, mvneta_percpu_unmask_interrupt, 41798c2ecf20Sopenharmony_ci pp, true); 41808c2ecf20Sopenharmony_ci i++; 41818c2ecf20Sopenharmony_ci 41828c2ecf20Sopenharmony_ci } 41838c2ecf20Sopenharmony_ci}; 41848c2ecf20Sopenharmony_ci 41858c2ecf20Sopenharmony_cistatic int mvneta_cpu_online(unsigned int cpu, struct hlist_node *node) 41868c2ecf20Sopenharmony_ci{ 41878c2ecf20Sopenharmony_ci int other_cpu; 41888c2ecf20Sopenharmony_ci struct mvneta_port *pp = hlist_entry_safe(node, struct mvneta_port, 41898c2ecf20Sopenharmony_ci node_online); 41908c2ecf20Sopenharmony_ci struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); 41918c2ecf20Sopenharmony_ci 41928c2ecf20Sopenharmony_ci /* Armada 3700's per-cpu interrupt for mvneta is broken, all interrupts 41938c2ecf20Sopenharmony_ci * are routed to CPU 0, so we don't need all the cpu-hotplug support 41948c2ecf20Sopenharmony_ci */ 41958c2ecf20Sopenharmony_ci if (pp->neta_armada3700) 41968c2ecf20Sopenharmony_ci return 0; 41978c2ecf20Sopenharmony_ci 41988c2ecf20Sopenharmony_ci spin_lock(&pp->lock); 41998c2ecf20Sopenharmony_ci /* 42008c2ecf20Sopenharmony_ci * Configuring the driver for a new CPU while the driver is 42018c2ecf20Sopenharmony_ci * stopping is racy, so just avoid it. 42028c2ecf20Sopenharmony_ci */ 42038c2ecf20Sopenharmony_ci if (pp->is_stopped) { 42048c2ecf20Sopenharmony_ci spin_unlock(&pp->lock); 42058c2ecf20Sopenharmony_ci return 0; 42068c2ecf20Sopenharmony_ci } 42078c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(pp->dev); 42088c2ecf20Sopenharmony_ci 42098c2ecf20Sopenharmony_ci /* 42108c2ecf20Sopenharmony_ci * We have to synchronise on tha napi of each CPU except the one 42118c2ecf20Sopenharmony_ci * just being woken up 42128c2ecf20Sopenharmony_ci */ 42138c2ecf20Sopenharmony_ci for_each_online_cpu(other_cpu) { 42148c2ecf20Sopenharmony_ci if (other_cpu != cpu) { 42158c2ecf20Sopenharmony_ci struct mvneta_pcpu_port *other_port = 42168c2ecf20Sopenharmony_ci per_cpu_ptr(pp->ports, other_cpu); 42178c2ecf20Sopenharmony_ci 42188c2ecf20Sopenharmony_ci napi_synchronize(&other_port->napi); 42198c2ecf20Sopenharmony_ci } 42208c2ecf20Sopenharmony_ci } 42218c2ecf20Sopenharmony_ci 42228c2ecf20Sopenharmony_ci /* Mask all ethernet port interrupts */ 42238c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_mask_interrupt, pp, true); 42248c2ecf20Sopenharmony_ci napi_enable(&port->napi); 42258c2ecf20Sopenharmony_ci 42268c2ecf20Sopenharmony_ci /* 42278c2ecf20Sopenharmony_ci * Enable per-CPU interrupts on the CPU that is 42288c2ecf20Sopenharmony_ci * brought up. 42298c2ecf20Sopenharmony_ci */ 42308c2ecf20Sopenharmony_ci mvneta_percpu_enable(pp); 42318c2ecf20Sopenharmony_ci 42328c2ecf20Sopenharmony_ci /* 42338c2ecf20Sopenharmony_ci * Enable per-CPU interrupt on the one CPU we care 42348c2ecf20Sopenharmony_ci * about. 42358c2ecf20Sopenharmony_ci */ 42368c2ecf20Sopenharmony_ci mvneta_percpu_elect(pp); 42378c2ecf20Sopenharmony_ci 42388c2ecf20Sopenharmony_ci /* Unmask all ethernet port interrupts */ 42398c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true); 42408c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_MISC_MASK, 42418c2ecf20Sopenharmony_ci MVNETA_CAUSE_PHY_STATUS_CHANGE | 42428c2ecf20Sopenharmony_ci MVNETA_CAUSE_LINK_CHANGE); 42438c2ecf20Sopenharmony_ci netif_tx_start_all_queues(pp->dev); 42448c2ecf20Sopenharmony_ci spin_unlock(&pp->lock); 42458c2ecf20Sopenharmony_ci return 0; 42468c2ecf20Sopenharmony_ci} 42478c2ecf20Sopenharmony_ci 42488c2ecf20Sopenharmony_cistatic int mvneta_cpu_down_prepare(unsigned int cpu, struct hlist_node *node) 42498c2ecf20Sopenharmony_ci{ 42508c2ecf20Sopenharmony_ci struct mvneta_port *pp = hlist_entry_safe(node, struct mvneta_port, 42518c2ecf20Sopenharmony_ci node_online); 42528c2ecf20Sopenharmony_ci struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); 42538c2ecf20Sopenharmony_ci 42548c2ecf20Sopenharmony_ci /* 42558c2ecf20Sopenharmony_ci * Thanks to this lock we are sure that any pending cpu election is 42568c2ecf20Sopenharmony_ci * done. 42578c2ecf20Sopenharmony_ci */ 42588c2ecf20Sopenharmony_ci spin_lock(&pp->lock); 42598c2ecf20Sopenharmony_ci /* Mask all ethernet port interrupts */ 42608c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_mask_interrupt, pp, true); 42618c2ecf20Sopenharmony_ci spin_unlock(&pp->lock); 42628c2ecf20Sopenharmony_ci 42638c2ecf20Sopenharmony_ci napi_synchronize(&port->napi); 42648c2ecf20Sopenharmony_ci napi_disable(&port->napi); 42658c2ecf20Sopenharmony_ci /* Disable per-CPU interrupts on the CPU that is brought down. */ 42668c2ecf20Sopenharmony_ci mvneta_percpu_disable(pp); 42678c2ecf20Sopenharmony_ci return 0; 42688c2ecf20Sopenharmony_ci} 42698c2ecf20Sopenharmony_ci 42708c2ecf20Sopenharmony_cistatic int mvneta_cpu_dead(unsigned int cpu, struct hlist_node *node) 42718c2ecf20Sopenharmony_ci{ 42728c2ecf20Sopenharmony_ci struct mvneta_port *pp = hlist_entry_safe(node, struct mvneta_port, 42738c2ecf20Sopenharmony_ci node_dead); 42748c2ecf20Sopenharmony_ci 42758c2ecf20Sopenharmony_ci /* Check if a new CPU must be elected now this on is down */ 42768c2ecf20Sopenharmony_ci spin_lock(&pp->lock); 42778c2ecf20Sopenharmony_ci mvneta_percpu_elect(pp); 42788c2ecf20Sopenharmony_ci spin_unlock(&pp->lock); 42798c2ecf20Sopenharmony_ci /* Unmask all ethernet port interrupts */ 42808c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true); 42818c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_INTR_MISC_MASK, 42828c2ecf20Sopenharmony_ci MVNETA_CAUSE_PHY_STATUS_CHANGE | 42838c2ecf20Sopenharmony_ci MVNETA_CAUSE_LINK_CHANGE); 42848c2ecf20Sopenharmony_ci netif_tx_start_all_queues(pp->dev); 42858c2ecf20Sopenharmony_ci return 0; 42868c2ecf20Sopenharmony_ci} 42878c2ecf20Sopenharmony_ci 42888c2ecf20Sopenharmony_cistatic int mvneta_open(struct net_device *dev) 42898c2ecf20Sopenharmony_ci{ 42908c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 42918c2ecf20Sopenharmony_ci int ret; 42928c2ecf20Sopenharmony_ci 42938c2ecf20Sopenharmony_ci pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu); 42948c2ecf20Sopenharmony_ci 42958c2ecf20Sopenharmony_ci ret = mvneta_setup_rxqs(pp); 42968c2ecf20Sopenharmony_ci if (ret) 42978c2ecf20Sopenharmony_ci return ret; 42988c2ecf20Sopenharmony_ci 42998c2ecf20Sopenharmony_ci ret = mvneta_setup_txqs(pp); 43008c2ecf20Sopenharmony_ci if (ret) 43018c2ecf20Sopenharmony_ci goto err_cleanup_rxqs; 43028c2ecf20Sopenharmony_ci 43038c2ecf20Sopenharmony_ci /* Connect to port interrupt line */ 43048c2ecf20Sopenharmony_ci if (pp->neta_armada3700) 43058c2ecf20Sopenharmony_ci ret = request_irq(pp->dev->irq, mvneta_isr, 0, 43068c2ecf20Sopenharmony_ci dev->name, pp); 43078c2ecf20Sopenharmony_ci else 43088c2ecf20Sopenharmony_ci ret = request_percpu_irq(pp->dev->irq, mvneta_percpu_isr, 43098c2ecf20Sopenharmony_ci dev->name, pp->ports); 43108c2ecf20Sopenharmony_ci if (ret) { 43118c2ecf20Sopenharmony_ci netdev_err(pp->dev, "cannot request irq %d\n", pp->dev->irq); 43128c2ecf20Sopenharmony_ci goto err_cleanup_txqs; 43138c2ecf20Sopenharmony_ci } 43148c2ecf20Sopenharmony_ci 43158c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) { 43168c2ecf20Sopenharmony_ci /* Enable per-CPU interrupt on all the CPU to handle our RX 43178c2ecf20Sopenharmony_ci * queue interrupts 43188c2ecf20Sopenharmony_ci */ 43198c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_enable, pp, true); 43208c2ecf20Sopenharmony_ci 43218c2ecf20Sopenharmony_ci pp->is_stopped = false; 43228c2ecf20Sopenharmony_ci /* Register a CPU notifier to handle the case where our CPU 43238c2ecf20Sopenharmony_ci * might be taken offline. 43248c2ecf20Sopenharmony_ci */ 43258c2ecf20Sopenharmony_ci ret = cpuhp_state_add_instance_nocalls(online_hpstate, 43268c2ecf20Sopenharmony_ci &pp->node_online); 43278c2ecf20Sopenharmony_ci if (ret) 43288c2ecf20Sopenharmony_ci goto err_free_irq; 43298c2ecf20Sopenharmony_ci 43308c2ecf20Sopenharmony_ci ret = cpuhp_state_add_instance_nocalls(CPUHP_NET_MVNETA_DEAD, 43318c2ecf20Sopenharmony_ci &pp->node_dead); 43328c2ecf20Sopenharmony_ci if (ret) 43338c2ecf20Sopenharmony_ci goto err_free_online_hp; 43348c2ecf20Sopenharmony_ci } 43358c2ecf20Sopenharmony_ci 43368c2ecf20Sopenharmony_ci ret = mvneta_mdio_probe(pp); 43378c2ecf20Sopenharmony_ci if (ret < 0) { 43388c2ecf20Sopenharmony_ci netdev_err(dev, "cannot probe MDIO bus\n"); 43398c2ecf20Sopenharmony_ci goto err_free_dead_hp; 43408c2ecf20Sopenharmony_ci } 43418c2ecf20Sopenharmony_ci 43428c2ecf20Sopenharmony_ci mvneta_start_dev(pp); 43438c2ecf20Sopenharmony_ci 43448c2ecf20Sopenharmony_ci return 0; 43458c2ecf20Sopenharmony_ci 43468c2ecf20Sopenharmony_cierr_free_dead_hp: 43478c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) 43488c2ecf20Sopenharmony_ci cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD, 43498c2ecf20Sopenharmony_ci &pp->node_dead); 43508c2ecf20Sopenharmony_cierr_free_online_hp: 43518c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) 43528c2ecf20Sopenharmony_ci cpuhp_state_remove_instance_nocalls(online_hpstate, 43538c2ecf20Sopenharmony_ci &pp->node_online); 43548c2ecf20Sopenharmony_cierr_free_irq: 43558c2ecf20Sopenharmony_ci if (pp->neta_armada3700) { 43568c2ecf20Sopenharmony_ci free_irq(pp->dev->irq, pp); 43578c2ecf20Sopenharmony_ci } else { 43588c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_disable, pp, true); 43598c2ecf20Sopenharmony_ci free_percpu_irq(pp->dev->irq, pp->ports); 43608c2ecf20Sopenharmony_ci } 43618c2ecf20Sopenharmony_cierr_cleanup_txqs: 43628c2ecf20Sopenharmony_ci mvneta_cleanup_txqs(pp); 43638c2ecf20Sopenharmony_cierr_cleanup_rxqs: 43648c2ecf20Sopenharmony_ci mvneta_cleanup_rxqs(pp); 43658c2ecf20Sopenharmony_ci return ret; 43668c2ecf20Sopenharmony_ci} 43678c2ecf20Sopenharmony_ci 43688c2ecf20Sopenharmony_ci/* Stop the port, free port interrupt line */ 43698c2ecf20Sopenharmony_cistatic int mvneta_stop(struct net_device *dev) 43708c2ecf20Sopenharmony_ci{ 43718c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 43728c2ecf20Sopenharmony_ci 43738c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) { 43748c2ecf20Sopenharmony_ci /* Inform that we are stopping so we don't want to setup the 43758c2ecf20Sopenharmony_ci * driver for new CPUs in the notifiers. The code of the 43768c2ecf20Sopenharmony_ci * notifier for CPU online is protected by the same spinlock, 43778c2ecf20Sopenharmony_ci * so when we get the lock, the notifer work is done. 43788c2ecf20Sopenharmony_ci */ 43798c2ecf20Sopenharmony_ci spin_lock(&pp->lock); 43808c2ecf20Sopenharmony_ci pp->is_stopped = true; 43818c2ecf20Sopenharmony_ci spin_unlock(&pp->lock); 43828c2ecf20Sopenharmony_ci 43838c2ecf20Sopenharmony_ci mvneta_stop_dev(pp); 43848c2ecf20Sopenharmony_ci mvneta_mdio_remove(pp); 43858c2ecf20Sopenharmony_ci 43868c2ecf20Sopenharmony_ci cpuhp_state_remove_instance_nocalls(online_hpstate, 43878c2ecf20Sopenharmony_ci &pp->node_online); 43888c2ecf20Sopenharmony_ci cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD, 43898c2ecf20Sopenharmony_ci &pp->node_dead); 43908c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_disable, pp, true); 43918c2ecf20Sopenharmony_ci free_percpu_irq(dev->irq, pp->ports); 43928c2ecf20Sopenharmony_ci } else { 43938c2ecf20Sopenharmony_ci mvneta_stop_dev(pp); 43948c2ecf20Sopenharmony_ci mvneta_mdio_remove(pp); 43958c2ecf20Sopenharmony_ci free_irq(dev->irq, pp); 43968c2ecf20Sopenharmony_ci } 43978c2ecf20Sopenharmony_ci 43988c2ecf20Sopenharmony_ci mvneta_cleanup_rxqs(pp); 43998c2ecf20Sopenharmony_ci mvneta_cleanup_txqs(pp); 44008c2ecf20Sopenharmony_ci 44018c2ecf20Sopenharmony_ci return 0; 44028c2ecf20Sopenharmony_ci} 44038c2ecf20Sopenharmony_ci 44048c2ecf20Sopenharmony_cistatic int mvneta_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 44058c2ecf20Sopenharmony_ci{ 44068c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 44078c2ecf20Sopenharmony_ci 44088c2ecf20Sopenharmony_ci return phylink_mii_ioctl(pp->phylink, ifr, cmd); 44098c2ecf20Sopenharmony_ci} 44108c2ecf20Sopenharmony_ci 44118c2ecf20Sopenharmony_cistatic int mvneta_xdp_setup(struct net_device *dev, struct bpf_prog *prog, 44128c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 44138c2ecf20Sopenharmony_ci{ 44148c2ecf20Sopenharmony_ci bool need_update, running = netif_running(dev); 44158c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 44168c2ecf20Sopenharmony_ci struct bpf_prog *old_prog; 44178c2ecf20Sopenharmony_ci 44188c2ecf20Sopenharmony_ci if (prog && dev->mtu > MVNETA_MAX_RX_BUF_SIZE) { 44198c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "MTU too large for XDP"); 44208c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 44218c2ecf20Sopenharmony_ci } 44228c2ecf20Sopenharmony_ci 44238c2ecf20Sopenharmony_ci if (pp->bm_priv) { 44248c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 44258c2ecf20Sopenharmony_ci "Hardware Buffer Management not supported on XDP"); 44268c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 44278c2ecf20Sopenharmony_ci } 44288c2ecf20Sopenharmony_ci 44298c2ecf20Sopenharmony_ci need_update = !!pp->xdp_prog != !!prog; 44308c2ecf20Sopenharmony_ci if (running && need_update) 44318c2ecf20Sopenharmony_ci mvneta_stop(dev); 44328c2ecf20Sopenharmony_ci 44338c2ecf20Sopenharmony_ci old_prog = xchg(&pp->xdp_prog, prog); 44348c2ecf20Sopenharmony_ci if (old_prog) 44358c2ecf20Sopenharmony_ci bpf_prog_put(old_prog); 44368c2ecf20Sopenharmony_ci 44378c2ecf20Sopenharmony_ci if (running && need_update) 44388c2ecf20Sopenharmony_ci return mvneta_open(dev); 44398c2ecf20Sopenharmony_ci 44408c2ecf20Sopenharmony_ci return 0; 44418c2ecf20Sopenharmony_ci} 44428c2ecf20Sopenharmony_ci 44438c2ecf20Sopenharmony_cistatic int mvneta_xdp(struct net_device *dev, struct netdev_bpf *xdp) 44448c2ecf20Sopenharmony_ci{ 44458c2ecf20Sopenharmony_ci switch (xdp->command) { 44468c2ecf20Sopenharmony_ci case XDP_SETUP_PROG: 44478c2ecf20Sopenharmony_ci return mvneta_xdp_setup(dev, xdp->prog, xdp->extack); 44488c2ecf20Sopenharmony_ci default: 44498c2ecf20Sopenharmony_ci return -EINVAL; 44508c2ecf20Sopenharmony_ci } 44518c2ecf20Sopenharmony_ci} 44528c2ecf20Sopenharmony_ci 44538c2ecf20Sopenharmony_ci/* Ethtool methods */ 44548c2ecf20Sopenharmony_ci 44558c2ecf20Sopenharmony_ci/* Set link ksettings (phy address, speed) for ethtools */ 44568c2ecf20Sopenharmony_cistatic int 44578c2ecf20Sopenharmony_cimvneta_ethtool_set_link_ksettings(struct net_device *ndev, 44588c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 44598c2ecf20Sopenharmony_ci{ 44608c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(ndev); 44618c2ecf20Sopenharmony_ci 44628c2ecf20Sopenharmony_ci return phylink_ethtool_ksettings_set(pp->phylink, cmd); 44638c2ecf20Sopenharmony_ci} 44648c2ecf20Sopenharmony_ci 44658c2ecf20Sopenharmony_ci/* Get link ksettings for ethtools */ 44668c2ecf20Sopenharmony_cistatic int 44678c2ecf20Sopenharmony_cimvneta_ethtool_get_link_ksettings(struct net_device *ndev, 44688c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 44698c2ecf20Sopenharmony_ci{ 44708c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(ndev); 44718c2ecf20Sopenharmony_ci 44728c2ecf20Sopenharmony_ci return phylink_ethtool_ksettings_get(pp->phylink, cmd); 44738c2ecf20Sopenharmony_ci} 44748c2ecf20Sopenharmony_ci 44758c2ecf20Sopenharmony_cistatic int mvneta_ethtool_nway_reset(struct net_device *dev) 44768c2ecf20Sopenharmony_ci{ 44778c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 44788c2ecf20Sopenharmony_ci 44798c2ecf20Sopenharmony_ci return phylink_ethtool_nway_reset(pp->phylink); 44808c2ecf20Sopenharmony_ci} 44818c2ecf20Sopenharmony_ci 44828c2ecf20Sopenharmony_ci/* Set interrupt coalescing for ethtools */ 44838c2ecf20Sopenharmony_cistatic int mvneta_ethtool_set_coalesce(struct net_device *dev, 44848c2ecf20Sopenharmony_ci struct ethtool_coalesce *c) 44858c2ecf20Sopenharmony_ci{ 44868c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 44878c2ecf20Sopenharmony_ci int queue; 44888c2ecf20Sopenharmony_ci 44898c2ecf20Sopenharmony_ci for (queue = 0; queue < rxq_number; queue++) { 44908c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; 44918c2ecf20Sopenharmony_ci rxq->time_coal = c->rx_coalesce_usecs; 44928c2ecf20Sopenharmony_ci rxq->pkts_coal = c->rx_max_coalesced_frames; 44938c2ecf20Sopenharmony_ci mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal); 44948c2ecf20Sopenharmony_ci mvneta_rx_time_coal_set(pp, rxq, rxq->time_coal); 44958c2ecf20Sopenharmony_ci } 44968c2ecf20Sopenharmony_ci 44978c2ecf20Sopenharmony_ci for (queue = 0; queue < txq_number; queue++) { 44988c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq = &pp->txqs[queue]; 44998c2ecf20Sopenharmony_ci txq->done_pkts_coal = c->tx_max_coalesced_frames; 45008c2ecf20Sopenharmony_ci mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal); 45018c2ecf20Sopenharmony_ci } 45028c2ecf20Sopenharmony_ci 45038c2ecf20Sopenharmony_ci return 0; 45048c2ecf20Sopenharmony_ci} 45058c2ecf20Sopenharmony_ci 45068c2ecf20Sopenharmony_ci/* get coalescing for ethtools */ 45078c2ecf20Sopenharmony_cistatic int mvneta_ethtool_get_coalesce(struct net_device *dev, 45088c2ecf20Sopenharmony_ci struct ethtool_coalesce *c) 45098c2ecf20Sopenharmony_ci{ 45108c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 45118c2ecf20Sopenharmony_ci 45128c2ecf20Sopenharmony_ci c->rx_coalesce_usecs = pp->rxqs[0].time_coal; 45138c2ecf20Sopenharmony_ci c->rx_max_coalesced_frames = pp->rxqs[0].pkts_coal; 45148c2ecf20Sopenharmony_ci 45158c2ecf20Sopenharmony_ci c->tx_max_coalesced_frames = pp->txqs[0].done_pkts_coal; 45168c2ecf20Sopenharmony_ci return 0; 45178c2ecf20Sopenharmony_ci} 45188c2ecf20Sopenharmony_ci 45198c2ecf20Sopenharmony_ci 45208c2ecf20Sopenharmony_cistatic void mvneta_ethtool_get_drvinfo(struct net_device *dev, 45218c2ecf20Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 45228c2ecf20Sopenharmony_ci{ 45238c2ecf20Sopenharmony_ci strlcpy(drvinfo->driver, MVNETA_DRIVER_NAME, 45248c2ecf20Sopenharmony_ci sizeof(drvinfo->driver)); 45258c2ecf20Sopenharmony_ci strlcpy(drvinfo->version, MVNETA_DRIVER_VERSION, 45268c2ecf20Sopenharmony_ci sizeof(drvinfo->version)); 45278c2ecf20Sopenharmony_ci strlcpy(drvinfo->bus_info, dev_name(&dev->dev), 45288c2ecf20Sopenharmony_ci sizeof(drvinfo->bus_info)); 45298c2ecf20Sopenharmony_ci} 45308c2ecf20Sopenharmony_ci 45318c2ecf20Sopenharmony_ci 45328c2ecf20Sopenharmony_cistatic void mvneta_ethtool_get_ringparam(struct net_device *netdev, 45338c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 45348c2ecf20Sopenharmony_ci{ 45358c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(netdev); 45368c2ecf20Sopenharmony_ci 45378c2ecf20Sopenharmony_ci ring->rx_max_pending = MVNETA_MAX_RXD; 45388c2ecf20Sopenharmony_ci ring->tx_max_pending = MVNETA_MAX_TXD; 45398c2ecf20Sopenharmony_ci ring->rx_pending = pp->rx_ring_size; 45408c2ecf20Sopenharmony_ci ring->tx_pending = pp->tx_ring_size; 45418c2ecf20Sopenharmony_ci} 45428c2ecf20Sopenharmony_ci 45438c2ecf20Sopenharmony_cistatic int mvneta_ethtool_set_ringparam(struct net_device *dev, 45448c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 45458c2ecf20Sopenharmony_ci{ 45468c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 45478c2ecf20Sopenharmony_ci 45488c2ecf20Sopenharmony_ci if ((ring->rx_pending == 0) || (ring->tx_pending == 0)) 45498c2ecf20Sopenharmony_ci return -EINVAL; 45508c2ecf20Sopenharmony_ci pp->rx_ring_size = ring->rx_pending < MVNETA_MAX_RXD ? 45518c2ecf20Sopenharmony_ci ring->rx_pending : MVNETA_MAX_RXD; 45528c2ecf20Sopenharmony_ci 45538c2ecf20Sopenharmony_ci pp->tx_ring_size = clamp_t(u16, ring->tx_pending, 45548c2ecf20Sopenharmony_ci MVNETA_MAX_SKB_DESCS * 2, MVNETA_MAX_TXD); 45558c2ecf20Sopenharmony_ci if (pp->tx_ring_size != ring->tx_pending) 45568c2ecf20Sopenharmony_ci netdev_warn(dev, "TX queue size set to %u (requested %u)\n", 45578c2ecf20Sopenharmony_ci pp->tx_ring_size, ring->tx_pending); 45588c2ecf20Sopenharmony_ci 45598c2ecf20Sopenharmony_ci if (netif_running(dev)) { 45608c2ecf20Sopenharmony_ci mvneta_stop(dev); 45618c2ecf20Sopenharmony_ci if (mvneta_open(dev)) { 45628c2ecf20Sopenharmony_ci netdev_err(dev, 45638c2ecf20Sopenharmony_ci "error on opening device after ring param change\n"); 45648c2ecf20Sopenharmony_ci return -ENOMEM; 45658c2ecf20Sopenharmony_ci } 45668c2ecf20Sopenharmony_ci } 45678c2ecf20Sopenharmony_ci 45688c2ecf20Sopenharmony_ci return 0; 45698c2ecf20Sopenharmony_ci} 45708c2ecf20Sopenharmony_ci 45718c2ecf20Sopenharmony_cistatic void mvneta_ethtool_get_pauseparam(struct net_device *dev, 45728c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 45738c2ecf20Sopenharmony_ci{ 45748c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 45758c2ecf20Sopenharmony_ci 45768c2ecf20Sopenharmony_ci phylink_ethtool_get_pauseparam(pp->phylink, pause); 45778c2ecf20Sopenharmony_ci} 45788c2ecf20Sopenharmony_ci 45798c2ecf20Sopenharmony_cistatic int mvneta_ethtool_set_pauseparam(struct net_device *dev, 45808c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 45818c2ecf20Sopenharmony_ci{ 45828c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 45838c2ecf20Sopenharmony_ci 45848c2ecf20Sopenharmony_ci return phylink_ethtool_set_pauseparam(pp->phylink, pause); 45858c2ecf20Sopenharmony_ci} 45868c2ecf20Sopenharmony_ci 45878c2ecf20Sopenharmony_cistatic void mvneta_ethtool_get_strings(struct net_device *netdev, u32 sset, 45888c2ecf20Sopenharmony_ci u8 *data) 45898c2ecf20Sopenharmony_ci{ 45908c2ecf20Sopenharmony_ci if (sset == ETH_SS_STATS) { 45918c2ecf20Sopenharmony_ci int i; 45928c2ecf20Sopenharmony_ci 45938c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mvneta_statistics); i++) 45948c2ecf20Sopenharmony_ci memcpy(data + i * ETH_GSTRING_LEN, 45958c2ecf20Sopenharmony_ci mvneta_statistics[i].name, ETH_GSTRING_LEN); 45968c2ecf20Sopenharmony_ci } 45978c2ecf20Sopenharmony_ci} 45988c2ecf20Sopenharmony_ci 45998c2ecf20Sopenharmony_cistatic void 46008c2ecf20Sopenharmony_cimvneta_ethtool_update_pcpu_stats(struct mvneta_port *pp, 46018c2ecf20Sopenharmony_ci struct mvneta_ethtool_stats *es) 46028c2ecf20Sopenharmony_ci{ 46038c2ecf20Sopenharmony_ci unsigned int start; 46048c2ecf20Sopenharmony_ci int cpu; 46058c2ecf20Sopenharmony_ci 46068c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 46078c2ecf20Sopenharmony_ci struct mvneta_pcpu_stats *stats; 46088c2ecf20Sopenharmony_ci u64 skb_alloc_error; 46098c2ecf20Sopenharmony_ci u64 refill_error; 46108c2ecf20Sopenharmony_ci u64 xdp_redirect; 46118c2ecf20Sopenharmony_ci u64 xdp_xmit_err; 46128c2ecf20Sopenharmony_ci u64 xdp_tx_err; 46138c2ecf20Sopenharmony_ci u64 xdp_pass; 46148c2ecf20Sopenharmony_ci u64 xdp_drop; 46158c2ecf20Sopenharmony_ci u64 xdp_xmit; 46168c2ecf20Sopenharmony_ci u64 xdp_tx; 46178c2ecf20Sopenharmony_ci 46188c2ecf20Sopenharmony_ci stats = per_cpu_ptr(pp->stats, cpu); 46198c2ecf20Sopenharmony_ci do { 46208c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&stats->syncp); 46218c2ecf20Sopenharmony_ci skb_alloc_error = stats->es.skb_alloc_error; 46228c2ecf20Sopenharmony_ci refill_error = stats->es.refill_error; 46238c2ecf20Sopenharmony_ci xdp_redirect = stats->es.ps.xdp_redirect; 46248c2ecf20Sopenharmony_ci xdp_pass = stats->es.ps.xdp_pass; 46258c2ecf20Sopenharmony_ci xdp_drop = stats->es.ps.xdp_drop; 46268c2ecf20Sopenharmony_ci xdp_xmit = stats->es.ps.xdp_xmit; 46278c2ecf20Sopenharmony_ci xdp_xmit_err = stats->es.ps.xdp_xmit_err; 46288c2ecf20Sopenharmony_ci xdp_tx = stats->es.ps.xdp_tx; 46298c2ecf20Sopenharmony_ci xdp_tx_err = stats->es.ps.xdp_tx_err; 46308c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); 46318c2ecf20Sopenharmony_ci 46328c2ecf20Sopenharmony_ci es->skb_alloc_error += skb_alloc_error; 46338c2ecf20Sopenharmony_ci es->refill_error += refill_error; 46348c2ecf20Sopenharmony_ci es->ps.xdp_redirect += xdp_redirect; 46358c2ecf20Sopenharmony_ci es->ps.xdp_pass += xdp_pass; 46368c2ecf20Sopenharmony_ci es->ps.xdp_drop += xdp_drop; 46378c2ecf20Sopenharmony_ci es->ps.xdp_xmit += xdp_xmit; 46388c2ecf20Sopenharmony_ci es->ps.xdp_xmit_err += xdp_xmit_err; 46398c2ecf20Sopenharmony_ci es->ps.xdp_tx += xdp_tx; 46408c2ecf20Sopenharmony_ci es->ps.xdp_tx_err += xdp_tx_err; 46418c2ecf20Sopenharmony_ci } 46428c2ecf20Sopenharmony_ci} 46438c2ecf20Sopenharmony_ci 46448c2ecf20Sopenharmony_cistatic void mvneta_ethtool_update_stats(struct mvneta_port *pp) 46458c2ecf20Sopenharmony_ci{ 46468c2ecf20Sopenharmony_ci struct mvneta_ethtool_stats stats = {}; 46478c2ecf20Sopenharmony_ci const struct mvneta_statistic *s; 46488c2ecf20Sopenharmony_ci void __iomem *base = pp->base; 46498c2ecf20Sopenharmony_ci u32 high, low; 46508c2ecf20Sopenharmony_ci u64 val; 46518c2ecf20Sopenharmony_ci int i; 46528c2ecf20Sopenharmony_ci 46538c2ecf20Sopenharmony_ci mvneta_ethtool_update_pcpu_stats(pp, &stats); 46548c2ecf20Sopenharmony_ci for (i = 0, s = mvneta_statistics; 46558c2ecf20Sopenharmony_ci s < mvneta_statistics + ARRAY_SIZE(mvneta_statistics); 46568c2ecf20Sopenharmony_ci s++, i++) { 46578c2ecf20Sopenharmony_ci switch (s->type) { 46588c2ecf20Sopenharmony_ci case T_REG_32: 46598c2ecf20Sopenharmony_ci val = readl_relaxed(base + s->offset); 46608c2ecf20Sopenharmony_ci pp->ethtool_stats[i] += val; 46618c2ecf20Sopenharmony_ci break; 46628c2ecf20Sopenharmony_ci case T_REG_64: 46638c2ecf20Sopenharmony_ci /* Docs say to read low 32-bit then high */ 46648c2ecf20Sopenharmony_ci low = readl_relaxed(base + s->offset); 46658c2ecf20Sopenharmony_ci high = readl_relaxed(base + s->offset + 4); 46668c2ecf20Sopenharmony_ci val = (u64)high << 32 | low; 46678c2ecf20Sopenharmony_ci pp->ethtool_stats[i] += val; 46688c2ecf20Sopenharmony_ci break; 46698c2ecf20Sopenharmony_ci case T_SW: 46708c2ecf20Sopenharmony_ci switch (s->offset) { 46718c2ecf20Sopenharmony_ci case ETHTOOL_STAT_EEE_WAKEUP: 46728c2ecf20Sopenharmony_ci val = phylink_get_eee_err(pp->phylink); 46738c2ecf20Sopenharmony_ci pp->ethtool_stats[i] += val; 46748c2ecf20Sopenharmony_ci break; 46758c2ecf20Sopenharmony_ci case ETHTOOL_STAT_SKB_ALLOC_ERR: 46768c2ecf20Sopenharmony_ci pp->ethtool_stats[i] = stats.skb_alloc_error; 46778c2ecf20Sopenharmony_ci break; 46788c2ecf20Sopenharmony_ci case ETHTOOL_STAT_REFILL_ERR: 46798c2ecf20Sopenharmony_ci pp->ethtool_stats[i] = stats.refill_error; 46808c2ecf20Sopenharmony_ci break; 46818c2ecf20Sopenharmony_ci case ETHTOOL_XDP_REDIRECT: 46828c2ecf20Sopenharmony_ci pp->ethtool_stats[i] = stats.ps.xdp_redirect; 46838c2ecf20Sopenharmony_ci break; 46848c2ecf20Sopenharmony_ci case ETHTOOL_XDP_PASS: 46858c2ecf20Sopenharmony_ci pp->ethtool_stats[i] = stats.ps.xdp_pass; 46868c2ecf20Sopenharmony_ci break; 46878c2ecf20Sopenharmony_ci case ETHTOOL_XDP_DROP: 46888c2ecf20Sopenharmony_ci pp->ethtool_stats[i] = stats.ps.xdp_drop; 46898c2ecf20Sopenharmony_ci break; 46908c2ecf20Sopenharmony_ci case ETHTOOL_XDP_TX: 46918c2ecf20Sopenharmony_ci pp->ethtool_stats[i] = stats.ps.xdp_tx; 46928c2ecf20Sopenharmony_ci break; 46938c2ecf20Sopenharmony_ci case ETHTOOL_XDP_TX_ERR: 46948c2ecf20Sopenharmony_ci pp->ethtool_stats[i] = stats.ps.xdp_tx_err; 46958c2ecf20Sopenharmony_ci break; 46968c2ecf20Sopenharmony_ci case ETHTOOL_XDP_XMIT: 46978c2ecf20Sopenharmony_ci pp->ethtool_stats[i] = stats.ps.xdp_xmit; 46988c2ecf20Sopenharmony_ci break; 46998c2ecf20Sopenharmony_ci case ETHTOOL_XDP_XMIT_ERR: 47008c2ecf20Sopenharmony_ci pp->ethtool_stats[i] = stats.ps.xdp_xmit_err; 47018c2ecf20Sopenharmony_ci break; 47028c2ecf20Sopenharmony_ci } 47038c2ecf20Sopenharmony_ci break; 47048c2ecf20Sopenharmony_ci } 47058c2ecf20Sopenharmony_ci } 47068c2ecf20Sopenharmony_ci} 47078c2ecf20Sopenharmony_ci 47088c2ecf20Sopenharmony_cistatic void mvneta_ethtool_get_stats(struct net_device *dev, 47098c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 47108c2ecf20Sopenharmony_ci{ 47118c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 47128c2ecf20Sopenharmony_ci int i; 47138c2ecf20Sopenharmony_ci 47148c2ecf20Sopenharmony_ci mvneta_ethtool_update_stats(pp); 47158c2ecf20Sopenharmony_ci 47168c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mvneta_statistics); i++) 47178c2ecf20Sopenharmony_ci *data++ = pp->ethtool_stats[i]; 47188c2ecf20Sopenharmony_ci} 47198c2ecf20Sopenharmony_ci 47208c2ecf20Sopenharmony_cistatic int mvneta_ethtool_get_sset_count(struct net_device *dev, int sset) 47218c2ecf20Sopenharmony_ci{ 47228c2ecf20Sopenharmony_ci if (sset == ETH_SS_STATS) 47238c2ecf20Sopenharmony_ci return ARRAY_SIZE(mvneta_statistics); 47248c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 47258c2ecf20Sopenharmony_ci} 47268c2ecf20Sopenharmony_ci 47278c2ecf20Sopenharmony_cistatic u32 mvneta_ethtool_get_rxfh_indir_size(struct net_device *dev) 47288c2ecf20Sopenharmony_ci{ 47298c2ecf20Sopenharmony_ci return MVNETA_RSS_LU_TABLE_SIZE; 47308c2ecf20Sopenharmony_ci} 47318c2ecf20Sopenharmony_ci 47328c2ecf20Sopenharmony_cistatic int mvneta_ethtool_get_rxnfc(struct net_device *dev, 47338c2ecf20Sopenharmony_ci struct ethtool_rxnfc *info, 47348c2ecf20Sopenharmony_ci u32 *rules __always_unused) 47358c2ecf20Sopenharmony_ci{ 47368c2ecf20Sopenharmony_ci switch (info->cmd) { 47378c2ecf20Sopenharmony_ci case ETHTOOL_GRXRINGS: 47388c2ecf20Sopenharmony_ci info->data = rxq_number; 47398c2ecf20Sopenharmony_ci return 0; 47408c2ecf20Sopenharmony_ci case ETHTOOL_GRXFH: 47418c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 47428c2ecf20Sopenharmony_ci default: 47438c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 47448c2ecf20Sopenharmony_ci } 47458c2ecf20Sopenharmony_ci} 47468c2ecf20Sopenharmony_ci 47478c2ecf20Sopenharmony_cistatic int mvneta_config_rss(struct mvneta_port *pp) 47488c2ecf20Sopenharmony_ci{ 47498c2ecf20Sopenharmony_ci int cpu; 47508c2ecf20Sopenharmony_ci u32 val; 47518c2ecf20Sopenharmony_ci 47528c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(pp->dev); 47538c2ecf20Sopenharmony_ci 47548c2ecf20Sopenharmony_ci on_each_cpu(mvneta_percpu_mask_interrupt, pp, true); 47558c2ecf20Sopenharmony_ci 47568c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) { 47578c2ecf20Sopenharmony_ci /* We have to synchronise on the napi of each CPU */ 47588c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) { 47598c2ecf20Sopenharmony_ci struct mvneta_pcpu_port *pcpu_port = 47608c2ecf20Sopenharmony_ci per_cpu_ptr(pp->ports, cpu); 47618c2ecf20Sopenharmony_ci 47628c2ecf20Sopenharmony_ci napi_synchronize(&pcpu_port->napi); 47638c2ecf20Sopenharmony_ci napi_disable(&pcpu_port->napi); 47648c2ecf20Sopenharmony_ci } 47658c2ecf20Sopenharmony_ci } else { 47668c2ecf20Sopenharmony_ci napi_synchronize(&pp->napi); 47678c2ecf20Sopenharmony_ci napi_disable(&pp->napi); 47688c2ecf20Sopenharmony_ci } 47698c2ecf20Sopenharmony_ci 47708c2ecf20Sopenharmony_ci pp->rxq_def = pp->indir[0]; 47718c2ecf20Sopenharmony_ci 47728c2ecf20Sopenharmony_ci /* Update unicast mapping */ 47738c2ecf20Sopenharmony_ci mvneta_set_rx_mode(pp->dev); 47748c2ecf20Sopenharmony_ci 47758c2ecf20Sopenharmony_ci /* Update val of portCfg register accordingly with all RxQueue types */ 47768c2ecf20Sopenharmony_ci val = MVNETA_PORT_CONFIG_DEFL_VALUE(pp->rxq_def); 47778c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_PORT_CONFIG, val); 47788c2ecf20Sopenharmony_ci 47798c2ecf20Sopenharmony_ci /* Update the elected CPU matching the new rxq_def */ 47808c2ecf20Sopenharmony_ci spin_lock(&pp->lock); 47818c2ecf20Sopenharmony_ci mvneta_percpu_elect(pp); 47828c2ecf20Sopenharmony_ci spin_unlock(&pp->lock); 47838c2ecf20Sopenharmony_ci 47848c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) { 47858c2ecf20Sopenharmony_ci /* We have to synchronise on the napi of each CPU */ 47868c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) { 47878c2ecf20Sopenharmony_ci struct mvneta_pcpu_port *pcpu_port = 47888c2ecf20Sopenharmony_ci per_cpu_ptr(pp->ports, cpu); 47898c2ecf20Sopenharmony_ci 47908c2ecf20Sopenharmony_ci napi_enable(&pcpu_port->napi); 47918c2ecf20Sopenharmony_ci } 47928c2ecf20Sopenharmony_ci } else { 47938c2ecf20Sopenharmony_ci napi_enable(&pp->napi); 47948c2ecf20Sopenharmony_ci } 47958c2ecf20Sopenharmony_ci 47968c2ecf20Sopenharmony_ci netif_tx_start_all_queues(pp->dev); 47978c2ecf20Sopenharmony_ci 47988c2ecf20Sopenharmony_ci return 0; 47998c2ecf20Sopenharmony_ci} 48008c2ecf20Sopenharmony_ci 48018c2ecf20Sopenharmony_cistatic int mvneta_ethtool_set_rxfh(struct net_device *dev, const u32 *indir, 48028c2ecf20Sopenharmony_ci const u8 *key, const u8 hfunc) 48038c2ecf20Sopenharmony_ci{ 48048c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 48058c2ecf20Sopenharmony_ci 48068c2ecf20Sopenharmony_ci /* Current code for Armada 3700 doesn't support RSS features yet */ 48078c2ecf20Sopenharmony_ci if (pp->neta_armada3700) 48088c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 48098c2ecf20Sopenharmony_ci 48108c2ecf20Sopenharmony_ci /* We require at least one supported parameter to be changed 48118c2ecf20Sopenharmony_ci * and no change in any of the unsupported parameters 48128c2ecf20Sopenharmony_ci */ 48138c2ecf20Sopenharmony_ci if (key || 48148c2ecf20Sopenharmony_ci (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)) 48158c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 48168c2ecf20Sopenharmony_ci 48178c2ecf20Sopenharmony_ci if (!indir) 48188c2ecf20Sopenharmony_ci return 0; 48198c2ecf20Sopenharmony_ci 48208c2ecf20Sopenharmony_ci memcpy(pp->indir, indir, MVNETA_RSS_LU_TABLE_SIZE); 48218c2ecf20Sopenharmony_ci 48228c2ecf20Sopenharmony_ci return mvneta_config_rss(pp); 48238c2ecf20Sopenharmony_ci} 48248c2ecf20Sopenharmony_ci 48258c2ecf20Sopenharmony_cistatic int mvneta_ethtool_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, 48268c2ecf20Sopenharmony_ci u8 *hfunc) 48278c2ecf20Sopenharmony_ci{ 48288c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 48298c2ecf20Sopenharmony_ci 48308c2ecf20Sopenharmony_ci /* Current code for Armada 3700 doesn't support RSS features yet */ 48318c2ecf20Sopenharmony_ci if (pp->neta_armada3700) 48328c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 48338c2ecf20Sopenharmony_ci 48348c2ecf20Sopenharmony_ci if (hfunc) 48358c2ecf20Sopenharmony_ci *hfunc = ETH_RSS_HASH_TOP; 48368c2ecf20Sopenharmony_ci 48378c2ecf20Sopenharmony_ci if (!indir) 48388c2ecf20Sopenharmony_ci return 0; 48398c2ecf20Sopenharmony_ci 48408c2ecf20Sopenharmony_ci memcpy(indir, pp->indir, MVNETA_RSS_LU_TABLE_SIZE); 48418c2ecf20Sopenharmony_ci 48428c2ecf20Sopenharmony_ci return 0; 48438c2ecf20Sopenharmony_ci} 48448c2ecf20Sopenharmony_ci 48458c2ecf20Sopenharmony_cistatic void mvneta_ethtool_get_wol(struct net_device *dev, 48468c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wol) 48478c2ecf20Sopenharmony_ci{ 48488c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 48498c2ecf20Sopenharmony_ci 48508c2ecf20Sopenharmony_ci phylink_ethtool_get_wol(pp->phylink, wol); 48518c2ecf20Sopenharmony_ci} 48528c2ecf20Sopenharmony_ci 48538c2ecf20Sopenharmony_cistatic int mvneta_ethtool_set_wol(struct net_device *dev, 48548c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wol) 48558c2ecf20Sopenharmony_ci{ 48568c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 48578c2ecf20Sopenharmony_ci int ret; 48588c2ecf20Sopenharmony_ci 48598c2ecf20Sopenharmony_ci ret = phylink_ethtool_set_wol(pp->phylink, wol); 48608c2ecf20Sopenharmony_ci if (!ret) 48618c2ecf20Sopenharmony_ci device_set_wakeup_enable(&dev->dev, !!wol->wolopts); 48628c2ecf20Sopenharmony_ci 48638c2ecf20Sopenharmony_ci return ret; 48648c2ecf20Sopenharmony_ci} 48658c2ecf20Sopenharmony_ci 48668c2ecf20Sopenharmony_cistatic int mvneta_ethtool_get_eee(struct net_device *dev, 48678c2ecf20Sopenharmony_ci struct ethtool_eee *eee) 48688c2ecf20Sopenharmony_ci{ 48698c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 48708c2ecf20Sopenharmony_ci u32 lpi_ctl0; 48718c2ecf20Sopenharmony_ci 48728c2ecf20Sopenharmony_ci lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0); 48738c2ecf20Sopenharmony_ci 48748c2ecf20Sopenharmony_ci eee->eee_enabled = pp->eee_enabled; 48758c2ecf20Sopenharmony_ci eee->eee_active = pp->eee_active; 48768c2ecf20Sopenharmony_ci eee->tx_lpi_enabled = pp->tx_lpi_enabled; 48778c2ecf20Sopenharmony_ci eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale; 48788c2ecf20Sopenharmony_ci 48798c2ecf20Sopenharmony_ci return phylink_ethtool_get_eee(pp->phylink, eee); 48808c2ecf20Sopenharmony_ci} 48818c2ecf20Sopenharmony_ci 48828c2ecf20Sopenharmony_cistatic int mvneta_ethtool_set_eee(struct net_device *dev, 48838c2ecf20Sopenharmony_ci struct ethtool_eee *eee) 48848c2ecf20Sopenharmony_ci{ 48858c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 48868c2ecf20Sopenharmony_ci u32 lpi_ctl0; 48878c2ecf20Sopenharmony_ci 48888c2ecf20Sopenharmony_ci /* The Armada 37x documents do not give limits for this other than 48898c2ecf20Sopenharmony_ci * it being an 8-bit register. */ 48908c2ecf20Sopenharmony_ci if (eee->tx_lpi_enabled && eee->tx_lpi_timer > 255) 48918c2ecf20Sopenharmony_ci return -EINVAL; 48928c2ecf20Sopenharmony_ci 48938c2ecf20Sopenharmony_ci lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0); 48948c2ecf20Sopenharmony_ci lpi_ctl0 &= ~(0xff << 8); 48958c2ecf20Sopenharmony_ci lpi_ctl0 |= eee->tx_lpi_timer << 8; 48968c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi_ctl0); 48978c2ecf20Sopenharmony_ci 48988c2ecf20Sopenharmony_ci pp->eee_enabled = eee->eee_enabled; 48998c2ecf20Sopenharmony_ci pp->tx_lpi_enabled = eee->tx_lpi_enabled; 49008c2ecf20Sopenharmony_ci 49018c2ecf20Sopenharmony_ci mvneta_set_eee(pp, eee->tx_lpi_enabled && eee->eee_enabled); 49028c2ecf20Sopenharmony_ci 49038c2ecf20Sopenharmony_ci return phylink_ethtool_set_eee(pp->phylink, eee); 49048c2ecf20Sopenharmony_ci} 49058c2ecf20Sopenharmony_ci 49068c2ecf20Sopenharmony_cistatic const struct net_device_ops mvneta_netdev_ops = { 49078c2ecf20Sopenharmony_ci .ndo_open = mvneta_open, 49088c2ecf20Sopenharmony_ci .ndo_stop = mvneta_stop, 49098c2ecf20Sopenharmony_ci .ndo_start_xmit = mvneta_tx, 49108c2ecf20Sopenharmony_ci .ndo_set_rx_mode = mvneta_set_rx_mode, 49118c2ecf20Sopenharmony_ci .ndo_set_mac_address = mvneta_set_mac_addr, 49128c2ecf20Sopenharmony_ci .ndo_change_mtu = mvneta_change_mtu, 49138c2ecf20Sopenharmony_ci .ndo_fix_features = mvneta_fix_features, 49148c2ecf20Sopenharmony_ci .ndo_get_stats64 = mvneta_get_stats64, 49158c2ecf20Sopenharmony_ci .ndo_do_ioctl = mvneta_ioctl, 49168c2ecf20Sopenharmony_ci .ndo_bpf = mvneta_xdp, 49178c2ecf20Sopenharmony_ci .ndo_xdp_xmit = mvneta_xdp_xmit, 49188c2ecf20Sopenharmony_ci}; 49198c2ecf20Sopenharmony_ci 49208c2ecf20Sopenharmony_cistatic const struct ethtool_ops mvneta_eth_tool_ops = { 49218c2ecf20Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | 49228c2ecf20Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES, 49238c2ecf20Sopenharmony_ci .nway_reset = mvneta_ethtool_nway_reset, 49248c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 49258c2ecf20Sopenharmony_ci .set_coalesce = mvneta_ethtool_set_coalesce, 49268c2ecf20Sopenharmony_ci .get_coalesce = mvneta_ethtool_get_coalesce, 49278c2ecf20Sopenharmony_ci .get_drvinfo = mvneta_ethtool_get_drvinfo, 49288c2ecf20Sopenharmony_ci .get_ringparam = mvneta_ethtool_get_ringparam, 49298c2ecf20Sopenharmony_ci .set_ringparam = mvneta_ethtool_set_ringparam, 49308c2ecf20Sopenharmony_ci .get_pauseparam = mvneta_ethtool_get_pauseparam, 49318c2ecf20Sopenharmony_ci .set_pauseparam = mvneta_ethtool_set_pauseparam, 49328c2ecf20Sopenharmony_ci .get_strings = mvneta_ethtool_get_strings, 49338c2ecf20Sopenharmony_ci .get_ethtool_stats = mvneta_ethtool_get_stats, 49348c2ecf20Sopenharmony_ci .get_sset_count = mvneta_ethtool_get_sset_count, 49358c2ecf20Sopenharmony_ci .get_rxfh_indir_size = mvneta_ethtool_get_rxfh_indir_size, 49368c2ecf20Sopenharmony_ci .get_rxnfc = mvneta_ethtool_get_rxnfc, 49378c2ecf20Sopenharmony_ci .get_rxfh = mvneta_ethtool_get_rxfh, 49388c2ecf20Sopenharmony_ci .set_rxfh = mvneta_ethtool_set_rxfh, 49398c2ecf20Sopenharmony_ci .get_link_ksettings = mvneta_ethtool_get_link_ksettings, 49408c2ecf20Sopenharmony_ci .set_link_ksettings = mvneta_ethtool_set_link_ksettings, 49418c2ecf20Sopenharmony_ci .get_wol = mvneta_ethtool_get_wol, 49428c2ecf20Sopenharmony_ci .set_wol = mvneta_ethtool_set_wol, 49438c2ecf20Sopenharmony_ci .get_eee = mvneta_ethtool_get_eee, 49448c2ecf20Sopenharmony_ci .set_eee = mvneta_ethtool_set_eee, 49458c2ecf20Sopenharmony_ci}; 49468c2ecf20Sopenharmony_ci 49478c2ecf20Sopenharmony_ci/* Initialize hw */ 49488c2ecf20Sopenharmony_cistatic int mvneta_init(struct device *dev, struct mvneta_port *pp) 49498c2ecf20Sopenharmony_ci{ 49508c2ecf20Sopenharmony_ci int queue; 49518c2ecf20Sopenharmony_ci 49528c2ecf20Sopenharmony_ci /* Disable port */ 49538c2ecf20Sopenharmony_ci mvneta_port_disable(pp); 49548c2ecf20Sopenharmony_ci 49558c2ecf20Sopenharmony_ci /* Set port default values */ 49568c2ecf20Sopenharmony_ci mvneta_defaults_set(pp); 49578c2ecf20Sopenharmony_ci 49588c2ecf20Sopenharmony_ci pp->txqs = devm_kcalloc(dev, txq_number, sizeof(*pp->txqs), GFP_KERNEL); 49598c2ecf20Sopenharmony_ci if (!pp->txqs) 49608c2ecf20Sopenharmony_ci return -ENOMEM; 49618c2ecf20Sopenharmony_ci 49628c2ecf20Sopenharmony_ci /* Initialize TX descriptor rings */ 49638c2ecf20Sopenharmony_ci for (queue = 0; queue < txq_number; queue++) { 49648c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq = &pp->txqs[queue]; 49658c2ecf20Sopenharmony_ci txq->id = queue; 49668c2ecf20Sopenharmony_ci txq->size = pp->tx_ring_size; 49678c2ecf20Sopenharmony_ci txq->done_pkts_coal = MVNETA_TXDONE_COAL_PKTS; 49688c2ecf20Sopenharmony_ci } 49698c2ecf20Sopenharmony_ci 49708c2ecf20Sopenharmony_ci pp->rxqs = devm_kcalloc(dev, rxq_number, sizeof(*pp->rxqs), GFP_KERNEL); 49718c2ecf20Sopenharmony_ci if (!pp->rxqs) 49728c2ecf20Sopenharmony_ci return -ENOMEM; 49738c2ecf20Sopenharmony_ci 49748c2ecf20Sopenharmony_ci /* Create Rx descriptor rings */ 49758c2ecf20Sopenharmony_ci for (queue = 0; queue < rxq_number; queue++) { 49768c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; 49778c2ecf20Sopenharmony_ci rxq->id = queue; 49788c2ecf20Sopenharmony_ci rxq->size = pp->rx_ring_size; 49798c2ecf20Sopenharmony_ci rxq->pkts_coal = MVNETA_RX_COAL_PKTS; 49808c2ecf20Sopenharmony_ci rxq->time_coal = MVNETA_RX_COAL_USEC; 49818c2ecf20Sopenharmony_ci rxq->buf_virt_addr 49828c2ecf20Sopenharmony_ci = devm_kmalloc_array(pp->dev->dev.parent, 49838c2ecf20Sopenharmony_ci rxq->size, 49848c2ecf20Sopenharmony_ci sizeof(*rxq->buf_virt_addr), 49858c2ecf20Sopenharmony_ci GFP_KERNEL); 49868c2ecf20Sopenharmony_ci if (!rxq->buf_virt_addr) 49878c2ecf20Sopenharmony_ci return -ENOMEM; 49888c2ecf20Sopenharmony_ci } 49898c2ecf20Sopenharmony_ci 49908c2ecf20Sopenharmony_ci return 0; 49918c2ecf20Sopenharmony_ci} 49928c2ecf20Sopenharmony_ci 49938c2ecf20Sopenharmony_ci/* platform glue : initialize decoding windows */ 49948c2ecf20Sopenharmony_cistatic void mvneta_conf_mbus_windows(struct mvneta_port *pp, 49958c2ecf20Sopenharmony_ci const struct mbus_dram_target_info *dram) 49968c2ecf20Sopenharmony_ci{ 49978c2ecf20Sopenharmony_ci u32 win_enable; 49988c2ecf20Sopenharmony_ci u32 win_protect; 49998c2ecf20Sopenharmony_ci int i; 50008c2ecf20Sopenharmony_ci 50018c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) { 50028c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_WIN_BASE(i), 0); 50038c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_WIN_SIZE(i), 0); 50048c2ecf20Sopenharmony_ci 50058c2ecf20Sopenharmony_ci if (i < 4) 50068c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_WIN_REMAP(i), 0); 50078c2ecf20Sopenharmony_ci } 50088c2ecf20Sopenharmony_ci 50098c2ecf20Sopenharmony_ci win_enable = 0x3f; 50108c2ecf20Sopenharmony_ci win_protect = 0; 50118c2ecf20Sopenharmony_ci 50128c2ecf20Sopenharmony_ci if (dram) { 50138c2ecf20Sopenharmony_ci for (i = 0; i < dram->num_cs; i++) { 50148c2ecf20Sopenharmony_ci const struct mbus_dram_window *cs = dram->cs + i; 50158c2ecf20Sopenharmony_ci 50168c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_WIN_BASE(i), 50178c2ecf20Sopenharmony_ci (cs->base & 0xffff0000) | 50188c2ecf20Sopenharmony_ci (cs->mbus_attr << 8) | 50198c2ecf20Sopenharmony_ci dram->mbus_dram_target_id); 50208c2ecf20Sopenharmony_ci 50218c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_WIN_SIZE(i), 50228c2ecf20Sopenharmony_ci (cs->size - 1) & 0xffff0000); 50238c2ecf20Sopenharmony_ci 50248c2ecf20Sopenharmony_ci win_enable &= ~(1 << i); 50258c2ecf20Sopenharmony_ci win_protect |= 3 << (2 * i); 50268c2ecf20Sopenharmony_ci } 50278c2ecf20Sopenharmony_ci } else { 50288c2ecf20Sopenharmony_ci /* For Armada3700 open default 4GB Mbus window, leaving 50298c2ecf20Sopenharmony_ci * arbitration of target/attribute to a different layer 50308c2ecf20Sopenharmony_ci * of configuration. 50318c2ecf20Sopenharmony_ci */ 50328c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_WIN_SIZE(0), 0xffff0000); 50338c2ecf20Sopenharmony_ci win_enable &= ~BIT(0); 50348c2ecf20Sopenharmony_ci win_protect = 3; 50358c2ecf20Sopenharmony_ci } 50368c2ecf20Sopenharmony_ci 50378c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_BASE_ADDR_ENABLE, win_enable); 50388c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_ACCESS_PROTECT_ENABLE, win_protect); 50398c2ecf20Sopenharmony_ci} 50408c2ecf20Sopenharmony_ci 50418c2ecf20Sopenharmony_ci/* Power up the port */ 50428c2ecf20Sopenharmony_cistatic int mvneta_port_power_up(struct mvneta_port *pp, int phy_mode) 50438c2ecf20Sopenharmony_ci{ 50448c2ecf20Sopenharmony_ci /* MAC Cause register should be cleared */ 50458c2ecf20Sopenharmony_ci mvreg_write(pp, MVNETA_UNIT_INTR_CAUSE, 0); 50468c2ecf20Sopenharmony_ci 50478c2ecf20Sopenharmony_ci if (phy_mode != PHY_INTERFACE_MODE_QSGMII && 50488c2ecf20Sopenharmony_ci phy_mode != PHY_INTERFACE_MODE_SGMII && 50498c2ecf20Sopenharmony_ci !phy_interface_mode_is_8023z(phy_mode) && 50508c2ecf20Sopenharmony_ci !phy_interface_mode_is_rgmii(phy_mode)) 50518c2ecf20Sopenharmony_ci return -EINVAL; 50528c2ecf20Sopenharmony_ci 50538c2ecf20Sopenharmony_ci return 0; 50548c2ecf20Sopenharmony_ci} 50558c2ecf20Sopenharmony_ci 50568c2ecf20Sopenharmony_ci/* Device initialization routine */ 50578c2ecf20Sopenharmony_cistatic int mvneta_probe(struct platform_device *pdev) 50588c2ecf20Sopenharmony_ci{ 50598c2ecf20Sopenharmony_ci struct device_node *dn = pdev->dev.of_node; 50608c2ecf20Sopenharmony_ci struct device_node *bm_node; 50618c2ecf20Sopenharmony_ci struct mvneta_port *pp; 50628c2ecf20Sopenharmony_ci struct net_device *dev; 50638c2ecf20Sopenharmony_ci struct phylink *phylink; 50648c2ecf20Sopenharmony_ci struct phy *comphy; 50658c2ecf20Sopenharmony_ci const char *dt_mac_addr; 50668c2ecf20Sopenharmony_ci char hw_mac_addr[ETH_ALEN]; 50678c2ecf20Sopenharmony_ci phy_interface_t phy_mode; 50688c2ecf20Sopenharmony_ci const char *mac_from; 50698c2ecf20Sopenharmony_ci int tx_csum_limit; 50708c2ecf20Sopenharmony_ci int err; 50718c2ecf20Sopenharmony_ci int cpu; 50728c2ecf20Sopenharmony_ci 50738c2ecf20Sopenharmony_ci dev = devm_alloc_etherdev_mqs(&pdev->dev, sizeof(struct mvneta_port), 50748c2ecf20Sopenharmony_ci txq_number, rxq_number); 50758c2ecf20Sopenharmony_ci if (!dev) 50768c2ecf20Sopenharmony_ci return -ENOMEM; 50778c2ecf20Sopenharmony_ci 50788c2ecf20Sopenharmony_ci dev->irq = irq_of_parse_and_map(dn, 0); 50798c2ecf20Sopenharmony_ci if (dev->irq == 0) 50808c2ecf20Sopenharmony_ci return -EINVAL; 50818c2ecf20Sopenharmony_ci 50828c2ecf20Sopenharmony_ci err = of_get_phy_mode(dn, &phy_mode); 50838c2ecf20Sopenharmony_ci if (err) { 50848c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "incorrect phy-mode\n"); 50858c2ecf20Sopenharmony_ci goto err_free_irq; 50868c2ecf20Sopenharmony_ci } 50878c2ecf20Sopenharmony_ci 50888c2ecf20Sopenharmony_ci comphy = devm_of_phy_get(&pdev->dev, dn, NULL); 50898c2ecf20Sopenharmony_ci if (comphy == ERR_PTR(-EPROBE_DEFER)) { 50908c2ecf20Sopenharmony_ci err = -EPROBE_DEFER; 50918c2ecf20Sopenharmony_ci goto err_free_irq; 50928c2ecf20Sopenharmony_ci } else if (IS_ERR(comphy)) { 50938c2ecf20Sopenharmony_ci comphy = NULL; 50948c2ecf20Sopenharmony_ci } 50958c2ecf20Sopenharmony_ci 50968c2ecf20Sopenharmony_ci pp = netdev_priv(dev); 50978c2ecf20Sopenharmony_ci spin_lock_init(&pp->lock); 50988c2ecf20Sopenharmony_ci 50998c2ecf20Sopenharmony_ci pp->phylink_config.dev = &dev->dev; 51008c2ecf20Sopenharmony_ci pp->phylink_config.type = PHYLINK_NETDEV; 51018c2ecf20Sopenharmony_ci 51028c2ecf20Sopenharmony_ci phylink = phylink_create(&pp->phylink_config, pdev->dev.fwnode, 51038c2ecf20Sopenharmony_ci phy_mode, &mvneta_phylink_ops); 51048c2ecf20Sopenharmony_ci if (IS_ERR(phylink)) { 51058c2ecf20Sopenharmony_ci err = PTR_ERR(phylink); 51068c2ecf20Sopenharmony_ci goto err_free_irq; 51078c2ecf20Sopenharmony_ci } 51088c2ecf20Sopenharmony_ci 51098c2ecf20Sopenharmony_ci dev->tx_queue_len = MVNETA_MAX_TXD; 51108c2ecf20Sopenharmony_ci dev->watchdog_timeo = 5 * HZ; 51118c2ecf20Sopenharmony_ci dev->netdev_ops = &mvneta_netdev_ops; 51128c2ecf20Sopenharmony_ci 51138c2ecf20Sopenharmony_ci dev->ethtool_ops = &mvneta_eth_tool_ops; 51148c2ecf20Sopenharmony_ci 51158c2ecf20Sopenharmony_ci pp->phylink = phylink; 51168c2ecf20Sopenharmony_ci pp->comphy = comphy; 51178c2ecf20Sopenharmony_ci pp->phy_interface = phy_mode; 51188c2ecf20Sopenharmony_ci pp->dn = dn; 51198c2ecf20Sopenharmony_ci 51208c2ecf20Sopenharmony_ci pp->rxq_def = rxq_def; 51218c2ecf20Sopenharmony_ci pp->indir[0] = rxq_def; 51228c2ecf20Sopenharmony_ci 51238c2ecf20Sopenharmony_ci /* Get special SoC configurations */ 51248c2ecf20Sopenharmony_ci if (of_device_is_compatible(dn, "marvell,armada-3700-neta")) 51258c2ecf20Sopenharmony_ci pp->neta_armada3700 = true; 51268c2ecf20Sopenharmony_ci 51278c2ecf20Sopenharmony_ci pp->clk = devm_clk_get(&pdev->dev, "core"); 51288c2ecf20Sopenharmony_ci if (IS_ERR(pp->clk)) 51298c2ecf20Sopenharmony_ci pp->clk = devm_clk_get(&pdev->dev, NULL); 51308c2ecf20Sopenharmony_ci if (IS_ERR(pp->clk)) { 51318c2ecf20Sopenharmony_ci err = PTR_ERR(pp->clk); 51328c2ecf20Sopenharmony_ci goto err_free_phylink; 51338c2ecf20Sopenharmony_ci } 51348c2ecf20Sopenharmony_ci 51358c2ecf20Sopenharmony_ci clk_prepare_enable(pp->clk); 51368c2ecf20Sopenharmony_ci 51378c2ecf20Sopenharmony_ci pp->clk_bus = devm_clk_get(&pdev->dev, "bus"); 51388c2ecf20Sopenharmony_ci if (!IS_ERR(pp->clk_bus)) 51398c2ecf20Sopenharmony_ci clk_prepare_enable(pp->clk_bus); 51408c2ecf20Sopenharmony_ci 51418c2ecf20Sopenharmony_ci pp->base = devm_platform_ioremap_resource(pdev, 0); 51428c2ecf20Sopenharmony_ci if (IS_ERR(pp->base)) { 51438c2ecf20Sopenharmony_ci err = PTR_ERR(pp->base); 51448c2ecf20Sopenharmony_ci goto err_clk; 51458c2ecf20Sopenharmony_ci } 51468c2ecf20Sopenharmony_ci 51478c2ecf20Sopenharmony_ci /* Alloc per-cpu port structure */ 51488c2ecf20Sopenharmony_ci pp->ports = alloc_percpu(struct mvneta_pcpu_port); 51498c2ecf20Sopenharmony_ci if (!pp->ports) { 51508c2ecf20Sopenharmony_ci err = -ENOMEM; 51518c2ecf20Sopenharmony_ci goto err_clk; 51528c2ecf20Sopenharmony_ci } 51538c2ecf20Sopenharmony_ci 51548c2ecf20Sopenharmony_ci /* Alloc per-cpu stats */ 51558c2ecf20Sopenharmony_ci pp->stats = netdev_alloc_pcpu_stats(struct mvneta_pcpu_stats); 51568c2ecf20Sopenharmony_ci if (!pp->stats) { 51578c2ecf20Sopenharmony_ci err = -ENOMEM; 51588c2ecf20Sopenharmony_ci goto err_free_ports; 51598c2ecf20Sopenharmony_ci } 51608c2ecf20Sopenharmony_ci 51618c2ecf20Sopenharmony_ci dt_mac_addr = of_get_mac_address(dn); 51628c2ecf20Sopenharmony_ci if (!IS_ERR(dt_mac_addr)) { 51638c2ecf20Sopenharmony_ci mac_from = "device tree"; 51648c2ecf20Sopenharmony_ci ether_addr_copy(dev->dev_addr, dt_mac_addr); 51658c2ecf20Sopenharmony_ci } else { 51668c2ecf20Sopenharmony_ci mvneta_get_mac_addr(pp, hw_mac_addr); 51678c2ecf20Sopenharmony_ci if (is_valid_ether_addr(hw_mac_addr)) { 51688c2ecf20Sopenharmony_ci mac_from = "hardware"; 51698c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, hw_mac_addr, ETH_ALEN); 51708c2ecf20Sopenharmony_ci } else { 51718c2ecf20Sopenharmony_ci mac_from = "random"; 51728c2ecf20Sopenharmony_ci eth_hw_addr_random(dev); 51738c2ecf20Sopenharmony_ci } 51748c2ecf20Sopenharmony_ci } 51758c2ecf20Sopenharmony_ci 51768c2ecf20Sopenharmony_ci if (!of_property_read_u32(dn, "tx-csum-limit", &tx_csum_limit)) { 51778c2ecf20Sopenharmony_ci if (tx_csum_limit < 0 || 51788c2ecf20Sopenharmony_ci tx_csum_limit > MVNETA_TX_CSUM_MAX_SIZE) { 51798c2ecf20Sopenharmony_ci tx_csum_limit = MVNETA_TX_CSUM_DEF_SIZE; 51808c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 51818c2ecf20Sopenharmony_ci "Wrong TX csum limit in DT, set to %dB\n", 51828c2ecf20Sopenharmony_ci MVNETA_TX_CSUM_DEF_SIZE); 51838c2ecf20Sopenharmony_ci } 51848c2ecf20Sopenharmony_ci } else if (of_device_is_compatible(dn, "marvell,armada-370-neta")) { 51858c2ecf20Sopenharmony_ci tx_csum_limit = MVNETA_TX_CSUM_DEF_SIZE; 51868c2ecf20Sopenharmony_ci } else { 51878c2ecf20Sopenharmony_ci tx_csum_limit = MVNETA_TX_CSUM_MAX_SIZE; 51888c2ecf20Sopenharmony_ci } 51898c2ecf20Sopenharmony_ci 51908c2ecf20Sopenharmony_ci pp->tx_csum_limit = tx_csum_limit; 51918c2ecf20Sopenharmony_ci 51928c2ecf20Sopenharmony_ci pp->dram_target_info = mv_mbus_dram_info(); 51938c2ecf20Sopenharmony_ci /* Armada3700 requires setting default configuration of Mbus 51948c2ecf20Sopenharmony_ci * windows, however without using filled mbus_dram_target_info 51958c2ecf20Sopenharmony_ci * structure. 51968c2ecf20Sopenharmony_ci */ 51978c2ecf20Sopenharmony_ci if (pp->dram_target_info || pp->neta_armada3700) 51988c2ecf20Sopenharmony_ci mvneta_conf_mbus_windows(pp, pp->dram_target_info); 51998c2ecf20Sopenharmony_ci 52008c2ecf20Sopenharmony_ci pp->tx_ring_size = MVNETA_MAX_TXD; 52018c2ecf20Sopenharmony_ci pp->rx_ring_size = MVNETA_MAX_RXD; 52028c2ecf20Sopenharmony_ci 52038c2ecf20Sopenharmony_ci pp->dev = dev; 52048c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 52058c2ecf20Sopenharmony_ci 52068c2ecf20Sopenharmony_ci pp->id = global_port_id++; 52078c2ecf20Sopenharmony_ci 52088c2ecf20Sopenharmony_ci /* Obtain access to BM resources if enabled and already initialized */ 52098c2ecf20Sopenharmony_ci bm_node = of_parse_phandle(dn, "buffer-manager", 0); 52108c2ecf20Sopenharmony_ci if (bm_node) { 52118c2ecf20Sopenharmony_ci pp->bm_priv = mvneta_bm_get(bm_node); 52128c2ecf20Sopenharmony_ci if (pp->bm_priv) { 52138c2ecf20Sopenharmony_ci err = mvneta_bm_port_init(pdev, pp); 52148c2ecf20Sopenharmony_ci if (err < 0) { 52158c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 52168c2ecf20Sopenharmony_ci "use SW buffer management\n"); 52178c2ecf20Sopenharmony_ci mvneta_bm_put(pp->bm_priv); 52188c2ecf20Sopenharmony_ci pp->bm_priv = NULL; 52198c2ecf20Sopenharmony_ci } 52208c2ecf20Sopenharmony_ci } 52218c2ecf20Sopenharmony_ci /* Set RX packet offset correction for platforms, whose 52228c2ecf20Sopenharmony_ci * NET_SKB_PAD, exceeds 64B. It should be 64B for 64-bit 52238c2ecf20Sopenharmony_ci * platforms and 0B for 32-bit ones. 52248c2ecf20Sopenharmony_ci */ 52258c2ecf20Sopenharmony_ci pp->rx_offset_correction = max(0, 52268c2ecf20Sopenharmony_ci NET_SKB_PAD - 52278c2ecf20Sopenharmony_ci MVNETA_RX_PKT_OFFSET_CORRECTION); 52288c2ecf20Sopenharmony_ci } 52298c2ecf20Sopenharmony_ci of_node_put(bm_node); 52308c2ecf20Sopenharmony_ci 52318c2ecf20Sopenharmony_ci /* sw buffer management */ 52328c2ecf20Sopenharmony_ci if (!pp->bm_priv) 52338c2ecf20Sopenharmony_ci pp->rx_offset_correction = MVNETA_SKB_HEADROOM; 52348c2ecf20Sopenharmony_ci 52358c2ecf20Sopenharmony_ci err = mvneta_init(&pdev->dev, pp); 52368c2ecf20Sopenharmony_ci if (err < 0) 52378c2ecf20Sopenharmony_ci goto err_netdev; 52388c2ecf20Sopenharmony_ci 52398c2ecf20Sopenharmony_ci err = mvneta_port_power_up(pp, pp->phy_interface); 52408c2ecf20Sopenharmony_ci if (err < 0) { 52418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't power up port\n"); 52428c2ecf20Sopenharmony_ci goto err_netdev; 52438c2ecf20Sopenharmony_ci } 52448c2ecf20Sopenharmony_ci 52458c2ecf20Sopenharmony_ci /* Armada3700 network controller does not support per-cpu 52468c2ecf20Sopenharmony_ci * operation, so only single NAPI should be initialized. 52478c2ecf20Sopenharmony_ci */ 52488c2ecf20Sopenharmony_ci if (pp->neta_armada3700) { 52498c2ecf20Sopenharmony_ci netif_napi_add(dev, &pp->napi, mvneta_poll, NAPI_POLL_WEIGHT); 52508c2ecf20Sopenharmony_ci } else { 52518c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { 52528c2ecf20Sopenharmony_ci struct mvneta_pcpu_port *port = 52538c2ecf20Sopenharmony_ci per_cpu_ptr(pp->ports, cpu); 52548c2ecf20Sopenharmony_ci 52558c2ecf20Sopenharmony_ci netif_napi_add(dev, &port->napi, mvneta_poll, 52568c2ecf20Sopenharmony_ci NAPI_POLL_WEIGHT); 52578c2ecf20Sopenharmony_ci port->pp = pp; 52588c2ecf20Sopenharmony_ci } 52598c2ecf20Sopenharmony_ci } 52608c2ecf20Sopenharmony_ci 52618c2ecf20Sopenharmony_ci dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | 52628c2ecf20Sopenharmony_ci NETIF_F_TSO | NETIF_F_RXCSUM; 52638c2ecf20Sopenharmony_ci dev->hw_features |= dev->features; 52648c2ecf20Sopenharmony_ci dev->vlan_features |= dev->features; 52658c2ecf20Sopenharmony_ci dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; 52668c2ecf20Sopenharmony_ci dev->gso_max_segs = MVNETA_MAX_TSO_SEGS; 52678c2ecf20Sopenharmony_ci 52688c2ecf20Sopenharmony_ci /* MTU range: 68 - 9676 */ 52698c2ecf20Sopenharmony_ci dev->min_mtu = ETH_MIN_MTU; 52708c2ecf20Sopenharmony_ci /* 9676 == 9700 - 20 and rounding to 8 */ 52718c2ecf20Sopenharmony_ci dev->max_mtu = 9676; 52728c2ecf20Sopenharmony_ci 52738c2ecf20Sopenharmony_ci err = register_netdev(dev); 52748c2ecf20Sopenharmony_ci if (err < 0) { 52758c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register\n"); 52768c2ecf20Sopenharmony_ci goto err_netdev; 52778c2ecf20Sopenharmony_ci } 52788c2ecf20Sopenharmony_ci 52798c2ecf20Sopenharmony_ci netdev_info(dev, "Using %s mac address %pM\n", mac_from, 52808c2ecf20Sopenharmony_ci dev->dev_addr); 52818c2ecf20Sopenharmony_ci 52828c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pp->dev); 52838c2ecf20Sopenharmony_ci 52848c2ecf20Sopenharmony_ci return 0; 52858c2ecf20Sopenharmony_ci 52868c2ecf20Sopenharmony_cierr_netdev: 52878c2ecf20Sopenharmony_ci if (pp->bm_priv) { 52888c2ecf20Sopenharmony_ci mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_long, 1 << pp->id); 52898c2ecf20Sopenharmony_ci mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_short, 52908c2ecf20Sopenharmony_ci 1 << pp->id); 52918c2ecf20Sopenharmony_ci mvneta_bm_put(pp->bm_priv); 52928c2ecf20Sopenharmony_ci } 52938c2ecf20Sopenharmony_ci free_percpu(pp->stats); 52948c2ecf20Sopenharmony_cierr_free_ports: 52958c2ecf20Sopenharmony_ci free_percpu(pp->ports); 52968c2ecf20Sopenharmony_cierr_clk: 52978c2ecf20Sopenharmony_ci clk_disable_unprepare(pp->clk_bus); 52988c2ecf20Sopenharmony_ci clk_disable_unprepare(pp->clk); 52998c2ecf20Sopenharmony_cierr_free_phylink: 53008c2ecf20Sopenharmony_ci if (pp->phylink) 53018c2ecf20Sopenharmony_ci phylink_destroy(pp->phylink); 53028c2ecf20Sopenharmony_cierr_free_irq: 53038c2ecf20Sopenharmony_ci irq_dispose_mapping(dev->irq); 53048c2ecf20Sopenharmony_ci return err; 53058c2ecf20Sopenharmony_ci} 53068c2ecf20Sopenharmony_ci 53078c2ecf20Sopenharmony_ci/* Device removal routine */ 53088c2ecf20Sopenharmony_cistatic int mvneta_remove(struct platform_device *pdev) 53098c2ecf20Sopenharmony_ci{ 53108c2ecf20Sopenharmony_ci struct net_device *dev = platform_get_drvdata(pdev); 53118c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 53128c2ecf20Sopenharmony_ci 53138c2ecf20Sopenharmony_ci unregister_netdev(dev); 53148c2ecf20Sopenharmony_ci clk_disable_unprepare(pp->clk_bus); 53158c2ecf20Sopenharmony_ci clk_disable_unprepare(pp->clk); 53168c2ecf20Sopenharmony_ci free_percpu(pp->ports); 53178c2ecf20Sopenharmony_ci free_percpu(pp->stats); 53188c2ecf20Sopenharmony_ci irq_dispose_mapping(dev->irq); 53198c2ecf20Sopenharmony_ci phylink_destroy(pp->phylink); 53208c2ecf20Sopenharmony_ci 53218c2ecf20Sopenharmony_ci if (pp->bm_priv) { 53228c2ecf20Sopenharmony_ci mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_long, 1 << pp->id); 53238c2ecf20Sopenharmony_ci mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_short, 53248c2ecf20Sopenharmony_ci 1 << pp->id); 53258c2ecf20Sopenharmony_ci mvneta_bm_put(pp->bm_priv); 53268c2ecf20Sopenharmony_ci } 53278c2ecf20Sopenharmony_ci 53288c2ecf20Sopenharmony_ci return 0; 53298c2ecf20Sopenharmony_ci} 53308c2ecf20Sopenharmony_ci 53318c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 53328c2ecf20Sopenharmony_cistatic int mvneta_suspend(struct device *device) 53338c2ecf20Sopenharmony_ci{ 53348c2ecf20Sopenharmony_ci int queue; 53358c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(device); 53368c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 53378c2ecf20Sopenharmony_ci 53388c2ecf20Sopenharmony_ci if (!netif_running(dev)) 53398c2ecf20Sopenharmony_ci goto clean_exit; 53408c2ecf20Sopenharmony_ci 53418c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) { 53428c2ecf20Sopenharmony_ci spin_lock(&pp->lock); 53438c2ecf20Sopenharmony_ci pp->is_stopped = true; 53448c2ecf20Sopenharmony_ci spin_unlock(&pp->lock); 53458c2ecf20Sopenharmony_ci 53468c2ecf20Sopenharmony_ci cpuhp_state_remove_instance_nocalls(online_hpstate, 53478c2ecf20Sopenharmony_ci &pp->node_online); 53488c2ecf20Sopenharmony_ci cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD, 53498c2ecf20Sopenharmony_ci &pp->node_dead); 53508c2ecf20Sopenharmony_ci } 53518c2ecf20Sopenharmony_ci 53528c2ecf20Sopenharmony_ci rtnl_lock(); 53538c2ecf20Sopenharmony_ci mvneta_stop_dev(pp); 53548c2ecf20Sopenharmony_ci rtnl_unlock(); 53558c2ecf20Sopenharmony_ci 53568c2ecf20Sopenharmony_ci for (queue = 0; queue < rxq_number; queue++) { 53578c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; 53588c2ecf20Sopenharmony_ci 53598c2ecf20Sopenharmony_ci mvneta_rxq_drop_pkts(pp, rxq); 53608c2ecf20Sopenharmony_ci } 53618c2ecf20Sopenharmony_ci 53628c2ecf20Sopenharmony_ci for (queue = 0; queue < txq_number; queue++) { 53638c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq = &pp->txqs[queue]; 53648c2ecf20Sopenharmony_ci 53658c2ecf20Sopenharmony_ci mvneta_txq_hw_deinit(pp, txq); 53668c2ecf20Sopenharmony_ci } 53678c2ecf20Sopenharmony_ci 53688c2ecf20Sopenharmony_ciclean_exit: 53698c2ecf20Sopenharmony_ci netif_device_detach(dev); 53708c2ecf20Sopenharmony_ci clk_disable_unprepare(pp->clk_bus); 53718c2ecf20Sopenharmony_ci clk_disable_unprepare(pp->clk); 53728c2ecf20Sopenharmony_ci 53738c2ecf20Sopenharmony_ci return 0; 53748c2ecf20Sopenharmony_ci} 53758c2ecf20Sopenharmony_ci 53768c2ecf20Sopenharmony_cistatic int mvneta_resume(struct device *device) 53778c2ecf20Sopenharmony_ci{ 53788c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(device); 53798c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(device); 53808c2ecf20Sopenharmony_ci struct mvneta_port *pp = netdev_priv(dev); 53818c2ecf20Sopenharmony_ci int err, queue; 53828c2ecf20Sopenharmony_ci 53838c2ecf20Sopenharmony_ci clk_prepare_enable(pp->clk); 53848c2ecf20Sopenharmony_ci if (!IS_ERR(pp->clk_bus)) 53858c2ecf20Sopenharmony_ci clk_prepare_enable(pp->clk_bus); 53868c2ecf20Sopenharmony_ci if (pp->dram_target_info || pp->neta_armada3700) 53878c2ecf20Sopenharmony_ci mvneta_conf_mbus_windows(pp, pp->dram_target_info); 53888c2ecf20Sopenharmony_ci if (pp->bm_priv) { 53898c2ecf20Sopenharmony_ci err = mvneta_bm_port_init(pdev, pp); 53908c2ecf20Sopenharmony_ci if (err < 0) { 53918c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "use SW buffer management\n"); 53928c2ecf20Sopenharmony_ci pp->rx_offset_correction = MVNETA_SKB_HEADROOM; 53938c2ecf20Sopenharmony_ci pp->bm_priv = NULL; 53948c2ecf20Sopenharmony_ci } 53958c2ecf20Sopenharmony_ci } 53968c2ecf20Sopenharmony_ci mvneta_defaults_set(pp); 53978c2ecf20Sopenharmony_ci err = mvneta_port_power_up(pp, pp->phy_interface); 53988c2ecf20Sopenharmony_ci if (err < 0) { 53998c2ecf20Sopenharmony_ci dev_err(device, "can't power up port\n"); 54008c2ecf20Sopenharmony_ci return err; 54018c2ecf20Sopenharmony_ci } 54028c2ecf20Sopenharmony_ci 54038c2ecf20Sopenharmony_ci netif_device_attach(dev); 54048c2ecf20Sopenharmony_ci 54058c2ecf20Sopenharmony_ci if (!netif_running(dev)) 54068c2ecf20Sopenharmony_ci return 0; 54078c2ecf20Sopenharmony_ci 54088c2ecf20Sopenharmony_ci for (queue = 0; queue < rxq_number; queue++) { 54098c2ecf20Sopenharmony_ci struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; 54108c2ecf20Sopenharmony_ci 54118c2ecf20Sopenharmony_ci rxq->next_desc_to_proc = 0; 54128c2ecf20Sopenharmony_ci mvneta_rxq_hw_init(pp, rxq); 54138c2ecf20Sopenharmony_ci } 54148c2ecf20Sopenharmony_ci 54158c2ecf20Sopenharmony_ci for (queue = 0; queue < txq_number; queue++) { 54168c2ecf20Sopenharmony_ci struct mvneta_tx_queue *txq = &pp->txqs[queue]; 54178c2ecf20Sopenharmony_ci 54188c2ecf20Sopenharmony_ci txq->next_desc_to_proc = 0; 54198c2ecf20Sopenharmony_ci mvneta_txq_hw_init(pp, txq); 54208c2ecf20Sopenharmony_ci } 54218c2ecf20Sopenharmony_ci 54228c2ecf20Sopenharmony_ci if (!pp->neta_armada3700) { 54238c2ecf20Sopenharmony_ci spin_lock(&pp->lock); 54248c2ecf20Sopenharmony_ci pp->is_stopped = false; 54258c2ecf20Sopenharmony_ci spin_unlock(&pp->lock); 54268c2ecf20Sopenharmony_ci cpuhp_state_add_instance_nocalls(online_hpstate, 54278c2ecf20Sopenharmony_ci &pp->node_online); 54288c2ecf20Sopenharmony_ci cpuhp_state_add_instance_nocalls(CPUHP_NET_MVNETA_DEAD, 54298c2ecf20Sopenharmony_ci &pp->node_dead); 54308c2ecf20Sopenharmony_ci } 54318c2ecf20Sopenharmony_ci 54328c2ecf20Sopenharmony_ci rtnl_lock(); 54338c2ecf20Sopenharmony_ci mvneta_start_dev(pp); 54348c2ecf20Sopenharmony_ci rtnl_unlock(); 54358c2ecf20Sopenharmony_ci mvneta_set_rx_mode(dev); 54368c2ecf20Sopenharmony_ci 54378c2ecf20Sopenharmony_ci return 0; 54388c2ecf20Sopenharmony_ci} 54398c2ecf20Sopenharmony_ci#endif 54408c2ecf20Sopenharmony_ci 54418c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mvneta_pm_ops, mvneta_suspend, mvneta_resume); 54428c2ecf20Sopenharmony_ci 54438c2ecf20Sopenharmony_cistatic const struct of_device_id mvneta_match[] = { 54448c2ecf20Sopenharmony_ci { .compatible = "marvell,armada-370-neta" }, 54458c2ecf20Sopenharmony_ci { .compatible = "marvell,armada-xp-neta" }, 54468c2ecf20Sopenharmony_ci { .compatible = "marvell,armada-3700-neta" }, 54478c2ecf20Sopenharmony_ci { } 54488c2ecf20Sopenharmony_ci}; 54498c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mvneta_match); 54508c2ecf20Sopenharmony_ci 54518c2ecf20Sopenharmony_cistatic struct platform_driver mvneta_driver = { 54528c2ecf20Sopenharmony_ci .probe = mvneta_probe, 54538c2ecf20Sopenharmony_ci .remove = mvneta_remove, 54548c2ecf20Sopenharmony_ci .driver = { 54558c2ecf20Sopenharmony_ci .name = MVNETA_DRIVER_NAME, 54568c2ecf20Sopenharmony_ci .of_match_table = mvneta_match, 54578c2ecf20Sopenharmony_ci .pm = &mvneta_pm_ops, 54588c2ecf20Sopenharmony_ci }, 54598c2ecf20Sopenharmony_ci}; 54608c2ecf20Sopenharmony_ci 54618c2ecf20Sopenharmony_cistatic int __init mvneta_driver_init(void) 54628c2ecf20Sopenharmony_ci{ 54638c2ecf20Sopenharmony_ci int ret; 54648c2ecf20Sopenharmony_ci 54658c2ecf20Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "net/mvneta:online", 54668c2ecf20Sopenharmony_ci mvneta_cpu_online, 54678c2ecf20Sopenharmony_ci mvneta_cpu_down_prepare); 54688c2ecf20Sopenharmony_ci if (ret < 0) 54698c2ecf20Sopenharmony_ci goto out; 54708c2ecf20Sopenharmony_ci online_hpstate = ret; 54718c2ecf20Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_NET_MVNETA_DEAD, "net/mvneta:dead", 54728c2ecf20Sopenharmony_ci NULL, mvneta_cpu_dead); 54738c2ecf20Sopenharmony_ci if (ret) 54748c2ecf20Sopenharmony_ci goto err_dead; 54758c2ecf20Sopenharmony_ci 54768c2ecf20Sopenharmony_ci ret = platform_driver_register(&mvneta_driver); 54778c2ecf20Sopenharmony_ci if (ret) 54788c2ecf20Sopenharmony_ci goto err; 54798c2ecf20Sopenharmony_ci return 0; 54808c2ecf20Sopenharmony_ci 54818c2ecf20Sopenharmony_cierr: 54828c2ecf20Sopenharmony_ci cpuhp_remove_multi_state(CPUHP_NET_MVNETA_DEAD); 54838c2ecf20Sopenharmony_cierr_dead: 54848c2ecf20Sopenharmony_ci cpuhp_remove_multi_state(online_hpstate); 54858c2ecf20Sopenharmony_ciout: 54868c2ecf20Sopenharmony_ci return ret; 54878c2ecf20Sopenharmony_ci} 54888c2ecf20Sopenharmony_cimodule_init(mvneta_driver_init); 54898c2ecf20Sopenharmony_ci 54908c2ecf20Sopenharmony_cistatic void __exit mvneta_driver_exit(void) 54918c2ecf20Sopenharmony_ci{ 54928c2ecf20Sopenharmony_ci platform_driver_unregister(&mvneta_driver); 54938c2ecf20Sopenharmony_ci cpuhp_remove_multi_state(CPUHP_NET_MVNETA_DEAD); 54948c2ecf20Sopenharmony_ci cpuhp_remove_multi_state(online_hpstate); 54958c2ecf20Sopenharmony_ci} 54968c2ecf20Sopenharmony_cimodule_exit(mvneta_driver_exit); 54978c2ecf20Sopenharmony_ci 54988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell NETA Ethernet Driver - www.marvell.com"); 54998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rami Rosen <rosenr@marvell.com>, Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); 55008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 55018c2ecf20Sopenharmony_ci 55028c2ecf20Sopenharmony_cimodule_param(rxq_number, int, 0444); 55038c2ecf20Sopenharmony_cimodule_param(txq_number, int, 0444); 55048c2ecf20Sopenharmony_ci 55058c2ecf20Sopenharmony_cimodule_param(rxq_def, int, 0444); 55068c2ecf20Sopenharmony_cimodule_param(rx_copybreak, int, 0644); 5507