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