162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for Marvell PPv2 network controller for Armada 375 SoC.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014 Marvell
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Marcin Wojtas <mw@semihalf.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/acpi.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/netdevice.h>
1362306a36Sopenharmony_ci#include <linux/etherdevice.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/skbuff.h>
1662306a36Sopenharmony_ci#include <linux/inetdevice.h>
1762306a36Sopenharmony_ci#include <linux/mbus.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/cpumask.h>
2262306a36Sopenharmony_ci#include <linux/of.h>
2362306a36Sopenharmony_ci#include <linux/of_irq.h>
2462306a36Sopenharmony_ci#include <linux/of_mdio.h>
2562306a36Sopenharmony_ci#include <linux/of_net.h>
2662306a36Sopenharmony_ci#include <linux/of_address.h>
2762306a36Sopenharmony_ci#include <linux/phy.h>
2862306a36Sopenharmony_ci#include <linux/phylink.h>
2962306a36Sopenharmony_ci#include <linux/phy/phy.h>
3062306a36Sopenharmony_ci#include <linux/ptp_classify.h>
3162306a36Sopenharmony_ci#include <linux/clk.h>
3262306a36Sopenharmony_ci#include <linux/hrtimer.h>
3362306a36Sopenharmony_ci#include <linux/ktime.h>
3462306a36Sopenharmony_ci#include <linux/regmap.h>
3562306a36Sopenharmony_ci#include <uapi/linux/ppp_defs.h>
3662306a36Sopenharmony_ci#include <net/ip.h>
3762306a36Sopenharmony_ci#include <net/ipv6.h>
3862306a36Sopenharmony_ci#include <net/page_pool/helpers.h>
3962306a36Sopenharmony_ci#include <net/tso.h>
4062306a36Sopenharmony_ci#include <linux/bpf_trace.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include "mvpp2.h"
4362306a36Sopenharmony_ci#include "mvpp2_prs.h"
4462306a36Sopenharmony_ci#include "mvpp2_cls.h"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cienum mvpp2_bm_pool_log_num {
4762306a36Sopenharmony_ci	MVPP2_BM_SHORT,
4862306a36Sopenharmony_ci	MVPP2_BM_LONG,
4962306a36Sopenharmony_ci	MVPP2_BM_JUMBO,
5062306a36Sopenharmony_ci	MVPP2_BM_POOLS_NUM
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic struct {
5462306a36Sopenharmony_ci	int pkt_size;
5562306a36Sopenharmony_ci	int buf_num;
5662306a36Sopenharmony_ci} mvpp2_pools[MVPP2_BM_POOLS_NUM];
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* The prototype is added here to be used in start_dev when using ACPI. This
5962306a36Sopenharmony_ci * will be removed once phylink is used for all modes (dt+ACPI).
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_cistatic void mvpp2_acpi_start(struct mvpp2_port *port);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* Queue modes */
6462306a36Sopenharmony_ci#define MVPP2_QDIST_SINGLE_MODE	0
6562306a36Sopenharmony_ci#define MVPP2_QDIST_MULTI_MODE	1
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int queue_mode = MVPP2_QDIST_MULTI_MODE;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cimodule_param(queue_mode, int, 0444);
7062306a36Sopenharmony_ciMODULE_PARM_DESC(queue_mode, "Set queue_mode (single=0, multi=1)");
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Utility/helper methods */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_civoid mvpp2_write(struct mvpp2 *priv, u32 offset, u32 data)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	writel(data, priv->swth_base[0] + offset);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciu32 mvpp2_read(struct mvpp2 *priv, u32 offset)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	return readl(priv->swth_base[0] + offset);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic u32 mvpp2_read_relaxed(struct mvpp2 *priv, u32 offset)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	return readl_relaxed(priv->swth_base[0] + offset);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic inline u32 mvpp2_cpu_to_thread(struct mvpp2 *priv, int cpu)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	return cpu % priv->nthreads;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void mvpp2_cm3_write(struct mvpp2 *priv, u32 offset, u32 data)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	writel(data, priv->cm3_base + offset);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic u32 mvpp2_cm3_read(struct mvpp2 *priv, u32 offset)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	return readl(priv->cm3_base + offset);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic struct page_pool *
10562306a36Sopenharmony_cimvpp2_create_page_pool(struct device *dev, int num, int len,
10662306a36Sopenharmony_ci		       enum dma_data_direction dma_dir)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct page_pool_params pp_params = {
10962306a36Sopenharmony_ci		/* internal DMA mapping in page_pool */
11062306a36Sopenharmony_ci		.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
11162306a36Sopenharmony_ci		.pool_size = num,
11262306a36Sopenharmony_ci		.nid = NUMA_NO_NODE,
11362306a36Sopenharmony_ci		.dev = dev,
11462306a36Sopenharmony_ci		.dma_dir = dma_dir,
11562306a36Sopenharmony_ci		.offset = MVPP2_SKB_HEADROOM,
11662306a36Sopenharmony_ci		.max_len = len,
11762306a36Sopenharmony_ci	};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return page_pool_create(&pp_params);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/* These accessors should be used to access:
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci * - per-thread registers, where each thread has its own copy of the
12562306a36Sopenharmony_ci *   register.
12662306a36Sopenharmony_ci *
12762306a36Sopenharmony_ci *   MVPP2_BM_VIRT_ALLOC_REG
12862306a36Sopenharmony_ci *   MVPP2_BM_ADDR_HIGH_ALLOC
12962306a36Sopenharmony_ci *   MVPP22_BM_ADDR_HIGH_RLS_REG
13062306a36Sopenharmony_ci *   MVPP2_BM_VIRT_RLS_REG
13162306a36Sopenharmony_ci *   MVPP2_ISR_RX_TX_CAUSE_REG
13262306a36Sopenharmony_ci *   MVPP2_ISR_RX_TX_MASK_REG
13362306a36Sopenharmony_ci *   MVPP2_TXQ_NUM_REG
13462306a36Sopenharmony_ci *   MVPP2_AGGR_TXQ_UPDATE_REG
13562306a36Sopenharmony_ci *   MVPP2_TXQ_RSVD_REQ_REG
13662306a36Sopenharmony_ci *   MVPP2_TXQ_RSVD_RSLT_REG
13762306a36Sopenharmony_ci *   MVPP2_TXQ_SENT_REG
13862306a36Sopenharmony_ci *   MVPP2_RXQ_NUM_REG
13962306a36Sopenharmony_ci *
14062306a36Sopenharmony_ci * - global registers that must be accessed through a specific thread
14162306a36Sopenharmony_ci *   window, because they are related to an access to a per-thread
14262306a36Sopenharmony_ci *   register
14362306a36Sopenharmony_ci *
14462306a36Sopenharmony_ci *   MVPP2_BM_PHY_ALLOC_REG    (related to MVPP2_BM_VIRT_ALLOC_REG)
14562306a36Sopenharmony_ci *   MVPP2_BM_PHY_RLS_REG      (related to MVPP2_BM_VIRT_RLS_REG)
14662306a36Sopenharmony_ci *   MVPP2_RXQ_THRESH_REG      (related to MVPP2_RXQ_NUM_REG)
14762306a36Sopenharmony_ci *   MVPP2_RXQ_DESC_ADDR_REG   (related to MVPP2_RXQ_NUM_REG)
14862306a36Sopenharmony_ci *   MVPP2_RXQ_DESC_SIZE_REG   (related to MVPP2_RXQ_NUM_REG)
14962306a36Sopenharmony_ci *   MVPP2_RXQ_INDEX_REG       (related to MVPP2_RXQ_NUM_REG)
15062306a36Sopenharmony_ci *   MVPP2_TXQ_PENDING_REG     (related to MVPP2_TXQ_NUM_REG)
15162306a36Sopenharmony_ci *   MVPP2_TXQ_DESC_ADDR_REG   (related to MVPP2_TXQ_NUM_REG)
15262306a36Sopenharmony_ci *   MVPP2_TXQ_DESC_SIZE_REG   (related to MVPP2_TXQ_NUM_REG)
15362306a36Sopenharmony_ci *   MVPP2_TXQ_INDEX_REG       (related to MVPP2_TXQ_NUM_REG)
15462306a36Sopenharmony_ci *   MVPP2_TXQ_PENDING_REG     (related to MVPP2_TXQ_NUM_REG)
15562306a36Sopenharmony_ci *   MVPP2_TXQ_PREF_BUF_REG    (related to MVPP2_TXQ_NUM_REG)
15662306a36Sopenharmony_ci *   MVPP2_TXQ_PREF_BUF_REG    (related to MVPP2_TXQ_NUM_REG)
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_cistatic void mvpp2_thread_write(struct mvpp2 *priv, unsigned int thread,
15962306a36Sopenharmony_ci			       u32 offset, u32 data)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	writel(data, priv->swth_base[thread] + offset);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic u32 mvpp2_thread_read(struct mvpp2 *priv, unsigned int thread,
16562306a36Sopenharmony_ci			     u32 offset)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	return readl(priv->swth_base[thread] + offset);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void mvpp2_thread_write_relaxed(struct mvpp2 *priv, unsigned int thread,
17162306a36Sopenharmony_ci				       u32 offset, u32 data)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	writel_relaxed(data, priv->swth_base[thread] + offset);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic u32 mvpp2_thread_read_relaxed(struct mvpp2 *priv, unsigned int thread,
17762306a36Sopenharmony_ci				     u32 offset)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	return readl_relaxed(priv->swth_base[thread] + offset);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic dma_addr_t mvpp2_txdesc_dma_addr_get(struct mvpp2_port *port,
18362306a36Sopenharmony_ci					    struct mvpp2_tx_desc *tx_desc)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
18662306a36Sopenharmony_ci		return le32_to_cpu(tx_desc->pp21.buf_dma_addr);
18762306a36Sopenharmony_ci	else
18862306a36Sopenharmony_ci		return le64_to_cpu(tx_desc->pp22.buf_dma_addr_ptp) &
18962306a36Sopenharmony_ci		       MVPP2_DESC_DMA_MASK;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void mvpp2_txdesc_dma_addr_set(struct mvpp2_port *port,
19362306a36Sopenharmony_ci				      struct mvpp2_tx_desc *tx_desc,
19462306a36Sopenharmony_ci				      dma_addr_t dma_addr)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	dma_addr_t addr, offset;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	addr = dma_addr & ~MVPP2_TX_DESC_ALIGN;
19962306a36Sopenharmony_ci	offset = dma_addr & MVPP2_TX_DESC_ALIGN;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21) {
20262306a36Sopenharmony_ci		tx_desc->pp21.buf_dma_addr = cpu_to_le32(addr);
20362306a36Sopenharmony_ci		tx_desc->pp21.packet_offset = offset;
20462306a36Sopenharmony_ci	} else {
20562306a36Sopenharmony_ci		__le64 val = cpu_to_le64(addr);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		tx_desc->pp22.buf_dma_addr_ptp &= ~cpu_to_le64(MVPP2_DESC_DMA_MASK);
20862306a36Sopenharmony_ci		tx_desc->pp22.buf_dma_addr_ptp |= val;
20962306a36Sopenharmony_ci		tx_desc->pp22.packet_offset = offset;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic size_t mvpp2_txdesc_size_get(struct mvpp2_port *port,
21462306a36Sopenharmony_ci				    struct mvpp2_tx_desc *tx_desc)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
21762306a36Sopenharmony_ci		return le16_to_cpu(tx_desc->pp21.data_size);
21862306a36Sopenharmony_ci	else
21962306a36Sopenharmony_ci		return le16_to_cpu(tx_desc->pp22.data_size);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic void mvpp2_txdesc_size_set(struct mvpp2_port *port,
22362306a36Sopenharmony_ci				  struct mvpp2_tx_desc *tx_desc,
22462306a36Sopenharmony_ci				  size_t size)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
22762306a36Sopenharmony_ci		tx_desc->pp21.data_size = cpu_to_le16(size);
22862306a36Sopenharmony_ci	else
22962306a36Sopenharmony_ci		tx_desc->pp22.data_size = cpu_to_le16(size);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic void mvpp2_txdesc_txq_set(struct mvpp2_port *port,
23362306a36Sopenharmony_ci				 struct mvpp2_tx_desc *tx_desc,
23462306a36Sopenharmony_ci				 unsigned int txq)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
23762306a36Sopenharmony_ci		tx_desc->pp21.phys_txq = txq;
23862306a36Sopenharmony_ci	else
23962306a36Sopenharmony_ci		tx_desc->pp22.phys_txq = txq;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic void mvpp2_txdesc_cmd_set(struct mvpp2_port *port,
24362306a36Sopenharmony_ci				 struct mvpp2_tx_desc *tx_desc,
24462306a36Sopenharmony_ci				 unsigned int command)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
24762306a36Sopenharmony_ci		tx_desc->pp21.command = cpu_to_le32(command);
24862306a36Sopenharmony_ci	else
24962306a36Sopenharmony_ci		tx_desc->pp22.command = cpu_to_le32(command);
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic unsigned int mvpp2_txdesc_offset_get(struct mvpp2_port *port,
25362306a36Sopenharmony_ci					    struct mvpp2_tx_desc *tx_desc)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
25662306a36Sopenharmony_ci		return tx_desc->pp21.packet_offset;
25762306a36Sopenharmony_ci	else
25862306a36Sopenharmony_ci		return tx_desc->pp22.packet_offset;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic dma_addr_t mvpp2_rxdesc_dma_addr_get(struct mvpp2_port *port,
26262306a36Sopenharmony_ci					    struct mvpp2_rx_desc *rx_desc)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
26562306a36Sopenharmony_ci		return le32_to_cpu(rx_desc->pp21.buf_dma_addr);
26662306a36Sopenharmony_ci	else
26762306a36Sopenharmony_ci		return le64_to_cpu(rx_desc->pp22.buf_dma_addr_key_hash) &
26862306a36Sopenharmony_ci		       MVPP2_DESC_DMA_MASK;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic unsigned long mvpp2_rxdesc_cookie_get(struct mvpp2_port *port,
27262306a36Sopenharmony_ci					     struct mvpp2_rx_desc *rx_desc)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
27562306a36Sopenharmony_ci		return le32_to_cpu(rx_desc->pp21.buf_cookie);
27662306a36Sopenharmony_ci	else
27762306a36Sopenharmony_ci		return le64_to_cpu(rx_desc->pp22.buf_cookie_misc) &
27862306a36Sopenharmony_ci		       MVPP2_DESC_DMA_MASK;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic size_t mvpp2_rxdesc_size_get(struct mvpp2_port *port,
28262306a36Sopenharmony_ci				    struct mvpp2_rx_desc *rx_desc)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
28562306a36Sopenharmony_ci		return le16_to_cpu(rx_desc->pp21.data_size);
28662306a36Sopenharmony_ci	else
28762306a36Sopenharmony_ci		return le16_to_cpu(rx_desc->pp22.data_size);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic u32 mvpp2_rxdesc_status_get(struct mvpp2_port *port,
29162306a36Sopenharmony_ci				   struct mvpp2_rx_desc *rx_desc)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
29462306a36Sopenharmony_ci		return le32_to_cpu(rx_desc->pp21.status);
29562306a36Sopenharmony_ci	else
29662306a36Sopenharmony_ci		return le32_to_cpu(rx_desc->pp22.status);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void mvpp2_txq_inc_get(struct mvpp2_txq_pcpu *txq_pcpu)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	txq_pcpu->txq_get_index++;
30262306a36Sopenharmony_ci	if (txq_pcpu->txq_get_index == txq_pcpu->size)
30362306a36Sopenharmony_ci		txq_pcpu->txq_get_index = 0;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic void mvpp2_txq_inc_put(struct mvpp2_port *port,
30762306a36Sopenharmony_ci			      struct mvpp2_txq_pcpu *txq_pcpu,
30862306a36Sopenharmony_ci			      void *data,
30962306a36Sopenharmony_ci			      struct mvpp2_tx_desc *tx_desc,
31062306a36Sopenharmony_ci			      enum mvpp2_tx_buf_type buf_type)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct mvpp2_txq_pcpu_buf *tx_buf =
31362306a36Sopenharmony_ci		txq_pcpu->buffs + txq_pcpu->txq_put_index;
31462306a36Sopenharmony_ci	tx_buf->type = buf_type;
31562306a36Sopenharmony_ci	if (buf_type == MVPP2_TYPE_SKB)
31662306a36Sopenharmony_ci		tx_buf->skb = data;
31762306a36Sopenharmony_ci	else
31862306a36Sopenharmony_ci		tx_buf->xdpf = data;
31962306a36Sopenharmony_ci	tx_buf->size = mvpp2_txdesc_size_get(port, tx_desc);
32062306a36Sopenharmony_ci	tx_buf->dma = mvpp2_txdesc_dma_addr_get(port, tx_desc) +
32162306a36Sopenharmony_ci		mvpp2_txdesc_offset_get(port, tx_desc);
32262306a36Sopenharmony_ci	txq_pcpu->txq_put_index++;
32362306a36Sopenharmony_ci	if (txq_pcpu->txq_put_index == txq_pcpu->size)
32462306a36Sopenharmony_ci		txq_pcpu->txq_put_index = 0;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/* Get number of maximum RXQ */
32862306a36Sopenharmony_cistatic int mvpp2_get_nrxqs(struct mvpp2 *priv)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	unsigned int nrxqs;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (priv->hw_version >= MVPP22 && queue_mode == MVPP2_QDIST_SINGLE_MODE)
33362306a36Sopenharmony_ci		return 1;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/* According to the PPv2.2 datasheet and our experiments on
33662306a36Sopenharmony_ci	 * PPv2.1, RX queues have an allocation granularity of 4 (when
33762306a36Sopenharmony_ci	 * more than a single one on PPv2.2).
33862306a36Sopenharmony_ci	 * Round up to nearest multiple of 4.
33962306a36Sopenharmony_ci	 */
34062306a36Sopenharmony_ci	nrxqs = (num_possible_cpus() + 3) & ~0x3;
34162306a36Sopenharmony_ci	if (nrxqs > MVPP2_PORT_MAX_RXQ)
34262306a36Sopenharmony_ci		nrxqs = MVPP2_PORT_MAX_RXQ;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return nrxqs;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci/* Get number of physical egress port */
34862306a36Sopenharmony_cistatic inline int mvpp2_egress_port(struct mvpp2_port *port)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	return MVPP2_MAX_TCONT + port->id;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci/* Get number of physical TXQ */
35462306a36Sopenharmony_cistatic inline int mvpp2_txq_phys(int port, int txq)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	return (MVPP2_MAX_TCONT + port) * MVPP2_MAX_TXQ + txq;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/* Returns a struct page if page_pool is set, otherwise a buffer */
36062306a36Sopenharmony_cistatic void *mvpp2_frag_alloc(const struct mvpp2_bm_pool *pool,
36162306a36Sopenharmony_ci			      struct page_pool *page_pool)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	if (page_pool)
36462306a36Sopenharmony_ci		return page_pool_dev_alloc_pages(page_pool);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (likely(pool->frag_size <= PAGE_SIZE))
36762306a36Sopenharmony_ci		return netdev_alloc_frag(pool->frag_size);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return kmalloc(pool->frag_size, GFP_ATOMIC);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void mvpp2_frag_free(const struct mvpp2_bm_pool *pool,
37362306a36Sopenharmony_ci			    struct page_pool *page_pool, void *data)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	if (page_pool)
37662306a36Sopenharmony_ci		page_pool_put_full_page(page_pool, virt_to_head_page(data), false);
37762306a36Sopenharmony_ci	else if (likely(pool->frag_size <= PAGE_SIZE))
37862306a36Sopenharmony_ci		skb_free_frag(data);
37962306a36Sopenharmony_ci	else
38062306a36Sopenharmony_ci		kfree(data);
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci/* Buffer Manager configuration routines */
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci/* Create pool */
38662306a36Sopenharmony_cistatic int mvpp2_bm_pool_create(struct device *dev, struct mvpp2 *priv,
38762306a36Sopenharmony_ci				struct mvpp2_bm_pool *bm_pool, int size)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	u32 val;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* Number of buffer pointers must be a multiple of 16, as per
39262306a36Sopenharmony_ci	 * hardware constraints
39362306a36Sopenharmony_ci	 */
39462306a36Sopenharmony_ci	if (!IS_ALIGNED(size, 16))
39562306a36Sopenharmony_ci		return -EINVAL;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/* PPv2.1 needs 8 bytes per buffer pointer, PPv2.2 and PPv2.3 needs 16
39862306a36Sopenharmony_ci	 * bytes per buffer pointer
39962306a36Sopenharmony_ci	 */
40062306a36Sopenharmony_ci	if (priv->hw_version == MVPP21)
40162306a36Sopenharmony_ci		bm_pool->size_bytes = 2 * sizeof(u32) * size;
40262306a36Sopenharmony_ci	else
40362306a36Sopenharmony_ci		bm_pool->size_bytes = 2 * sizeof(u64) * size;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	bm_pool->virt_addr = dma_alloc_coherent(dev, bm_pool->size_bytes,
40662306a36Sopenharmony_ci						&bm_pool->dma_addr,
40762306a36Sopenharmony_ci						GFP_KERNEL);
40862306a36Sopenharmony_ci	if (!bm_pool->virt_addr)
40962306a36Sopenharmony_ci		return -ENOMEM;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (!IS_ALIGNED((unsigned long)bm_pool->virt_addr,
41262306a36Sopenharmony_ci			MVPP2_BM_POOL_PTR_ALIGN)) {
41362306a36Sopenharmony_ci		dma_free_coherent(dev, bm_pool->size_bytes,
41462306a36Sopenharmony_ci				  bm_pool->virt_addr, bm_pool->dma_addr);
41562306a36Sopenharmony_ci		dev_err(dev, "BM pool %d is not %d bytes aligned\n",
41662306a36Sopenharmony_ci			bm_pool->id, MVPP2_BM_POOL_PTR_ALIGN);
41762306a36Sopenharmony_ci		return -ENOMEM;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_BM_POOL_BASE_REG(bm_pool->id),
42162306a36Sopenharmony_ci		    lower_32_bits(bm_pool->dma_addr));
42262306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_BM_POOL_SIZE_REG(bm_pool->id), size);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id));
42562306a36Sopenharmony_ci	val |= MVPP2_BM_START_MASK;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	val &= ~MVPP2_BM_LOW_THRESH_MASK;
42862306a36Sopenharmony_ci	val &= ~MVPP2_BM_HIGH_THRESH_MASK;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/* Set 8 Pools BPPI threshold for MVPP23 */
43162306a36Sopenharmony_ci	if (priv->hw_version == MVPP23) {
43262306a36Sopenharmony_ci		val |= MVPP2_BM_LOW_THRESH_VALUE(MVPP23_BM_BPPI_LOW_THRESH);
43362306a36Sopenharmony_ci		val |= MVPP2_BM_HIGH_THRESH_VALUE(MVPP23_BM_BPPI_HIGH_THRESH);
43462306a36Sopenharmony_ci	} else {
43562306a36Sopenharmony_ci		val |= MVPP2_BM_LOW_THRESH_VALUE(MVPP2_BM_BPPI_LOW_THRESH);
43662306a36Sopenharmony_ci		val |= MVPP2_BM_HIGH_THRESH_VALUE(MVPP2_BM_BPPI_HIGH_THRESH);
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id), val);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	bm_pool->size = size;
44262306a36Sopenharmony_ci	bm_pool->pkt_size = 0;
44362306a36Sopenharmony_ci	bm_pool->buf_num = 0;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return 0;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/* Set pool buffer size */
44962306a36Sopenharmony_cistatic void mvpp2_bm_pool_bufsize_set(struct mvpp2 *priv,
45062306a36Sopenharmony_ci				      struct mvpp2_bm_pool *bm_pool,
45162306a36Sopenharmony_ci				      int buf_size)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	u32 val;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	bm_pool->buf_size = buf_size;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	val = ALIGN(buf_size, 1 << MVPP2_POOL_BUF_SIZE_OFFSET);
45862306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_POOL_BUF_SIZE_REG(bm_pool->id), val);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic void mvpp2_bm_bufs_get_addrs(struct device *dev, struct mvpp2 *priv,
46262306a36Sopenharmony_ci				    struct mvpp2_bm_pool *bm_pool,
46362306a36Sopenharmony_ci				    dma_addr_t *dma_addr,
46462306a36Sopenharmony_ci				    phys_addr_t *phys_addr)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	unsigned int thread = mvpp2_cpu_to_thread(priv, get_cpu());
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	*dma_addr = mvpp2_thread_read(priv, thread,
46962306a36Sopenharmony_ci				      MVPP2_BM_PHY_ALLOC_REG(bm_pool->id));
47062306a36Sopenharmony_ci	*phys_addr = mvpp2_thread_read(priv, thread, MVPP2_BM_VIRT_ALLOC_REG);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (priv->hw_version >= MVPP22) {
47362306a36Sopenharmony_ci		u32 val;
47462306a36Sopenharmony_ci		u32 dma_addr_highbits, phys_addr_highbits;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		val = mvpp2_thread_read(priv, thread, MVPP22_BM_ADDR_HIGH_ALLOC);
47762306a36Sopenharmony_ci		dma_addr_highbits = (val & MVPP22_BM_ADDR_HIGH_PHYS_MASK);
47862306a36Sopenharmony_ci		phys_addr_highbits = (val & MVPP22_BM_ADDR_HIGH_VIRT_MASK) >>
47962306a36Sopenharmony_ci			MVPP22_BM_ADDR_HIGH_VIRT_SHIFT;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		if (sizeof(dma_addr_t) == 8)
48262306a36Sopenharmony_ci			*dma_addr |= (u64)dma_addr_highbits << 32;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		if (sizeof(phys_addr_t) == 8)
48562306a36Sopenharmony_ci			*phys_addr |= (u64)phys_addr_highbits << 32;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	put_cpu();
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci/* Free all buffers from the pool */
49262306a36Sopenharmony_cistatic void mvpp2_bm_bufs_free(struct device *dev, struct mvpp2 *priv,
49362306a36Sopenharmony_ci			       struct mvpp2_bm_pool *bm_pool, int buf_num)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct page_pool *pp = NULL;
49662306a36Sopenharmony_ci	int i;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (buf_num > bm_pool->buf_num) {
49962306a36Sopenharmony_ci		WARN(1, "Pool does not have so many bufs pool(%d) bufs(%d)\n",
50062306a36Sopenharmony_ci		     bm_pool->id, buf_num);
50162306a36Sopenharmony_ci		buf_num = bm_pool->buf_num;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (priv->percpu_pools)
50562306a36Sopenharmony_ci		pp = priv->page_pool[bm_pool->id];
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	for (i = 0; i < buf_num; i++) {
50862306a36Sopenharmony_ci		dma_addr_t buf_dma_addr;
50962306a36Sopenharmony_ci		phys_addr_t buf_phys_addr;
51062306a36Sopenharmony_ci		void *data;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		mvpp2_bm_bufs_get_addrs(dev, priv, bm_pool,
51362306a36Sopenharmony_ci					&buf_dma_addr, &buf_phys_addr);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		if (!pp)
51662306a36Sopenharmony_ci			dma_unmap_single(dev, buf_dma_addr,
51762306a36Sopenharmony_ci					 bm_pool->buf_size, DMA_FROM_DEVICE);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		data = (void *)phys_to_virt(buf_phys_addr);
52062306a36Sopenharmony_ci		if (!data)
52162306a36Sopenharmony_ci			break;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		mvpp2_frag_free(bm_pool, pp, data);
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/* Update BM driver with number of buffers removed from pool */
52762306a36Sopenharmony_ci	bm_pool->buf_num -= i;
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci/* Check number of buffers in BM pool */
53162306a36Sopenharmony_cistatic int mvpp2_check_hw_buf_num(struct mvpp2 *priv, struct mvpp2_bm_pool *bm_pool)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	int buf_num = 0;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	buf_num += mvpp2_read(priv, MVPP2_BM_POOL_PTRS_NUM_REG(bm_pool->id)) &
53662306a36Sopenharmony_ci				    MVPP22_BM_POOL_PTRS_NUM_MASK;
53762306a36Sopenharmony_ci	buf_num += mvpp2_read(priv, MVPP2_BM_BPPI_PTRS_NUM_REG(bm_pool->id)) &
53862306a36Sopenharmony_ci				    MVPP2_BM_BPPI_PTR_NUM_MASK;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* HW has one buffer ready which is not reflected in the counters */
54162306a36Sopenharmony_ci	if (buf_num)
54262306a36Sopenharmony_ci		buf_num += 1;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return buf_num;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/* Cleanup pool */
54862306a36Sopenharmony_cistatic int mvpp2_bm_pool_destroy(struct device *dev, struct mvpp2 *priv,
54962306a36Sopenharmony_ci				 struct mvpp2_bm_pool *bm_pool)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	int buf_num;
55262306a36Sopenharmony_ci	u32 val;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	buf_num = mvpp2_check_hw_buf_num(priv, bm_pool);
55562306a36Sopenharmony_ci	mvpp2_bm_bufs_free(dev, priv, bm_pool, buf_num);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* Check buffer counters after free */
55862306a36Sopenharmony_ci	buf_num = mvpp2_check_hw_buf_num(priv, bm_pool);
55962306a36Sopenharmony_ci	if (buf_num) {
56062306a36Sopenharmony_ci		WARN(1, "cannot free all buffers in pool %d, buf_num left %d\n",
56162306a36Sopenharmony_ci		     bm_pool->id, bm_pool->buf_num);
56262306a36Sopenharmony_ci		return 0;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id));
56662306a36Sopenharmony_ci	val |= MVPP2_BM_STOP_MASK;
56762306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id), val);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (priv->percpu_pools) {
57062306a36Sopenharmony_ci		page_pool_destroy(priv->page_pool[bm_pool->id]);
57162306a36Sopenharmony_ci		priv->page_pool[bm_pool->id] = NULL;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	dma_free_coherent(dev, bm_pool->size_bytes,
57562306a36Sopenharmony_ci			  bm_pool->virt_addr,
57662306a36Sopenharmony_ci			  bm_pool->dma_addr);
57762306a36Sopenharmony_ci	return 0;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int mvpp2_bm_pools_init(struct device *dev, struct mvpp2 *priv)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	int i, err, size, poolnum = MVPP2_BM_POOLS_NUM;
58362306a36Sopenharmony_ci	struct mvpp2_bm_pool *bm_pool;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (priv->percpu_pools)
58662306a36Sopenharmony_ci		poolnum = mvpp2_get_nrxqs(priv) * 2;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	/* Create all pools with maximum size */
58962306a36Sopenharmony_ci	size = MVPP2_BM_POOL_SIZE_MAX;
59062306a36Sopenharmony_ci	for (i = 0; i < poolnum; i++) {
59162306a36Sopenharmony_ci		bm_pool = &priv->bm_pools[i];
59262306a36Sopenharmony_ci		bm_pool->id = i;
59362306a36Sopenharmony_ci		err = mvpp2_bm_pool_create(dev, priv, bm_pool, size);
59462306a36Sopenharmony_ci		if (err)
59562306a36Sopenharmony_ci			goto err_unroll_pools;
59662306a36Sopenharmony_ci		mvpp2_bm_pool_bufsize_set(priv, bm_pool, 0);
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci	return 0;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cierr_unroll_pools:
60162306a36Sopenharmony_ci	dev_err(dev, "failed to create BM pool %d, size %d\n", i, size);
60262306a36Sopenharmony_ci	for (i = i - 1; i >= 0; i--)
60362306a36Sopenharmony_ci		mvpp2_bm_pool_destroy(dev, priv, &priv->bm_pools[i]);
60462306a36Sopenharmony_ci	return err;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci/* Routine enable PPv23 8 pool mode */
60862306a36Sopenharmony_cistatic void mvpp23_bm_set_8pool_mode(struct mvpp2 *priv)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	int val;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	val = mvpp2_read(priv, MVPP22_BM_POOL_BASE_ADDR_HIGH_REG);
61362306a36Sopenharmony_ci	val |= MVPP23_BM_8POOL_MODE;
61462306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_BM_POOL_BASE_ADDR_HIGH_REG, val);
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci/* Cleanup pool before actual initialization in the OS */
61862306a36Sopenharmony_cistatic void mvpp2_bm_pool_cleanup(struct mvpp2 *priv, int pool_id)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	unsigned int thread = mvpp2_cpu_to_thread(priv, get_cpu());
62162306a36Sopenharmony_ci	u32 val;
62262306a36Sopenharmony_ci	int i;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* Drain the BM from all possible residues left by firmware */
62562306a36Sopenharmony_ci	for (i = 0; i < MVPP2_BM_POOL_SIZE_MAX; i++)
62662306a36Sopenharmony_ci		mvpp2_thread_read(priv, thread, MVPP2_BM_PHY_ALLOC_REG(pool_id));
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	put_cpu();
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/* Stop the BM pool */
63162306a36Sopenharmony_ci	val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(pool_id));
63262306a36Sopenharmony_ci	val |= MVPP2_BM_STOP_MASK;
63362306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(pool_id), val);
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic int mvpp2_bm_init(struct device *dev, struct mvpp2 *priv)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	enum dma_data_direction dma_dir = DMA_FROM_DEVICE;
63962306a36Sopenharmony_ci	int i, err, poolnum = MVPP2_BM_POOLS_NUM;
64062306a36Sopenharmony_ci	struct mvpp2_port *port;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (priv->percpu_pools)
64362306a36Sopenharmony_ci		poolnum = mvpp2_get_nrxqs(priv) * 2;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/* Clean up the pool state in case it contains stale state */
64662306a36Sopenharmony_ci	for (i = 0; i < poolnum; i++)
64762306a36Sopenharmony_ci		mvpp2_bm_pool_cleanup(priv, i);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (priv->percpu_pools) {
65062306a36Sopenharmony_ci		for (i = 0; i < priv->port_count; i++) {
65162306a36Sopenharmony_ci			port = priv->port_list[i];
65262306a36Sopenharmony_ci			if (port->xdp_prog) {
65362306a36Sopenharmony_ci				dma_dir = DMA_BIDIRECTIONAL;
65462306a36Sopenharmony_ci				break;
65562306a36Sopenharmony_ci			}
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		for (i = 0; i < poolnum; i++) {
65962306a36Sopenharmony_ci			/* the pool in use */
66062306a36Sopenharmony_ci			int pn = i / (poolnum / 2);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci			priv->page_pool[i] =
66362306a36Sopenharmony_ci				mvpp2_create_page_pool(dev,
66462306a36Sopenharmony_ci						       mvpp2_pools[pn].buf_num,
66562306a36Sopenharmony_ci						       mvpp2_pools[pn].pkt_size,
66662306a36Sopenharmony_ci						       dma_dir);
66762306a36Sopenharmony_ci			if (IS_ERR(priv->page_pool[i])) {
66862306a36Sopenharmony_ci				int j;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci				for (j = 0; j < i; j++) {
67162306a36Sopenharmony_ci					page_pool_destroy(priv->page_pool[j]);
67262306a36Sopenharmony_ci					priv->page_pool[j] = NULL;
67362306a36Sopenharmony_ci				}
67462306a36Sopenharmony_ci				return PTR_ERR(priv->page_pool[i]);
67562306a36Sopenharmony_ci			}
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	dev_info(dev, "using %d %s buffers\n", poolnum,
68062306a36Sopenharmony_ci		 priv->percpu_pools ? "per-cpu" : "shared");
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	for (i = 0; i < poolnum; i++) {
68362306a36Sopenharmony_ci		/* Mask BM all interrupts */
68462306a36Sopenharmony_ci		mvpp2_write(priv, MVPP2_BM_INTR_MASK_REG(i), 0);
68562306a36Sopenharmony_ci		/* Clear BM cause register */
68662306a36Sopenharmony_ci		mvpp2_write(priv, MVPP2_BM_INTR_CAUSE_REG(i), 0);
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	/* Allocate and initialize BM pools */
69062306a36Sopenharmony_ci	priv->bm_pools = devm_kcalloc(dev, poolnum,
69162306a36Sopenharmony_ci				      sizeof(*priv->bm_pools), GFP_KERNEL);
69262306a36Sopenharmony_ci	if (!priv->bm_pools)
69362306a36Sopenharmony_ci		return -ENOMEM;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	if (priv->hw_version == MVPP23)
69662306a36Sopenharmony_ci		mvpp23_bm_set_8pool_mode(priv);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	err = mvpp2_bm_pools_init(dev, priv);
69962306a36Sopenharmony_ci	if (err < 0)
70062306a36Sopenharmony_ci		return err;
70162306a36Sopenharmony_ci	return 0;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic void mvpp2_setup_bm_pool(void)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	/* Short pool */
70762306a36Sopenharmony_ci	mvpp2_pools[MVPP2_BM_SHORT].buf_num  = MVPP2_BM_SHORT_BUF_NUM;
70862306a36Sopenharmony_ci	mvpp2_pools[MVPP2_BM_SHORT].pkt_size = MVPP2_BM_SHORT_PKT_SIZE;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* Long pool */
71162306a36Sopenharmony_ci	mvpp2_pools[MVPP2_BM_LONG].buf_num  = MVPP2_BM_LONG_BUF_NUM;
71262306a36Sopenharmony_ci	mvpp2_pools[MVPP2_BM_LONG].pkt_size = MVPP2_BM_LONG_PKT_SIZE;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	/* Jumbo pool */
71562306a36Sopenharmony_ci	mvpp2_pools[MVPP2_BM_JUMBO].buf_num  = MVPP2_BM_JUMBO_BUF_NUM;
71662306a36Sopenharmony_ci	mvpp2_pools[MVPP2_BM_JUMBO].pkt_size = MVPP2_BM_JUMBO_PKT_SIZE;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci/* Attach long pool to rxq */
72062306a36Sopenharmony_cistatic void mvpp2_rxq_long_pool_set(struct mvpp2_port *port,
72162306a36Sopenharmony_ci				    int lrxq, int long_pool)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	u32 val, mask;
72462306a36Sopenharmony_ci	int prxq;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* Get queue physical ID */
72762306a36Sopenharmony_ci	prxq = port->rxqs[lrxq]->id;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
73062306a36Sopenharmony_ci		mask = MVPP21_RXQ_POOL_LONG_MASK;
73162306a36Sopenharmony_ci	else
73262306a36Sopenharmony_ci		mask = MVPP22_RXQ_POOL_LONG_MASK;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq));
73562306a36Sopenharmony_ci	val &= ~mask;
73662306a36Sopenharmony_ci	val |= (long_pool << MVPP2_RXQ_POOL_LONG_OFFS) & mask;
73762306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(prxq), val);
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci/* Attach short pool to rxq */
74162306a36Sopenharmony_cistatic void mvpp2_rxq_short_pool_set(struct mvpp2_port *port,
74262306a36Sopenharmony_ci				     int lrxq, int short_pool)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	u32 val, mask;
74562306a36Sopenharmony_ci	int prxq;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	/* Get queue physical ID */
74862306a36Sopenharmony_ci	prxq = port->rxqs[lrxq]->id;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
75162306a36Sopenharmony_ci		mask = MVPP21_RXQ_POOL_SHORT_MASK;
75262306a36Sopenharmony_ci	else
75362306a36Sopenharmony_ci		mask = MVPP22_RXQ_POOL_SHORT_MASK;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq));
75662306a36Sopenharmony_ci	val &= ~mask;
75762306a36Sopenharmony_ci	val |= (short_pool << MVPP2_RXQ_POOL_SHORT_OFFS) & mask;
75862306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(prxq), val);
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic void *mvpp2_buf_alloc(struct mvpp2_port *port,
76262306a36Sopenharmony_ci			     struct mvpp2_bm_pool *bm_pool,
76362306a36Sopenharmony_ci			     struct page_pool *page_pool,
76462306a36Sopenharmony_ci			     dma_addr_t *buf_dma_addr,
76562306a36Sopenharmony_ci			     phys_addr_t *buf_phys_addr,
76662306a36Sopenharmony_ci			     gfp_t gfp_mask)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	dma_addr_t dma_addr;
76962306a36Sopenharmony_ci	struct page *page;
77062306a36Sopenharmony_ci	void *data;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	data = mvpp2_frag_alloc(bm_pool, page_pool);
77362306a36Sopenharmony_ci	if (!data)
77462306a36Sopenharmony_ci		return NULL;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (page_pool) {
77762306a36Sopenharmony_ci		page = (struct page *)data;
77862306a36Sopenharmony_ci		dma_addr = page_pool_get_dma_addr(page);
77962306a36Sopenharmony_ci		data = page_to_virt(page);
78062306a36Sopenharmony_ci	} else {
78162306a36Sopenharmony_ci		dma_addr = dma_map_single(port->dev->dev.parent, data,
78262306a36Sopenharmony_ci					  MVPP2_RX_BUF_SIZE(bm_pool->pkt_size),
78362306a36Sopenharmony_ci					  DMA_FROM_DEVICE);
78462306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(port->dev->dev.parent, dma_addr))) {
78562306a36Sopenharmony_ci			mvpp2_frag_free(bm_pool, NULL, data);
78662306a36Sopenharmony_ci			return NULL;
78762306a36Sopenharmony_ci		}
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci	*buf_dma_addr = dma_addr;
79062306a36Sopenharmony_ci	*buf_phys_addr = virt_to_phys(data);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	return data;
79362306a36Sopenharmony_ci}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci/* Routine enable flow control for RXQs condition */
79662306a36Sopenharmony_cistatic void mvpp2_rxq_enable_fc(struct mvpp2_port *port)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	int val, cm3_state, host_id, q;
79962306a36Sopenharmony_ci	int fq = port->first_rxq;
80062306a36Sopenharmony_ci	unsigned long flags;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	spin_lock_irqsave(&port->priv->mss_spinlock, flags);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	/* Remove Flow control enable bit to prevent race between FW and Kernel
80562306a36Sopenharmony_ci	 * If Flow control was enabled, it would be re-enabled.
80662306a36Sopenharmony_ci	 */
80762306a36Sopenharmony_ci	val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
80862306a36Sopenharmony_ci	cm3_state = (val & FLOW_CONTROL_ENABLE_BIT);
80962306a36Sopenharmony_ci	val &= ~FLOW_CONTROL_ENABLE_BIT;
81062306a36Sopenharmony_ci	mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	/* Set same Flow control for all RXQs */
81362306a36Sopenharmony_ci	for (q = 0; q < port->nrxqs; q++) {
81462306a36Sopenharmony_ci		/* Set stop and start Flow control RXQ thresholds */
81562306a36Sopenharmony_ci		val = MSS_THRESHOLD_START;
81662306a36Sopenharmony_ci		val |= (MSS_THRESHOLD_STOP << MSS_RXQ_TRESH_STOP_OFFS);
81762306a36Sopenharmony_ci		mvpp2_cm3_write(port->priv, MSS_RXQ_TRESH_REG(q, fq), val);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci		val = mvpp2_cm3_read(port->priv, MSS_RXQ_ASS_REG(q, fq));
82062306a36Sopenharmony_ci		/* Set RXQ port ID */
82162306a36Sopenharmony_ci		val &= ~(MSS_RXQ_ASS_PORTID_MASK << MSS_RXQ_ASS_Q_BASE(q, fq));
82262306a36Sopenharmony_ci		val |= (port->id << MSS_RXQ_ASS_Q_BASE(q, fq));
82362306a36Sopenharmony_ci		val &= ~(MSS_RXQ_ASS_HOSTID_MASK << (MSS_RXQ_ASS_Q_BASE(q, fq)
82462306a36Sopenharmony_ci			+ MSS_RXQ_ASS_HOSTID_OFFS));
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci		/* Calculate RXQ host ID:
82762306a36Sopenharmony_ci		 * In Single queue mode: Host ID equal to Host ID used for
82862306a36Sopenharmony_ci		 *			 shared RX interrupt
82962306a36Sopenharmony_ci		 * In Multi queue mode: Host ID equal to number of
83062306a36Sopenharmony_ci		 *			RXQ ID / number of CoS queues
83162306a36Sopenharmony_ci		 * In Single resource mode: Host ID always equal to 0
83262306a36Sopenharmony_ci		 */
83362306a36Sopenharmony_ci		if (queue_mode == MVPP2_QDIST_SINGLE_MODE)
83462306a36Sopenharmony_ci			host_id = port->nqvecs;
83562306a36Sopenharmony_ci		else if (queue_mode == MVPP2_QDIST_MULTI_MODE)
83662306a36Sopenharmony_ci			host_id = q;
83762306a36Sopenharmony_ci		else
83862306a36Sopenharmony_ci			host_id = 0;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		/* Set RXQ host ID */
84162306a36Sopenharmony_ci		val |= (host_id << (MSS_RXQ_ASS_Q_BASE(q, fq)
84262306a36Sopenharmony_ci			+ MSS_RXQ_ASS_HOSTID_OFFS));
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		mvpp2_cm3_write(port->priv, MSS_RXQ_ASS_REG(q, fq), val);
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* Notify Firmware that Flow control config space ready for update */
84862306a36Sopenharmony_ci	val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
84962306a36Sopenharmony_ci	val |= FLOW_CONTROL_UPDATE_COMMAND_BIT;
85062306a36Sopenharmony_ci	val |= cm3_state;
85162306a36Sopenharmony_ci	mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	spin_unlock_irqrestore(&port->priv->mss_spinlock, flags);
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci/* Routine disable flow control for RXQs condition */
85762306a36Sopenharmony_cistatic void mvpp2_rxq_disable_fc(struct mvpp2_port *port)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	int val, cm3_state, q;
86062306a36Sopenharmony_ci	unsigned long flags;
86162306a36Sopenharmony_ci	int fq = port->first_rxq;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	spin_lock_irqsave(&port->priv->mss_spinlock, flags);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/* Remove Flow control enable bit to prevent race between FW and Kernel
86662306a36Sopenharmony_ci	 * If Flow control was enabled, it would be re-enabled.
86762306a36Sopenharmony_ci	 */
86862306a36Sopenharmony_ci	val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
86962306a36Sopenharmony_ci	cm3_state = (val & FLOW_CONTROL_ENABLE_BIT);
87062306a36Sopenharmony_ci	val &= ~FLOW_CONTROL_ENABLE_BIT;
87162306a36Sopenharmony_ci	mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	/* Disable Flow control for all RXQs */
87462306a36Sopenharmony_ci	for (q = 0; q < port->nrxqs; q++) {
87562306a36Sopenharmony_ci		/* Set threshold 0 to disable Flow control */
87662306a36Sopenharmony_ci		val = 0;
87762306a36Sopenharmony_ci		val |= (0 << MSS_RXQ_TRESH_STOP_OFFS);
87862306a36Sopenharmony_ci		mvpp2_cm3_write(port->priv, MSS_RXQ_TRESH_REG(q, fq), val);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci		val = mvpp2_cm3_read(port->priv, MSS_RXQ_ASS_REG(q, fq));
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci		val &= ~(MSS_RXQ_ASS_PORTID_MASK << MSS_RXQ_ASS_Q_BASE(q, fq));
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci		val &= ~(MSS_RXQ_ASS_HOSTID_MASK << (MSS_RXQ_ASS_Q_BASE(q, fq)
88562306a36Sopenharmony_ci			+ MSS_RXQ_ASS_HOSTID_OFFS));
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci		mvpp2_cm3_write(port->priv, MSS_RXQ_ASS_REG(q, fq), val);
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	/* Notify Firmware that Flow control config space ready for update */
89162306a36Sopenharmony_ci	val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
89262306a36Sopenharmony_ci	val |= FLOW_CONTROL_UPDATE_COMMAND_BIT;
89362306a36Sopenharmony_ci	val |= cm3_state;
89462306a36Sopenharmony_ci	mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	spin_unlock_irqrestore(&port->priv->mss_spinlock, flags);
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci/* Routine disable/enable flow control for BM pool condition */
90062306a36Sopenharmony_cistatic void mvpp2_bm_pool_update_fc(struct mvpp2_port *port,
90162306a36Sopenharmony_ci				    struct mvpp2_bm_pool *pool,
90262306a36Sopenharmony_ci				    bool en)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	int val, cm3_state;
90562306a36Sopenharmony_ci	unsigned long flags;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	spin_lock_irqsave(&port->priv->mss_spinlock, flags);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* Remove Flow control enable bit to prevent race between FW and Kernel
91062306a36Sopenharmony_ci	 * If Flow control were enabled, it would be re-enabled.
91162306a36Sopenharmony_ci	 */
91262306a36Sopenharmony_ci	val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
91362306a36Sopenharmony_ci	cm3_state = (val & FLOW_CONTROL_ENABLE_BIT);
91462306a36Sopenharmony_ci	val &= ~FLOW_CONTROL_ENABLE_BIT;
91562306a36Sopenharmony_ci	mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	/* Check if BM pool should be enabled/disable */
91862306a36Sopenharmony_ci	if (en) {
91962306a36Sopenharmony_ci		/* Set BM pool start and stop thresholds per port */
92062306a36Sopenharmony_ci		val = mvpp2_cm3_read(port->priv, MSS_BUF_POOL_REG(pool->id));
92162306a36Sopenharmony_ci		val |= MSS_BUF_POOL_PORT_OFFS(port->id);
92262306a36Sopenharmony_ci		val &= ~MSS_BUF_POOL_START_MASK;
92362306a36Sopenharmony_ci		val |= (MSS_THRESHOLD_START << MSS_BUF_POOL_START_OFFS);
92462306a36Sopenharmony_ci		val &= ~MSS_BUF_POOL_STOP_MASK;
92562306a36Sopenharmony_ci		val |= MSS_THRESHOLD_STOP;
92662306a36Sopenharmony_ci		mvpp2_cm3_write(port->priv, MSS_BUF_POOL_REG(pool->id), val);
92762306a36Sopenharmony_ci	} else {
92862306a36Sopenharmony_ci		/* Remove BM pool from the port */
92962306a36Sopenharmony_ci		val = mvpp2_cm3_read(port->priv, MSS_BUF_POOL_REG(pool->id));
93062306a36Sopenharmony_ci		val &= ~MSS_BUF_POOL_PORT_OFFS(port->id);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci		/* Zero BM pool start and stop thresholds to disable pool
93362306a36Sopenharmony_ci		 * flow control if pool empty (not used by any port)
93462306a36Sopenharmony_ci		 */
93562306a36Sopenharmony_ci		if (!pool->buf_num) {
93662306a36Sopenharmony_ci			val &= ~MSS_BUF_POOL_START_MASK;
93762306a36Sopenharmony_ci			val &= ~MSS_BUF_POOL_STOP_MASK;
93862306a36Sopenharmony_ci		}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci		mvpp2_cm3_write(port->priv, MSS_BUF_POOL_REG(pool->id), val);
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/* Notify Firmware that Flow control config space ready for update */
94462306a36Sopenharmony_ci	val = mvpp2_cm3_read(port->priv, MSS_FC_COM_REG);
94562306a36Sopenharmony_ci	val |= FLOW_CONTROL_UPDATE_COMMAND_BIT;
94662306a36Sopenharmony_ci	val |= cm3_state;
94762306a36Sopenharmony_ci	mvpp2_cm3_write(port->priv, MSS_FC_COM_REG, val);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	spin_unlock_irqrestore(&port->priv->mss_spinlock, flags);
95062306a36Sopenharmony_ci}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci/* disable/enable flow control for BM pool on all ports */
95362306a36Sopenharmony_cistatic void mvpp2_bm_pool_update_priv_fc(struct mvpp2 *priv, bool en)
95462306a36Sopenharmony_ci{
95562306a36Sopenharmony_ci	struct mvpp2_port *port;
95662306a36Sopenharmony_ci	int i;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	for (i = 0; i < priv->port_count; i++) {
95962306a36Sopenharmony_ci		port = priv->port_list[i];
96062306a36Sopenharmony_ci		if (port->priv->percpu_pools) {
96162306a36Sopenharmony_ci			for (i = 0; i < port->nrxqs; i++)
96262306a36Sopenharmony_ci				mvpp2_bm_pool_update_fc(port, &port->priv->bm_pools[i],
96362306a36Sopenharmony_ci							port->tx_fc & en);
96462306a36Sopenharmony_ci		} else {
96562306a36Sopenharmony_ci			mvpp2_bm_pool_update_fc(port, port->pool_long, port->tx_fc & en);
96662306a36Sopenharmony_ci			mvpp2_bm_pool_update_fc(port, port->pool_short, port->tx_fc & en);
96762306a36Sopenharmony_ci		}
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cistatic int mvpp2_enable_global_fc(struct mvpp2 *priv)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	int val, timeout = 0;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	/* Enable global flow control. In this stage global
97662306a36Sopenharmony_ci	 * flow control enabled, but still disabled per port.
97762306a36Sopenharmony_ci	 */
97862306a36Sopenharmony_ci	val = mvpp2_cm3_read(priv, MSS_FC_COM_REG);
97962306a36Sopenharmony_ci	val |= FLOW_CONTROL_ENABLE_BIT;
98062306a36Sopenharmony_ci	mvpp2_cm3_write(priv, MSS_FC_COM_REG, val);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	/* Check if Firmware running and disable FC if not*/
98362306a36Sopenharmony_ci	val |= FLOW_CONTROL_UPDATE_COMMAND_BIT;
98462306a36Sopenharmony_ci	mvpp2_cm3_write(priv, MSS_FC_COM_REG, val);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	while (timeout < MSS_FC_MAX_TIMEOUT) {
98762306a36Sopenharmony_ci		val = mvpp2_cm3_read(priv, MSS_FC_COM_REG);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci		if (!(val & FLOW_CONTROL_UPDATE_COMMAND_BIT))
99062306a36Sopenharmony_ci			return 0;
99162306a36Sopenharmony_ci		usleep_range(10, 20);
99262306a36Sopenharmony_ci		timeout++;
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	priv->global_tx_fc = false;
99662306a36Sopenharmony_ci	return -EOPNOTSUPP;
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci/* Release buffer to BM */
100062306a36Sopenharmony_cistatic inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool,
100162306a36Sopenharmony_ci				     dma_addr_t buf_dma_addr,
100262306a36Sopenharmony_ci				     phys_addr_t buf_phys_addr)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	unsigned int thread = mvpp2_cpu_to_thread(port->priv, get_cpu());
100562306a36Sopenharmony_ci	unsigned long flags = 0;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (test_bit(thread, &port->priv->lock_map))
100862306a36Sopenharmony_ci		spin_lock_irqsave(&port->bm_lock[thread], flags);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (port->priv->hw_version >= MVPP22) {
101162306a36Sopenharmony_ci		u32 val = 0;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci		if (sizeof(dma_addr_t) == 8)
101462306a36Sopenharmony_ci			val |= upper_32_bits(buf_dma_addr) &
101562306a36Sopenharmony_ci				MVPP22_BM_ADDR_HIGH_PHYS_RLS_MASK;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci		if (sizeof(phys_addr_t) == 8)
101862306a36Sopenharmony_ci			val |= (upper_32_bits(buf_phys_addr)
101962306a36Sopenharmony_ci				<< MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT) &
102062306a36Sopenharmony_ci				MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci		mvpp2_thread_write_relaxed(port->priv, thread,
102362306a36Sopenharmony_ci					   MVPP22_BM_ADDR_HIGH_RLS_REG, val);
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	/* MVPP2_BM_VIRT_RLS_REG is not interpreted by HW, and simply
102762306a36Sopenharmony_ci	 * returned in the "cookie" field of the RX
102862306a36Sopenharmony_ci	 * descriptor. Instead of storing the virtual address, we
102962306a36Sopenharmony_ci	 * store the physical address
103062306a36Sopenharmony_ci	 */
103162306a36Sopenharmony_ci	mvpp2_thread_write_relaxed(port->priv, thread,
103262306a36Sopenharmony_ci				   MVPP2_BM_VIRT_RLS_REG, buf_phys_addr);
103362306a36Sopenharmony_ci	mvpp2_thread_write_relaxed(port->priv, thread,
103462306a36Sopenharmony_ci				   MVPP2_BM_PHY_RLS_REG(pool), buf_dma_addr);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	if (test_bit(thread, &port->priv->lock_map))
103762306a36Sopenharmony_ci		spin_unlock_irqrestore(&port->bm_lock[thread], flags);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	put_cpu();
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci/* Allocate buffers for the pool */
104362306a36Sopenharmony_cistatic int mvpp2_bm_bufs_add(struct mvpp2_port *port,
104462306a36Sopenharmony_ci			     struct mvpp2_bm_pool *bm_pool, int buf_num)
104562306a36Sopenharmony_ci{
104662306a36Sopenharmony_ci	int i, buf_size, total_size;
104762306a36Sopenharmony_ci	dma_addr_t dma_addr;
104862306a36Sopenharmony_ci	phys_addr_t phys_addr;
104962306a36Sopenharmony_ci	struct page_pool *pp = NULL;
105062306a36Sopenharmony_ci	void *buf;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (port->priv->percpu_pools &&
105362306a36Sopenharmony_ci	    bm_pool->pkt_size > MVPP2_BM_LONG_PKT_SIZE) {
105462306a36Sopenharmony_ci		netdev_err(port->dev,
105562306a36Sopenharmony_ci			   "attempted to use jumbo frames with per-cpu pools");
105662306a36Sopenharmony_ci		return 0;
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	buf_size = MVPP2_RX_BUF_SIZE(bm_pool->pkt_size);
106062306a36Sopenharmony_ci	total_size = MVPP2_RX_TOTAL_SIZE(buf_size);
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	if (buf_num < 0 ||
106362306a36Sopenharmony_ci	    (buf_num + bm_pool->buf_num > bm_pool->size)) {
106462306a36Sopenharmony_ci		netdev_err(port->dev,
106562306a36Sopenharmony_ci			   "cannot allocate %d buffers for pool %d\n",
106662306a36Sopenharmony_ci			   buf_num, bm_pool->id);
106762306a36Sopenharmony_ci		return 0;
106862306a36Sopenharmony_ci	}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	if (port->priv->percpu_pools)
107162306a36Sopenharmony_ci		pp = port->priv->page_pool[bm_pool->id];
107262306a36Sopenharmony_ci	for (i = 0; i < buf_num; i++) {
107362306a36Sopenharmony_ci		buf = mvpp2_buf_alloc(port, bm_pool, pp, &dma_addr,
107462306a36Sopenharmony_ci				      &phys_addr, GFP_KERNEL);
107562306a36Sopenharmony_ci		if (!buf)
107662306a36Sopenharmony_ci			break;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci		mvpp2_bm_pool_put(port, bm_pool->id, dma_addr,
107962306a36Sopenharmony_ci				  phys_addr);
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	/* Update BM driver with number of buffers added to pool */
108362306a36Sopenharmony_ci	bm_pool->buf_num += i;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	netdev_dbg(port->dev,
108662306a36Sopenharmony_ci		   "pool %d: pkt_size=%4d, buf_size=%4d, total_size=%4d\n",
108762306a36Sopenharmony_ci		   bm_pool->id, bm_pool->pkt_size, buf_size, total_size);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	netdev_dbg(port->dev,
109062306a36Sopenharmony_ci		   "pool %d: %d of %d buffers added\n",
109162306a36Sopenharmony_ci		   bm_pool->id, i, buf_num);
109262306a36Sopenharmony_ci	return i;
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci/* Notify the driver that BM pool is being used as specific type and return the
109662306a36Sopenharmony_ci * pool pointer on success
109762306a36Sopenharmony_ci */
109862306a36Sopenharmony_cistatic struct mvpp2_bm_pool *
109962306a36Sopenharmony_cimvpp2_bm_pool_use(struct mvpp2_port *port, unsigned pool, int pkt_size)
110062306a36Sopenharmony_ci{
110162306a36Sopenharmony_ci	struct mvpp2_bm_pool *new_pool = &port->priv->bm_pools[pool];
110262306a36Sopenharmony_ci	int num;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if ((port->priv->percpu_pools && pool > mvpp2_get_nrxqs(port->priv) * 2) ||
110562306a36Sopenharmony_ci	    (!port->priv->percpu_pools && pool >= MVPP2_BM_POOLS_NUM)) {
110662306a36Sopenharmony_ci		netdev_err(port->dev, "Invalid pool %d\n", pool);
110762306a36Sopenharmony_ci		return NULL;
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	/* Allocate buffers in case BM pool is used as long pool, but packet
111162306a36Sopenharmony_ci	 * size doesn't match MTU or BM pool hasn't being used yet
111262306a36Sopenharmony_ci	 */
111362306a36Sopenharmony_ci	if (new_pool->pkt_size == 0) {
111462306a36Sopenharmony_ci		int pkts_num;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		/* Set default buffer number or free all the buffers in case
111762306a36Sopenharmony_ci		 * the pool is not empty
111862306a36Sopenharmony_ci		 */
111962306a36Sopenharmony_ci		pkts_num = new_pool->buf_num;
112062306a36Sopenharmony_ci		if (pkts_num == 0) {
112162306a36Sopenharmony_ci			if (port->priv->percpu_pools) {
112262306a36Sopenharmony_ci				if (pool < port->nrxqs)
112362306a36Sopenharmony_ci					pkts_num = mvpp2_pools[MVPP2_BM_SHORT].buf_num;
112462306a36Sopenharmony_ci				else
112562306a36Sopenharmony_ci					pkts_num = mvpp2_pools[MVPP2_BM_LONG].buf_num;
112662306a36Sopenharmony_ci			} else {
112762306a36Sopenharmony_ci				pkts_num = mvpp2_pools[pool].buf_num;
112862306a36Sopenharmony_ci			}
112962306a36Sopenharmony_ci		} else {
113062306a36Sopenharmony_ci			mvpp2_bm_bufs_free(port->dev->dev.parent,
113162306a36Sopenharmony_ci					   port->priv, new_pool, pkts_num);
113262306a36Sopenharmony_ci		}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci		new_pool->pkt_size = pkt_size;
113562306a36Sopenharmony_ci		new_pool->frag_size =
113662306a36Sopenharmony_ci			SKB_DATA_ALIGN(MVPP2_RX_BUF_SIZE(pkt_size)) +
113762306a36Sopenharmony_ci			MVPP2_SKB_SHINFO_SIZE;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci		/* Allocate buffers for this pool */
114062306a36Sopenharmony_ci		num = mvpp2_bm_bufs_add(port, new_pool, pkts_num);
114162306a36Sopenharmony_ci		if (num != pkts_num) {
114262306a36Sopenharmony_ci			WARN(1, "pool %d: %d of %d allocated\n",
114362306a36Sopenharmony_ci			     new_pool->id, num, pkts_num);
114462306a36Sopenharmony_ci			return NULL;
114562306a36Sopenharmony_ci		}
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	mvpp2_bm_pool_bufsize_set(port->priv, new_pool,
114962306a36Sopenharmony_ci				  MVPP2_RX_BUF_SIZE(new_pool->pkt_size));
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	return new_pool;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic struct mvpp2_bm_pool *
115562306a36Sopenharmony_cimvpp2_bm_pool_use_percpu(struct mvpp2_port *port, int type,
115662306a36Sopenharmony_ci			 unsigned int pool, int pkt_size)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct mvpp2_bm_pool *new_pool = &port->priv->bm_pools[pool];
115962306a36Sopenharmony_ci	int num;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	if (pool > port->nrxqs * 2) {
116262306a36Sopenharmony_ci		netdev_err(port->dev, "Invalid pool %d\n", pool);
116362306a36Sopenharmony_ci		return NULL;
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	/* Allocate buffers in case BM pool is used as long pool, but packet
116762306a36Sopenharmony_ci	 * size doesn't match MTU or BM pool hasn't being used yet
116862306a36Sopenharmony_ci	 */
116962306a36Sopenharmony_ci	if (new_pool->pkt_size == 0) {
117062306a36Sopenharmony_ci		int pkts_num;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci		/* Set default buffer number or free all the buffers in case
117362306a36Sopenharmony_ci		 * the pool is not empty
117462306a36Sopenharmony_ci		 */
117562306a36Sopenharmony_ci		pkts_num = new_pool->buf_num;
117662306a36Sopenharmony_ci		if (pkts_num == 0)
117762306a36Sopenharmony_ci			pkts_num = mvpp2_pools[type].buf_num;
117862306a36Sopenharmony_ci		else
117962306a36Sopenharmony_ci			mvpp2_bm_bufs_free(port->dev->dev.parent,
118062306a36Sopenharmony_ci					   port->priv, new_pool, pkts_num);
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci		new_pool->pkt_size = pkt_size;
118362306a36Sopenharmony_ci		new_pool->frag_size =
118462306a36Sopenharmony_ci			SKB_DATA_ALIGN(MVPP2_RX_BUF_SIZE(pkt_size)) +
118562306a36Sopenharmony_ci			MVPP2_SKB_SHINFO_SIZE;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		/* Allocate buffers for this pool */
118862306a36Sopenharmony_ci		num = mvpp2_bm_bufs_add(port, new_pool, pkts_num);
118962306a36Sopenharmony_ci		if (num != pkts_num) {
119062306a36Sopenharmony_ci			WARN(1, "pool %d: %d of %d allocated\n",
119162306a36Sopenharmony_ci			     new_pool->id, num, pkts_num);
119262306a36Sopenharmony_ci			return NULL;
119362306a36Sopenharmony_ci		}
119462306a36Sopenharmony_ci	}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	mvpp2_bm_pool_bufsize_set(port->priv, new_pool,
119762306a36Sopenharmony_ci				  MVPP2_RX_BUF_SIZE(new_pool->pkt_size));
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	return new_pool;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci/* Initialize pools for swf, shared buffers variant */
120362306a36Sopenharmony_cistatic int mvpp2_swf_bm_pool_init_shared(struct mvpp2_port *port)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	enum mvpp2_bm_pool_log_num long_log_pool, short_log_pool;
120662306a36Sopenharmony_ci	int rxq;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	/* If port pkt_size is higher than 1518B:
120962306a36Sopenharmony_ci	 * HW Long pool - SW Jumbo pool, HW Short pool - SW Long pool
121062306a36Sopenharmony_ci	 * else: HW Long pool - SW Long pool, HW Short pool - SW Short pool
121162306a36Sopenharmony_ci	 */
121262306a36Sopenharmony_ci	if (port->pkt_size > MVPP2_BM_LONG_PKT_SIZE) {
121362306a36Sopenharmony_ci		long_log_pool = MVPP2_BM_JUMBO;
121462306a36Sopenharmony_ci		short_log_pool = MVPP2_BM_LONG;
121562306a36Sopenharmony_ci	} else {
121662306a36Sopenharmony_ci		long_log_pool = MVPP2_BM_LONG;
121762306a36Sopenharmony_ci		short_log_pool = MVPP2_BM_SHORT;
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	if (!port->pool_long) {
122162306a36Sopenharmony_ci		port->pool_long =
122262306a36Sopenharmony_ci			mvpp2_bm_pool_use(port, long_log_pool,
122362306a36Sopenharmony_ci					  mvpp2_pools[long_log_pool].pkt_size);
122462306a36Sopenharmony_ci		if (!port->pool_long)
122562306a36Sopenharmony_ci			return -ENOMEM;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci		port->pool_long->port_map |= BIT(port->id);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci		for (rxq = 0; rxq < port->nrxqs; rxq++)
123062306a36Sopenharmony_ci			mvpp2_rxq_long_pool_set(port, rxq, port->pool_long->id);
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	if (!port->pool_short) {
123462306a36Sopenharmony_ci		port->pool_short =
123562306a36Sopenharmony_ci			mvpp2_bm_pool_use(port, short_log_pool,
123662306a36Sopenharmony_ci					  mvpp2_pools[short_log_pool].pkt_size);
123762306a36Sopenharmony_ci		if (!port->pool_short)
123862306a36Sopenharmony_ci			return -ENOMEM;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci		port->pool_short->port_map |= BIT(port->id);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci		for (rxq = 0; rxq < port->nrxqs; rxq++)
124362306a36Sopenharmony_ci			mvpp2_rxq_short_pool_set(port, rxq,
124462306a36Sopenharmony_ci						 port->pool_short->id);
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	return 0;
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci/* Initialize pools for swf, percpu buffers variant */
125162306a36Sopenharmony_cistatic int mvpp2_swf_bm_pool_init_percpu(struct mvpp2_port *port)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	struct mvpp2_bm_pool *bm_pool;
125462306a36Sopenharmony_ci	int i;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	for (i = 0; i < port->nrxqs; i++) {
125762306a36Sopenharmony_ci		bm_pool = mvpp2_bm_pool_use_percpu(port, MVPP2_BM_SHORT, i,
125862306a36Sopenharmony_ci						   mvpp2_pools[MVPP2_BM_SHORT].pkt_size);
125962306a36Sopenharmony_ci		if (!bm_pool)
126062306a36Sopenharmony_ci			return -ENOMEM;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci		bm_pool->port_map |= BIT(port->id);
126362306a36Sopenharmony_ci		mvpp2_rxq_short_pool_set(port, i, bm_pool->id);
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	for (i = 0; i < port->nrxqs; i++) {
126762306a36Sopenharmony_ci		bm_pool = mvpp2_bm_pool_use_percpu(port, MVPP2_BM_LONG, i + port->nrxqs,
126862306a36Sopenharmony_ci						   mvpp2_pools[MVPP2_BM_LONG].pkt_size);
126962306a36Sopenharmony_ci		if (!bm_pool)
127062306a36Sopenharmony_ci			return -ENOMEM;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci		bm_pool->port_map |= BIT(port->id);
127362306a36Sopenharmony_ci		mvpp2_rxq_long_pool_set(port, i, bm_pool->id);
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	port->pool_long = NULL;
127762306a36Sopenharmony_ci	port->pool_short = NULL;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	return 0;
128062306a36Sopenharmony_ci}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_cistatic int mvpp2_swf_bm_pool_init(struct mvpp2_port *port)
128362306a36Sopenharmony_ci{
128462306a36Sopenharmony_ci	if (port->priv->percpu_pools)
128562306a36Sopenharmony_ci		return mvpp2_swf_bm_pool_init_percpu(port);
128662306a36Sopenharmony_ci	else
128762306a36Sopenharmony_ci		return mvpp2_swf_bm_pool_init_shared(port);
128862306a36Sopenharmony_ci}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cistatic void mvpp2_set_hw_csum(struct mvpp2_port *port,
129162306a36Sopenharmony_ci			      enum mvpp2_bm_pool_log_num new_long_pool)
129262306a36Sopenharmony_ci{
129362306a36Sopenharmony_ci	const netdev_features_t csums = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	/* Update L4 checksum when jumbo enable/disable on port.
129662306a36Sopenharmony_ci	 * Only port 0 supports hardware checksum offload due to
129762306a36Sopenharmony_ci	 * the Tx FIFO size limitation.
129862306a36Sopenharmony_ci	 * Also, don't set NETIF_F_HW_CSUM because L3_offset in TX descriptor
129962306a36Sopenharmony_ci	 * has 7 bits, so the maximum L3 offset is 128.
130062306a36Sopenharmony_ci	 */
130162306a36Sopenharmony_ci	if (new_long_pool == MVPP2_BM_JUMBO && port->id != 0) {
130262306a36Sopenharmony_ci		port->dev->features &= ~csums;
130362306a36Sopenharmony_ci		port->dev->hw_features &= ~csums;
130462306a36Sopenharmony_ci	} else {
130562306a36Sopenharmony_ci		port->dev->features |= csums;
130662306a36Sopenharmony_ci		port->dev->hw_features |= csums;
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_cistatic int mvpp2_bm_update_mtu(struct net_device *dev, int mtu)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
131362306a36Sopenharmony_ci	enum mvpp2_bm_pool_log_num new_long_pool;
131462306a36Sopenharmony_ci	int pkt_size = MVPP2_RX_PKT_SIZE(mtu);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	if (port->priv->percpu_pools)
131762306a36Sopenharmony_ci		goto out_set;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	/* If port MTU is higher than 1518B:
132062306a36Sopenharmony_ci	 * HW Long pool - SW Jumbo pool, HW Short pool - SW Long pool
132162306a36Sopenharmony_ci	 * else: HW Long pool - SW Long pool, HW Short pool - SW Short pool
132262306a36Sopenharmony_ci	 */
132362306a36Sopenharmony_ci	if (pkt_size > MVPP2_BM_LONG_PKT_SIZE)
132462306a36Sopenharmony_ci		new_long_pool = MVPP2_BM_JUMBO;
132562306a36Sopenharmony_ci	else
132662306a36Sopenharmony_ci		new_long_pool = MVPP2_BM_LONG;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	if (new_long_pool != port->pool_long->id) {
132962306a36Sopenharmony_ci		if (port->tx_fc) {
133062306a36Sopenharmony_ci			if (pkt_size > MVPP2_BM_LONG_PKT_SIZE)
133162306a36Sopenharmony_ci				mvpp2_bm_pool_update_fc(port,
133262306a36Sopenharmony_ci							port->pool_short,
133362306a36Sopenharmony_ci							false);
133462306a36Sopenharmony_ci			else
133562306a36Sopenharmony_ci				mvpp2_bm_pool_update_fc(port, port->pool_long,
133662306a36Sopenharmony_ci							false);
133762306a36Sopenharmony_ci		}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci		/* Remove port from old short & long pool */
134062306a36Sopenharmony_ci		port->pool_long = mvpp2_bm_pool_use(port, port->pool_long->id,
134162306a36Sopenharmony_ci						    port->pool_long->pkt_size);
134262306a36Sopenharmony_ci		port->pool_long->port_map &= ~BIT(port->id);
134362306a36Sopenharmony_ci		port->pool_long = NULL;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci		port->pool_short = mvpp2_bm_pool_use(port, port->pool_short->id,
134662306a36Sopenharmony_ci						     port->pool_short->pkt_size);
134762306a36Sopenharmony_ci		port->pool_short->port_map &= ~BIT(port->id);
134862306a36Sopenharmony_ci		port->pool_short = NULL;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci		port->pkt_size =  pkt_size;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci		/* Add port to new short & long pool */
135362306a36Sopenharmony_ci		mvpp2_swf_bm_pool_init(port);
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci		mvpp2_set_hw_csum(port, new_long_pool);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci		if (port->tx_fc) {
135862306a36Sopenharmony_ci			if (pkt_size > MVPP2_BM_LONG_PKT_SIZE)
135962306a36Sopenharmony_ci				mvpp2_bm_pool_update_fc(port, port->pool_long,
136062306a36Sopenharmony_ci							true);
136162306a36Sopenharmony_ci			else
136262306a36Sopenharmony_ci				mvpp2_bm_pool_update_fc(port, port->pool_short,
136362306a36Sopenharmony_ci							true);
136462306a36Sopenharmony_ci		}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci		/* Update L4 checksum when jumbo enable/disable on port */
136762306a36Sopenharmony_ci		if (new_long_pool == MVPP2_BM_JUMBO && port->id != 0) {
136862306a36Sopenharmony_ci			dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
136962306a36Sopenharmony_ci			dev->hw_features &= ~(NETIF_F_IP_CSUM |
137062306a36Sopenharmony_ci					      NETIF_F_IPV6_CSUM);
137162306a36Sopenharmony_ci		} else {
137262306a36Sopenharmony_ci			dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
137362306a36Sopenharmony_ci			dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
137462306a36Sopenharmony_ci		}
137562306a36Sopenharmony_ci	}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ciout_set:
137862306a36Sopenharmony_ci	dev->mtu = mtu;
137962306a36Sopenharmony_ci	dev->wanted_features = dev->features;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	netdev_update_features(dev);
138262306a36Sopenharmony_ci	return 0;
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_cistatic inline void mvpp2_interrupts_enable(struct mvpp2_port *port)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	int i, sw_thread_mask = 0;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++)
139062306a36Sopenharmony_ci		sw_thread_mask |= port->qvecs[i].sw_thread_mask;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id),
139362306a36Sopenharmony_ci		    MVPP2_ISR_ENABLE_INTERRUPT(sw_thread_mask));
139462306a36Sopenharmony_ci}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_cistatic inline void mvpp2_interrupts_disable(struct mvpp2_port *port)
139762306a36Sopenharmony_ci{
139862306a36Sopenharmony_ci	int i, sw_thread_mask = 0;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++)
140162306a36Sopenharmony_ci		sw_thread_mask |= port->qvecs[i].sw_thread_mask;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id),
140462306a36Sopenharmony_ci		    MVPP2_ISR_DISABLE_INTERRUPT(sw_thread_mask));
140562306a36Sopenharmony_ci}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_cistatic inline void mvpp2_qvec_interrupt_enable(struct mvpp2_queue_vector *qvec)
140862306a36Sopenharmony_ci{
140962306a36Sopenharmony_ci	struct mvpp2_port *port = qvec->port;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id),
141262306a36Sopenharmony_ci		    MVPP2_ISR_ENABLE_INTERRUPT(qvec->sw_thread_mask));
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_cistatic inline void mvpp2_qvec_interrupt_disable(struct mvpp2_queue_vector *qvec)
141662306a36Sopenharmony_ci{
141762306a36Sopenharmony_ci	struct mvpp2_port *port = qvec->port;
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id),
142062306a36Sopenharmony_ci		    MVPP2_ISR_DISABLE_INTERRUPT(qvec->sw_thread_mask));
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci/* Mask the current thread's Rx/Tx interrupts
142462306a36Sopenharmony_ci * Called by on_each_cpu(), guaranteed to run with migration disabled,
142562306a36Sopenharmony_ci * using smp_processor_id() is OK.
142662306a36Sopenharmony_ci */
142762306a36Sopenharmony_cistatic void mvpp2_interrupts_mask(void *arg)
142862306a36Sopenharmony_ci{
142962306a36Sopenharmony_ci	struct mvpp2_port *port = arg;
143062306a36Sopenharmony_ci	int cpu = smp_processor_id();
143162306a36Sopenharmony_ci	u32 thread;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	/* If the thread isn't used, don't do anything */
143462306a36Sopenharmony_ci	if (cpu > port->priv->nthreads)
143562306a36Sopenharmony_ci		return;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	thread = mvpp2_cpu_to_thread(port->priv, cpu);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread,
144062306a36Sopenharmony_ci			   MVPP2_ISR_RX_TX_MASK_REG(port->id), 0);
144162306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread,
144262306a36Sopenharmony_ci			   MVPP2_ISR_RX_ERR_CAUSE_REG(port->id), 0);
144362306a36Sopenharmony_ci}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci/* Unmask the current thread's Rx/Tx interrupts.
144662306a36Sopenharmony_ci * Called by on_each_cpu(), guaranteed to run with migration disabled,
144762306a36Sopenharmony_ci * using smp_processor_id() is OK.
144862306a36Sopenharmony_ci */
144962306a36Sopenharmony_cistatic void mvpp2_interrupts_unmask(void *arg)
145062306a36Sopenharmony_ci{
145162306a36Sopenharmony_ci	struct mvpp2_port *port = arg;
145262306a36Sopenharmony_ci	int cpu = smp_processor_id();
145362306a36Sopenharmony_ci	u32 val, thread;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	/* If the thread isn't used, don't do anything */
145662306a36Sopenharmony_ci	if (cpu >= port->priv->nthreads)
145762306a36Sopenharmony_ci		return;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	thread = mvpp2_cpu_to_thread(port->priv, cpu);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	val = MVPP2_CAUSE_MISC_SUM_MASK |
146262306a36Sopenharmony_ci		MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK(port->priv->hw_version);
146362306a36Sopenharmony_ci	if (port->has_tx_irqs)
146462306a36Sopenharmony_ci		val |= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread,
146762306a36Sopenharmony_ci			   MVPP2_ISR_RX_TX_MASK_REG(port->id), val);
146862306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread,
146962306a36Sopenharmony_ci			   MVPP2_ISR_RX_ERR_CAUSE_REG(port->id),
147062306a36Sopenharmony_ci			   MVPP2_ISR_RX_ERR_CAUSE_NONOCC_MASK);
147162306a36Sopenharmony_ci}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_cistatic void
147462306a36Sopenharmony_cimvpp2_shared_interrupt_mask_unmask(struct mvpp2_port *port, bool mask)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	u32 val;
147762306a36Sopenharmony_ci	int i;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
148062306a36Sopenharmony_ci		return;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	if (mask)
148362306a36Sopenharmony_ci		val = 0;
148462306a36Sopenharmony_ci	else
148562306a36Sopenharmony_ci		val = MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK(MVPP22);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++) {
148862306a36Sopenharmony_ci		struct mvpp2_queue_vector *v = port->qvecs + i;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci		if (v->type != MVPP2_QUEUE_VECTOR_SHARED)
149162306a36Sopenharmony_ci			continue;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci		mvpp2_thread_write(port->priv, v->sw_thread_id,
149462306a36Sopenharmony_ci				   MVPP2_ISR_RX_TX_MASK_REG(port->id), val);
149562306a36Sopenharmony_ci		mvpp2_thread_write(port->priv, v->sw_thread_id,
149662306a36Sopenharmony_ci				   MVPP2_ISR_RX_ERR_CAUSE_REG(port->id),
149762306a36Sopenharmony_ci				   MVPP2_ISR_RX_ERR_CAUSE_NONOCC_MASK);
149862306a36Sopenharmony_ci	}
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci/* Only GOP port 0 has an XLG MAC */
150262306a36Sopenharmony_cistatic bool mvpp2_port_supports_xlg(struct mvpp2_port *port)
150362306a36Sopenharmony_ci{
150462306a36Sopenharmony_ci	return port->gop_id == 0;
150562306a36Sopenharmony_ci}
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_cistatic bool mvpp2_port_supports_rgmii(struct mvpp2_port *port)
150862306a36Sopenharmony_ci{
150962306a36Sopenharmony_ci	return !(port->priv->hw_version >= MVPP22 && port->gop_id == 0);
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci/* Port configuration routines */
151362306a36Sopenharmony_cistatic bool mvpp2_is_xlg(phy_interface_t interface)
151462306a36Sopenharmony_ci{
151562306a36Sopenharmony_ci	return interface == PHY_INTERFACE_MODE_10GBASER ||
151662306a36Sopenharmony_ci	       interface == PHY_INTERFACE_MODE_5GBASER ||
151762306a36Sopenharmony_ci	       interface == PHY_INTERFACE_MODE_XAUI;
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_cistatic void mvpp2_modify(void __iomem *ptr, u32 mask, u32 set)
152162306a36Sopenharmony_ci{
152262306a36Sopenharmony_ci	u32 old, val;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	old = val = readl(ptr);
152562306a36Sopenharmony_ci	val &= ~mask;
152662306a36Sopenharmony_ci	val |= set;
152762306a36Sopenharmony_ci	if (old != val)
152862306a36Sopenharmony_ci		writel(val, ptr);
152962306a36Sopenharmony_ci}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_cistatic void mvpp22_gop_init_rgmii(struct mvpp2_port *port)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
153462306a36Sopenharmony_ci	u32 val;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val);
153762306a36Sopenharmony_ci	val |= GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT;
153862306a36Sopenharmony_ci	regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	regmap_read(priv->sysctrl_base, GENCONF_CTRL0, &val);
154162306a36Sopenharmony_ci	if (port->gop_id == 2)
154262306a36Sopenharmony_ci		val |= GENCONF_CTRL0_PORT2_RGMII;
154362306a36Sopenharmony_ci	else if (port->gop_id == 3)
154462306a36Sopenharmony_ci		val |= GENCONF_CTRL0_PORT3_RGMII_MII;
154562306a36Sopenharmony_ci	regmap_write(priv->sysctrl_base, GENCONF_CTRL0, val);
154662306a36Sopenharmony_ci}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_cistatic void mvpp22_gop_init_sgmii(struct mvpp2_port *port)
154962306a36Sopenharmony_ci{
155062306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
155162306a36Sopenharmony_ci	u32 val;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val);
155462306a36Sopenharmony_ci	val |= GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT |
155562306a36Sopenharmony_ci	       GENCONF_PORT_CTRL0_RX_DATA_SAMPLE;
155662306a36Sopenharmony_ci	regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	if (port->gop_id > 1) {
155962306a36Sopenharmony_ci		regmap_read(priv->sysctrl_base, GENCONF_CTRL0, &val);
156062306a36Sopenharmony_ci		if (port->gop_id == 2)
156162306a36Sopenharmony_ci			val &= ~GENCONF_CTRL0_PORT2_RGMII;
156262306a36Sopenharmony_ci		else if (port->gop_id == 3)
156362306a36Sopenharmony_ci			val &= ~GENCONF_CTRL0_PORT3_RGMII_MII;
156462306a36Sopenharmony_ci		regmap_write(priv->sysctrl_base, GENCONF_CTRL0, val);
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_cistatic void mvpp22_gop_init_10gkr(struct mvpp2_port *port)
156962306a36Sopenharmony_ci{
157062306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
157162306a36Sopenharmony_ci	void __iomem *mpcs = priv->iface_base + MVPP22_MPCS_BASE(port->gop_id);
157262306a36Sopenharmony_ci	void __iomem *xpcs = priv->iface_base + MVPP22_XPCS_BASE(port->gop_id);
157362306a36Sopenharmony_ci	u32 val;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	val = readl(xpcs + MVPP22_XPCS_CFG0);
157662306a36Sopenharmony_ci	val &= ~(MVPP22_XPCS_CFG0_PCS_MODE(0x3) |
157762306a36Sopenharmony_ci		 MVPP22_XPCS_CFG0_ACTIVE_LANE(0x3));
157862306a36Sopenharmony_ci	val |= MVPP22_XPCS_CFG0_ACTIVE_LANE(2);
157962306a36Sopenharmony_ci	writel(val, xpcs + MVPP22_XPCS_CFG0);
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	val = readl(mpcs + MVPP22_MPCS_CTRL);
158262306a36Sopenharmony_ci	val &= ~MVPP22_MPCS_CTRL_FWD_ERR_CONN;
158362306a36Sopenharmony_ci	writel(val, mpcs + MVPP22_MPCS_CTRL);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	val = readl(mpcs + MVPP22_MPCS_CLK_RESET);
158662306a36Sopenharmony_ci	val &= ~MVPP22_MPCS_CLK_RESET_DIV_RATIO(0x7);
158762306a36Sopenharmony_ci	val |= MVPP22_MPCS_CLK_RESET_DIV_RATIO(1);
158862306a36Sopenharmony_ci	writel(val, mpcs + MVPP22_MPCS_CLK_RESET);
158962306a36Sopenharmony_ci}
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_cistatic void mvpp22_gop_fca_enable_periodic(struct mvpp2_port *port, bool en)
159262306a36Sopenharmony_ci{
159362306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
159462306a36Sopenharmony_ci	void __iomem *fca = priv->iface_base + MVPP22_FCA_BASE(port->gop_id);
159562306a36Sopenharmony_ci	u32 val;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	val = readl(fca + MVPP22_FCA_CONTROL_REG);
159862306a36Sopenharmony_ci	val &= ~MVPP22_FCA_ENABLE_PERIODIC;
159962306a36Sopenharmony_ci	if (en)
160062306a36Sopenharmony_ci		val |= MVPP22_FCA_ENABLE_PERIODIC;
160162306a36Sopenharmony_ci	writel(val, fca + MVPP22_FCA_CONTROL_REG);
160262306a36Sopenharmony_ci}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_cistatic void mvpp22_gop_fca_set_timer(struct mvpp2_port *port, u32 timer)
160562306a36Sopenharmony_ci{
160662306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
160762306a36Sopenharmony_ci	void __iomem *fca = priv->iface_base + MVPP22_FCA_BASE(port->gop_id);
160862306a36Sopenharmony_ci	u32 lsb, msb;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	lsb = timer & MVPP22_FCA_REG_MASK;
161162306a36Sopenharmony_ci	msb = timer >> MVPP22_FCA_REG_SIZE;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	writel(lsb, fca + MVPP22_PERIODIC_COUNTER_LSB_REG);
161462306a36Sopenharmony_ci	writel(msb, fca + MVPP22_PERIODIC_COUNTER_MSB_REG);
161562306a36Sopenharmony_ci}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci/* Set Flow Control timer x100 faster than pause quanta to ensure that link
161862306a36Sopenharmony_ci * partner won't send traffic if port is in XOFF mode.
161962306a36Sopenharmony_ci */
162062306a36Sopenharmony_cistatic void mvpp22_gop_fca_set_periodic_timer(struct mvpp2_port *port)
162162306a36Sopenharmony_ci{
162262306a36Sopenharmony_ci	u32 timer;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	timer = (port->priv->tclk / (USEC_PER_SEC * FC_CLK_DIVIDER))
162562306a36Sopenharmony_ci		* FC_QUANTA;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	mvpp22_gop_fca_enable_periodic(port, false);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	mvpp22_gop_fca_set_timer(port, timer);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	mvpp22_gop_fca_enable_periodic(port, true);
163262306a36Sopenharmony_ci}
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_cistatic int mvpp22_gop_init(struct mvpp2_port *port, phy_interface_t interface)
163562306a36Sopenharmony_ci{
163662306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
163762306a36Sopenharmony_ci	u32 val;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	if (!priv->sysctrl_base)
164062306a36Sopenharmony_ci		return 0;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	switch (interface) {
164362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII:
164462306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_ID:
164562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_RXID:
164662306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_TXID:
164762306a36Sopenharmony_ci		if (!mvpp2_port_supports_rgmii(port))
164862306a36Sopenharmony_ci			goto invalid_conf;
164962306a36Sopenharmony_ci		mvpp22_gop_init_rgmii(port);
165062306a36Sopenharmony_ci		break;
165162306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_SGMII:
165262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_1000BASEX:
165362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_2500BASEX:
165462306a36Sopenharmony_ci		mvpp22_gop_init_sgmii(port);
165562306a36Sopenharmony_ci		break;
165662306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_5GBASER:
165762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_10GBASER:
165862306a36Sopenharmony_ci		if (!mvpp2_port_supports_xlg(port))
165962306a36Sopenharmony_ci			goto invalid_conf;
166062306a36Sopenharmony_ci		mvpp22_gop_init_10gkr(port);
166162306a36Sopenharmony_ci		break;
166262306a36Sopenharmony_ci	default:
166362306a36Sopenharmony_ci		goto unsupported_conf;
166462306a36Sopenharmony_ci	}
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL1, &val);
166762306a36Sopenharmony_ci	val |= GENCONF_PORT_CTRL1_RESET(port->gop_id) |
166862306a36Sopenharmony_ci	       GENCONF_PORT_CTRL1_EN(port->gop_id);
166962306a36Sopenharmony_ci	regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL1, val);
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val);
167262306a36Sopenharmony_ci	val |= GENCONF_PORT_CTRL0_CLK_DIV_PHASE_CLR;
167362306a36Sopenharmony_ci	regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	regmap_read(priv->sysctrl_base, GENCONF_SOFT_RESET1, &val);
167662306a36Sopenharmony_ci	val |= GENCONF_SOFT_RESET1_GOP;
167762306a36Sopenharmony_ci	regmap_write(priv->sysctrl_base, GENCONF_SOFT_RESET1, val);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	mvpp22_gop_fca_set_periodic_timer(port);
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ciunsupported_conf:
168262306a36Sopenharmony_ci	return 0;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ciinvalid_conf:
168562306a36Sopenharmony_ci	netdev_err(port->dev, "Invalid port configuration\n");
168662306a36Sopenharmony_ci	return -EINVAL;
168762306a36Sopenharmony_ci}
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_cistatic void mvpp22_gop_unmask_irq(struct mvpp2_port *port)
169062306a36Sopenharmony_ci{
169162306a36Sopenharmony_ci	u32 val;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	if (phy_interface_mode_is_rgmii(port->phy_interface) ||
169462306a36Sopenharmony_ci	    phy_interface_mode_is_8023z(port->phy_interface) ||
169562306a36Sopenharmony_ci	    port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
169662306a36Sopenharmony_ci		/* Enable the GMAC link status irq for this port */
169762306a36Sopenharmony_ci		val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK);
169862306a36Sopenharmony_ci		val |= MVPP22_GMAC_INT_SUM_MASK_LINK_STAT;
169962306a36Sopenharmony_ci		writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK);
170062306a36Sopenharmony_ci	}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	if (mvpp2_port_supports_xlg(port)) {
170362306a36Sopenharmony_ci		/* Enable the XLG/GIG irqs for this port */
170462306a36Sopenharmony_ci		val = readl(port->base + MVPP22_XLG_EXT_INT_MASK);
170562306a36Sopenharmony_ci		if (mvpp2_is_xlg(port->phy_interface))
170662306a36Sopenharmony_ci			val |= MVPP22_XLG_EXT_INT_MASK_XLG;
170762306a36Sopenharmony_ci		else
170862306a36Sopenharmony_ci			val |= MVPP22_XLG_EXT_INT_MASK_GIG;
170962306a36Sopenharmony_ci		writel(val, port->base + MVPP22_XLG_EXT_INT_MASK);
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_cistatic void mvpp22_gop_mask_irq(struct mvpp2_port *port)
171462306a36Sopenharmony_ci{
171562306a36Sopenharmony_ci	u32 val;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	if (mvpp2_port_supports_xlg(port)) {
171862306a36Sopenharmony_ci		val = readl(port->base + MVPP22_XLG_EXT_INT_MASK);
171962306a36Sopenharmony_ci		val &= ~(MVPP22_XLG_EXT_INT_MASK_XLG |
172062306a36Sopenharmony_ci			 MVPP22_XLG_EXT_INT_MASK_GIG);
172162306a36Sopenharmony_ci		writel(val, port->base + MVPP22_XLG_EXT_INT_MASK);
172262306a36Sopenharmony_ci	}
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	if (phy_interface_mode_is_rgmii(port->phy_interface) ||
172562306a36Sopenharmony_ci	    phy_interface_mode_is_8023z(port->phy_interface) ||
172662306a36Sopenharmony_ci	    port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
172762306a36Sopenharmony_ci		val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK);
172862306a36Sopenharmony_ci		val &= ~MVPP22_GMAC_INT_SUM_MASK_LINK_STAT;
172962306a36Sopenharmony_ci		writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK);
173062306a36Sopenharmony_ci	}
173162306a36Sopenharmony_ci}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_cistatic void mvpp22_gop_setup_irq(struct mvpp2_port *port)
173462306a36Sopenharmony_ci{
173562306a36Sopenharmony_ci	u32 val;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	mvpp2_modify(port->base + MVPP22_GMAC_INT_SUM_MASK,
173862306a36Sopenharmony_ci		     MVPP22_GMAC_INT_SUM_MASK_PTP,
173962306a36Sopenharmony_ci		     MVPP22_GMAC_INT_SUM_MASK_PTP);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	if (port->phylink ||
174262306a36Sopenharmony_ci	    phy_interface_mode_is_rgmii(port->phy_interface) ||
174362306a36Sopenharmony_ci	    phy_interface_mode_is_8023z(port->phy_interface) ||
174462306a36Sopenharmony_ci	    port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
174562306a36Sopenharmony_ci		val = readl(port->base + MVPP22_GMAC_INT_MASK);
174662306a36Sopenharmony_ci		val |= MVPP22_GMAC_INT_MASK_LINK_STAT;
174762306a36Sopenharmony_ci		writel(val, port->base + MVPP22_GMAC_INT_MASK);
174862306a36Sopenharmony_ci	}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	if (mvpp2_port_supports_xlg(port)) {
175162306a36Sopenharmony_ci		val = readl(port->base + MVPP22_XLG_INT_MASK);
175262306a36Sopenharmony_ci		val |= MVPP22_XLG_INT_MASK_LINK;
175362306a36Sopenharmony_ci		writel(val, port->base + MVPP22_XLG_INT_MASK);
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci		mvpp2_modify(port->base + MVPP22_XLG_EXT_INT_MASK,
175662306a36Sopenharmony_ci			     MVPP22_XLG_EXT_INT_MASK_PTP,
175762306a36Sopenharmony_ci			     MVPP22_XLG_EXT_INT_MASK_PTP);
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	mvpp22_gop_unmask_irq(port);
176162306a36Sopenharmony_ci}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci/* Sets the PHY mode of the COMPHY (which configures the serdes lanes).
176462306a36Sopenharmony_ci *
176562306a36Sopenharmony_ci * The PHY mode used by the PPv2 driver comes from the network subsystem, while
176662306a36Sopenharmony_ci * the one given to the COMPHY comes from the generic PHY subsystem. Hence they
176762306a36Sopenharmony_ci * differ.
176862306a36Sopenharmony_ci *
176962306a36Sopenharmony_ci * The COMPHY configures the serdes lanes regardless of the actual use of the
177062306a36Sopenharmony_ci * lanes by the physical layer. This is why configurations like
177162306a36Sopenharmony_ci * "PPv2 (2500BaseX) - COMPHY (2500SGMII)" are valid.
177262306a36Sopenharmony_ci */
177362306a36Sopenharmony_cistatic int mvpp22_comphy_init(struct mvpp2_port *port,
177462306a36Sopenharmony_ci			      phy_interface_t interface)
177562306a36Sopenharmony_ci{
177662306a36Sopenharmony_ci	int ret;
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	if (!port->comphy)
177962306a36Sopenharmony_ci		return 0;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	ret = phy_set_mode_ext(port->comphy, PHY_MODE_ETHERNET, interface);
178262306a36Sopenharmony_ci	if (ret)
178362306a36Sopenharmony_ci		return ret;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	return phy_power_on(port->comphy);
178662306a36Sopenharmony_ci}
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_cistatic void mvpp2_port_enable(struct mvpp2_port *port)
178962306a36Sopenharmony_ci{
179062306a36Sopenharmony_ci	u32 val;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	if (mvpp2_port_supports_xlg(port) &&
179362306a36Sopenharmony_ci	    mvpp2_is_xlg(port->phy_interface)) {
179462306a36Sopenharmony_ci		val = readl(port->base + MVPP22_XLG_CTRL0_REG);
179562306a36Sopenharmony_ci		val |= MVPP22_XLG_CTRL0_PORT_EN;
179662306a36Sopenharmony_ci		val &= ~MVPP22_XLG_CTRL0_MIB_CNT_DIS;
179762306a36Sopenharmony_ci		writel(val, port->base + MVPP22_XLG_CTRL0_REG);
179862306a36Sopenharmony_ci	} else {
179962306a36Sopenharmony_ci		val = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
180062306a36Sopenharmony_ci		val |= MVPP2_GMAC_PORT_EN_MASK;
180162306a36Sopenharmony_ci		val |= MVPP2_GMAC_MIB_CNTR_EN_MASK;
180262306a36Sopenharmony_ci		writel(val, port->base + MVPP2_GMAC_CTRL_0_REG);
180362306a36Sopenharmony_ci	}
180462306a36Sopenharmony_ci}
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_cistatic void mvpp2_port_disable(struct mvpp2_port *port)
180762306a36Sopenharmony_ci{
180862306a36Sopenharmony_ci	u32 val;
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	if (mvpp2_port_supports_xlg(port) &&
181162306a36Sopenharmony_ci	    mvpp2_is_xlg(port->phy_interface)) {
181262306a36Sopenharmony_ci		val = readl(port->base + MVPP22_XLG_CTRL0_REG);
181362306a36Sopenharmony_ci		val &= ~MVPP22_XLG_CTRL0_PORT_EN;
181462306a36Sopenharmony_ci		writel(val, port->base + MVPP22_XLG_CTRL0_REG);
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	val = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
181862306a36Sopenharmony_ci	val &= ~(MVPP2_GMAC_PORT_EN_MASK);
181962306a36Sopenharmony_ci	writel(val, port->base + MVPP2_GMAC_CTRL_0_REG);
182062306a36Sopenharmony_ci}
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci/* Set IEEE 802.3x Flow Control Xon Packet Transmission Mode */
182362306a36Sopenharmony_cistatic void mvpp2_port_periodic_xon_disable(struct mvpp2_port *port)
182462306a36Sopenharmony_ci{
182562306a36Sopenharmony_ci	u32 val;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	val = readl(port->base + MVPP2_GMAC_CTRL_1_REG) &
182862306a36Sopenharmony_ci		    ~MVPP2_GMAC_PERIODIC_XON_EN_MASK;
182962306a36Sopenharmony_ci	writel(val, port->base + MVPP2_GMAC_CTRL_1_REG);
183062306a36Sopenharmony_ci}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci/* Configure loopback port */
183362306a36Sopenharmony_cistatic void mvpp2_port_loopback_set(struct mvpp2_port *port,
183462306a36Sopenharmony_ci				    const struct phylink_link_state *state)
183562306a36Sopenharmony_ci{
183662306a36Sopenharmony_ci	u32 val;
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	val = readl(port->base + MVPP2_GMAC_CTRL_1_REG);
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	if (state->speed == 1000)
184162306a36Sopenharmony_ci		val |= MVPP2_GMAC_GMII_LB_EN_MASK;
184262306a36Sopenharmony_ci	else
184362306a36Sopenharmony_ci		val &= ~MVPP2_GMAC_GMII_LB_EN_MASK;
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	if (phy_interface_mode_is_8023z(state->interface) ||
184662306a36Sopenharmony_ci	    state->interface == PHY_INTERFACE_MODE_SGMII)
184762306a36Sopenharmony_ci		val |= MVPP2_GMAC_PCS_LB_EN_MASK;
184862306a36Sopenharmony_ci	else
184962306a36Sopenharmony_ci		val &= ~MVPP2_GMAC_PCS_LB_EN_MASK;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	writel(val, port->base + MVPP2_GMAC_CTRL_1_REG);
185262306a36Sopenharmony_ci}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_cienum {
185562306a36Sopenharmony_ci	ETHTOOL_XDP_REDIRECT,
185662306a36Sopenharmony_ci	ETHTOOL_XDP_PASS,
185762306a36Sopenharmony_ci	ETHTOOL_XDP_DROP,
185862306a36Sopenharmony_ci	ETHTOOL_XDP_TX,
185962306a36Sopenharmony_ci	ETHTOOL_XDP_TX_ERR,
186062306a36Sopenharmony_ci	ETHTOOL_XDP_XMIT,
186162306a36Sopenharmony_ci	ETHTOOL_XDP_XMIT_ERR,
186262306a36Sopenharmony_ci};
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_cistruct mvpp2_ethtool_counter {
186562306a36Sopenharmony_ci	unsigned int offset;
186662306a36Sopenharmony_ci	const char string[ETH_GSTRING_LEN];
186762306a36Sopenharmony_ci	bool reg_is_64b;
186862306a36Sopenharmony_ci};
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_cistatic u64 mvpp2_read_count(struct mvpp2_port *port,
187162306a36Sopenharmony_ci			    const struct mvpp2_ethtool_counter *counter)
187262306a36Sopenharmony_ci{
187362306a36Sopenharmony_ci	u64 val;
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	val = readl(port->stats_base + counter->offset);
187662306a36Sopenharmony_ci	if (counter->reg_is_64b)
187762306a36Sopenharmony_ci		val += (u64)readl(port->stats_base + counter->offset + 4) << 32;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	return val;
188062306a36Sopenharmony_ci}
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci/* Some counters are accessed indirectly by first writing an index to
188362306a36Sopenharmony_ci * MVPP2_CTRS_IDX. The index can represent various resources depending on the
188462306a36Sopenharmony_ci * register we access, it can be a hit counter for some classification tables,
188562306a36Sopenharmony_ci * a counter specific to a rxq, a txq or a buffer pool.
188662306a36Sopenharmony_ci */
188762306a36Sopenharmony_cistatic u32 mvpp2_read_index(struct mvpp2 *priv, u32 index, u32 reg)
188862306a36Sopenharmony_ci{
188962306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_CTRS_IDX, index);
189062306a36Sopenharmony_ci	return mvpp2_read(priv, reg);
189162306a36Sopenharmony_ci}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci/* Due to the fact that software statistics and hardware statistics are, by
189462306a36Sopenharmony_ci * design, incremented at different moments in the chain of packet processing,
189562306a36Sopenharmony_ci * it is very likely that incoming packets could have been dropped after being
189662306a36Sopenharmony_ci * counted by hardware but before reaching software statistics (most probably
189762306a36Sopenharmony_ci * multicast packets), and in the opposite way, during transmission, FCS bytes
189862306a36Sopenharmony_ci * are added in between as well as TSO skb will be split and header bytes added.
189962306a36Sopenharmony_ci * Hence, statistics gathered from userspace with ifconfig (software) and
190062306a36Sopenharmony_ci * ethtool (hardware) cannot be compared.
190162306a36Sopenharmony_ci */
190262306a36Sopenharmony_cistatic const struct mvpp2_ethtool_counter mvpp2_ethtool_mib_regs[] = {
190362306a36Sopenharmony_ci	{ MVPP2_MIB_GOOD_OCTETS_RCVD, "good_octets_received", true },
190462306a36Sopenharmony_ci	{ MVPP2_MIB_BAD_OCTETS_RCVD, "bad_octets_received" },
190562306a36Sopenharmony_ci	{ MVPP2_MIB_CRC_ERRORS_SENT, "crc_errors_sent" },
190662306a36Sopenharmony_ci	{ MVPP2_MIB_UNICAST_FRAMES_RCVD, "unicast_frames_received" },
190762306a36Sopenharmony_ci	{ MVPP2_MIB_BROADCAST_FRAMES_RCVD, "broadcast_frames_received" },
190862306a36Sopenharmony_ci	{ MVPP2_MIB_MULTICAST_FRAMES_RCVD, "multicast_frames_received" },
190962306a36Sopenharmony_ci	{ MVPP2_MIB_FRAMES_64_OCTETS, "frames_64_octets" },
191062306a36Sopenharmony_ci	{ MVPP2_MIB_FRAMES_65_TO_127_OCTETS, "frames_65_to_127_octet" },
191162306a36Sopenharmony_ci	{ MVPP2_MIB_FRAMES_128_TO_255_OCTETS, "frames_128_to_255_octet" },
191262306a36Sopenharmony_ci	{ MVPP2_MIB_FRAMES_256_TO_511_OCTETS, "frames_256_to_511_octet" },
191362306a36Sopenharmony_ci	{ MVPP2_MIB_FRAMES_512_TO_1023_OCTETS, "frames_512_to_1023_octet" },
191462306a36Sopenharmony_ci	{ MVPP2_MIB_FRAMES_1024_TO_MAX_OCTETS, "frames_1024_to_max_octet" },
191562306a36Sopenharmony_ci	{ MVPP2_MIB_GOOD_OCTETS_SENT, "good_octets_sent", true },
191662306a36Sopenharmony_ci	{ MVPP2_MIB_UNICAST_FRAMES_SENT, "unicast_frames_sent" },
191762306a36Sopenharmony_ci	{ MVPP2_MIB_MULTICAST_FRAMES_SENT, "multicast_frames_sent" },
191862306a36Sopenharmony_ci	{ MVPP2_MIB_BROADCAST_FRAMES_SENT, "broadcast_frames_sent" },
191962306a36Sopenharmony_ci	{ MVPP2_MIB_FC_SENT, "fc_sent" },
192062306a36Sopenharmony_ci	{ MVPP2_MIB_FC_RCVD, "fc_received" },
192162306a36Sopenharmony_ci	{ MVPP2_MIB_RX_FIFO_OVERRUN, "rx_fifo_overrun" },
192262306a36Sopenharmony_ci	{ MVPP2_MIB_UNDERSIZE_RCVD, "undersize_received" },
192362306a36Sopenharmony_ci	{ MVPP2_MIB_FRAGMENTS_RCVD, "fragments_received" },
192462306a36Sopenharmony_ci	{ MVPP2_MIB_OVERSIZE_RCVD, "oversize_received" },
192562306a36Sopenharmony_ci	{ MVPP2_MIB_JABBER_RCVD, "jabber_received" },
192662306a36Sopenharmony_ci	{ MVPP2_MIB_MAC_RCV_ERROR, "mac_receive_error" },
192762306a36Sopenharmony_ci	{ MVPP2_MIB_BAD_CRC_EVENT, "bad_crc_event" },
192862306a36Sopenharmony_ci	{ MVPP2_MIB_COLLISION, "collision" },
192962306a36Sopenharmony_ci	{ MVPP2_MIB_LATE_COLLISION, "late_collision" },
193062306a36Sopenharmony_ci};
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_cistatic const struct mvpp2_ethtool_counter mvpp2_ethtool_port_regs[] = {
193362306a36Sopenharmony_ci	{ MVPP2_OVERRUN_ETH_DROP, "rx_fifo_or_parser_overrun_drops" },
193462306a36Sopenharmony_ci	{ MVPP2_CLS_ETH_DROP, "rx_classifier_drops" },
193562306a36Sopenharmony_ci};
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_cistatic const struct mvpp2_ethtool_counter mvpp2_ethtool_txq_regs[] = {
193862306a36Sopenharmony_ci	{ MVPP2_TX_DESC_ENQ_CTR, "txq_%d_desc_enqueue" },
193962306a36Sopenharmony_ci	{ MVPP2_TX_DESC_ENQ_TO_DDR_CTR, "txq_%d_desc_enqueue_to_ddr" },
194062306a36Sopenharmony_ci	{ MVPP2_TX_BUFF_ENQ_TO_DDR_CTR, "txq_%d_buff_euqueue_to_ddr" },
194162306a36Sopenharmony_ci	{ MVPP2_TX_DESC_ENQ_HW_FWD_CTR, "txq_%d_desc_hardware_forwarded" },
194262306a36Sopenharmony_ci	{ MVPP2_TX_PKTS_DEQ_CTR, "txq_%d_packets_dequeued" },
194362306a36Sopenharmony_ci	{ MVPP2_TX_PKTS_FULL_QUEUE_DROP_CTR, "txq_%d_queue_full_drops" },
194462306a36Sopenharmony_ci	{ MVPP2_TX_PKTS_EARLY_DROP_CTR, "txq_%d_packets_early_drops" },
194562306a36Sopenharmony_ci	{ MVPP2_TX_PKTS_BM_DROP_CTR, "txq_%d_packets_bm_drops" },
194662306a36Sopenharmony_ci	{ MVPP2_TX_PKTS_BM_MC_DROP_CTR, "txq_%d_packets_rep_bm_drops" },
194762306a36Sopenharmony_ci};
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_cistatic const struct mvpp2_ethtool_counter mvpp2_ethtool_rxq_regs[] = {
195062306a36Sopenharmony_ci	{ MVPP2_RX_DESC_ENQ_CTR, "rxq_%d_desc_enqueue" },
195162306a36Sopenharmony_ci	{ MVPP2_RX_PKTS_FULL_QUEUE_DROP_CTR, "rxq_%d_queue_full_drops" },
195262306a36Sopenharmony_ci	{ MVPP2_RX_PKTS_EARLY_DROP_CTR, "rxq_%d_packets_early_drops" },
195362306a36Sopenharmony_ci	{ MVPP2_RX_PKTS_BM_DROP_CTR, "rxq_%d_packets_bm_drops" },
195462306a36Sopenharmony_ci};
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_cistatic const struct mvpp2_ethtool_counter mvpp2_ethtool_xdp[] = {
195762306a36Sopenharmony_ci	{ ETHTOOL_XDP_REDIRECT, "rx_xdp_redirect", },
195862306a36Sopenharmony_ci	{ ETHTOOL_XDP_PASS, "rx_xdp_pass", },
195962306a36Sopenharmony_ci	{ ETHTOOL_XDP_DROP, "rx_xdp_drop", },
196062306a36Sopenharmony_ci	{ ETHTOOL_XDP_TX, "rx_xdp_tx", },
196162306a36Sopenharmony_ci	{ ETHTOOL_XDP_TX_ERR, "rx_xdp_tx_errors", },
196262306a36Sopenharmony_ci	{ ETHTOOL_XDP_XMIT, "tx_xdp_xmit", },
196362306a36Sopenharmony_ci	{ ETHTOOL_XDP_XMIT_ERR, "tx_xdp_xmit_errors", },
196462306a36Sopenharmony_ci};
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci#define MVPP2_N_ETHTOOL_STATS(ntxqs, nrxqs)	(ARRAY_SIZE(mvpp2_ethtool_mib_regs) + \
196762306a36Sopenharmony_ci						 ARRAY_SIZE(mvpp2_ethtool_port_regs) + \
196862306a36Sopenharmony_ci						 (ARRAY_SIZE(mvpp2_ethtool_txq_regs) * (ntxqs)) + \
196962306a36Sopenharmony_ci						 (ARRAY_SIZE(mvpp2_ethtool_rxq_regs) * (nrxqs)) + \
197062306a36Sopenharmony_ci						 ARRAY_SIZE(mvpp2_ethtool_xdp))
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_cistatic void mvpp2_ethtool_get_strings(struct net_device *netdev, u32 sset,
197362306a36Sopenharmony_ci				      u8 *data)
197462306a36Sopenharmony_ci{
197562306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(netdev);
197662306a36Sopenharmony_ci	int i, q;
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	if (sset != ETH_SS_STATS)
197962306a36Sopenharmony_ci		return;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_mib_regs); i++) {
198262306a36Sopenharmony_ci		strscpy(data, mvpp2_ethtool_mib_regs[i].string,
198362306a36Sopenharmony_ci			ETH_GSTRING_LEN);
198462306a36Sopenharmony_ci		data += ETH_GSTRING_LEN;
198562306a36Sopenharmony_ci	}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_port_regs); i++) {
198862306a36Sopenharmony_ci		strscpy(data, mvpp2_ethtool_port_regs[i].string,
198962306a36Sopenharmony_ci			ETH_GSTRING_LEN);
199062306a36Sopenharmony_ci		data += ETH_GSTRING_LEN;
199162306a36Sopenharmony_ci	}
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	for (q = 0; q < port->ntxqs; q++) {
199462306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_txq_regs); i++) {
199562306a36Sopenharmony_ci			snprintf(data, ETH_GSTRING_LEN,
199662306a36Sopenharmony_ci				 mvpp2_ethtool_txq_regs[i].string, q);
199762306a36Sopenharmony_ci			data += ETH_GSTRING_LEN;
199862306a36Sopenharmony_ci		}
199962306a36Sopenharmony_ci	}
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci	for (q = 0; q < port->nrxqs; q++) {
200262306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_rxq_regs); i++) {
200362306a36Sopenharmony_ci			snprintf(data, ETH_GSTRING_LEN,
200462306a36Sopenharmony_ci				 mvpp2_ethtool_rxq_regs[i].string,
200562306a36Sopenharmony_ci				 q);
200662306a36Sopenharmony_ci			data += ETH_GSTRING_LEN;
200762306a36Sopenharmony_ci		}
200862306a36Sopenharmony_ci	}
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_xdp); i++) {
201162306a36Sopenharmony_ci		strscpy(data, mvpp2_ethtool_xdp[i].string,
201262306a36Sopenharmony_ci			ETH_GSTRING_LEN);
201362306a36Sopenharmony_ci		data += ETH_GSTRING_LEN;
201462306a36Sopenharmony_ci	}
201562306a36Sopenharmony_ci}
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_cistatic void
201862306a36Sopenharmony_cimvpp2_get_xdp_stats(struct mvpp2_port *port, struct mvpp2_pcpu_stats *xdp_stats)
201962306a36Sopenharmony_ci{
202062306a36Sopenharmony_ci	unsigned int start;
202162306a36Sopenharmony_ci	unsigned int cpu;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	/* Gather XDP Statistics */
202462306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
202562306a36Sopenharmony_ci		struct mvpp2_pcpu_stats *cpu_stats;
202662306a36Sopenharmony_ci		u64	xdp_redirect;
202762306a36Sopenharmony_ci		u64	xdp_pass;
202862306a36Sopenharmony_ci		u64	xdp_drop;
202962306a36Sopenharmony_ci		u64	xdp_xmit;
203062306a36Sopenharmony_ci		u64	xdp_xmit_err;
203162306a36Sopenharmony_ci		u64	xdp_tx;
203262306a36Sopenharmony_ci		u64	xdp_tx_err;
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci		cpu_stats = per_cpu_ptr(port->stats, cpu);
203562306a36Sopenharmony_ci		do {
203662306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&cpu_stats->syncp);
203762306a36Sopenharmony_ci			xdp_redirect = cpu_stats->xdp_redirect;
203862306a36Sopenharmony_ci			xdp_pass   = cpu_stats->xdp_pass;
203962306a36Sopenharmony_ci			xdp_drop = cpu_stats->xdp_drop;
204062306a36Sopenharmony_ci			xdp_xmit   = cpu_stats->xdp_xmit;
204162306a36Sopenharmony_ci			xdp_xmit_err   = cpu_stats->xdp_xmit_err;
204262306a36Sopenharmony_ci			xdp_tx   = cpu_stats->xdp_tx;
204362306a36Sopenharmony_ci			xdp_tx_err   = cpu_stats->xdp_tx_err;
204462306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&cpu_stats->syncp, start));
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci		xdp_stats->xdp_redirect += xdp_redirect;
204762306a36Sopenharmony_ci		xdp_stats->xdp_pass   += xdp_pass;
204862306a36Sopenharmony_ci		xdp_stats->xdp_drop += xdp_drop;
204962306a36Sopenharmony_ci		xdp_stats->xdp_xmit   += xdp_xmit;
205062306a36Sopenharmony_ci		xdp_stats->xdp_xmit_err   += xdp_xmit_err;
205162306a36Sopenharmony_ci		xdp_stats->xdp_tx   += xdp_tx;
205262306a36Sopenharmony_ci		xdp_stats->xdp_tx_err   += xdp_tx_err;
205362306a36Sopenharmony_ci	}
205462306a36Sopenharmony_ci}
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_cistatic void mvpp2_read_stats(struct mvpp2_port *port)
205762306a36Sopenharmony_ci{
205862306a36Sopenharmony_ci	struct mvpp2_pcpu_stats xdp_stats = {};
205962306a36Sopenharmony_ci	const struct mvpp2_ethtool_counter *s;
206062306a36Sopenharmony_ci	u64 *pstats;
206162306a36Sopenharmony_ci	int i, q;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	pstats = port->ethtool_stats;
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_mib_regs); i++)
206662306a36Sopenharmony_ci		*pstats++ += mvpp2_read_count(port, &mvpp2_ethtool_mib_regs[i]);
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_port_regs); i++)
206962306a36Sopenharmony_ci		*pstats++ += mvpp2_read(port->priv,
207062306a36Sopenharmony_ci					mvpp2_ethtool_port_regs[i].offset +
207162306a36Sopenharmony_ci					4 * port->id);
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	for (q = 0; q < port->ntxqs; q++)
207462306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_txq_regs); i++)
207562306a36Sopenharmony_ci			*pstats++ += mvpp2_read_index(port->priv,
207662306a36Sopenharmony_ci						      MVPP22_CTRS_TX_CTR(port->id, q),
207762306a36Sopenharmony_ci						      mvpp2_ethtool_txq_regs[i].offset);
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	/* Rxqs are numbered from 0 from the user standpoint, but not from the
208062306a36Sopenharmony_ci	 * driver's. We need to add the  port->first_rxq offset.
208162306a36Sopenharmony_ci	 */
208262306a36Sopenharmony_ci	for (q = 0; q < port->nrxqs; q++)
208362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_rxq_regs); i++)
208462306a36Sopenharmony_ci			*pstats++ += mvpp2_read_index(port->priv,
208562306a36Sopenharmony_ci						      port->first_rxq + q,
208662306a36Sopenharmony_ci						      mvpp2_ethtool_rxq_regs[i].offset);
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	/* Gather XDP Statistics */
208962306a36Sopenharmony_ci	mvpp2_get_xdp_stats(port, &xdp_stats);
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	for (i = 0, s = mvpp2_ethtool_xdp;
209262306a36Sopenharmony_ci		 s < mvpp2_ethtool_xdp + ARRAY_SIZE(mvpp2_ethtool_xdp);
209362306a36Sopenharmony_ci	     s++, i++) {
209462306a36Sopenharmony_ci		switch (s->offset) {
209562306a36Sopenharmony_ci		case ETHTOOL_XDP_REDIRECT:
209662306a36Sopenharmony_ci			*pstats++ = xdp_stats.xdp_redirect;
209762306a36Sopenharmony_ci			break;
209862306a36Sopenharmony_ci		case ETHTOOL_XDP_PASS:
209962306a36Sopenharmony_ci			*pstats++ = xdp_stats.xdp_pass;
210062306a36Sopenharmony_ci			break;
210162306a36Sopenharmony_ci		case ETHTOOL_XDP_DROP:
210262306a36Sopenharmony_ci			*pstats++ = xdp_stats.xdp_drop;
210362306a36Sopenharmony_ci			break;
210462306a36Sopenharmony_ci		case ETHTOOL_XDP_TX:
210562306a36Sopenharmony_ci			*pstats++ = xdp_stats.xdp_tx;
210662306a36Sopenharmony_ci			break;
210762306a36Sopenharmony_ci		case ETHTOOL_XDP_TX_ERR:
210862306a36Sopenharmony_ci			*pstats++ = xdp_stats.xdp_tx_err;
210962306a36Sopenharmony_ci			break;
211062306a36Sopenharmony_ci		case ETHTOOL_XDP_XMIT:
211162306a36Sopenharmony_ci			*pstats++ = xdp_stats.xdp_xmit;
211262306a36Sopenharmony_ci			break;
211362306a36Sopenharmony_ci		case ETHTOOL_XDP_XMIT_ERR:
211462306a36Sopenharmony_ci			*pstats++ = xdp_stats.xdp_xmit_err;
211562306a36Sopenharmony_ci			break;
211662306a36Sopenharmony_ci		}
211762306a36Sopenharmony_ci	}
211862306a36Sopenharmony_ci}
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_cistatic void mvpp2_gather_hw_statistics(struct work_struct *work)
212162306a36Sopenharmony_ci{
212262306a36Sopenharmony_ci	struct delayed_work *del_work = to_delayed_work(work);
212362306a36Sopenharmony_ci	struct mvpp2_port *port = container_of(del_work, struct mvpp2_port,
212462306a36Sopenharmony_ci					       stats_work);
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	mutex_lock(&port->gather_stats_lock);
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	mvpp2_read_stats(port);
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	/* No need to read again the counters right after this function if it
213162306a36Sopenharmony_ci	 * was called asynchronously by the user (ie. use of ethtool).
213262306a36Sopenharmony_ci	 */
213362306a36Sopenharmony_ci	cancel_delayed_work(&port->stats_work);
213462306a36Sopenharmony_ci	queue_delayed_work(port->priv->stats_queue, &port->stats_work,
213562306a36Sopenharmony_ci			   MVPP2_MIB_COUNTERS_STATS_DELAY);
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	mutex_unlock(&port->gather_stats_lock);
213862306a36Sopenharmony_ci}
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_cistatic void mvpp2_ethtool_get_stats(struct net_device *dev,
214162306a36Sopenharmony_ci				    struct ethtool_stats *stats, u64 *data)
214262306a36Sopenharmony_ci{
214362306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	/* Update statistics for the given port, then take the lock to avoid
214662306a36Sopenharmony_ci	 * concurrent accesses on the ethtool_stats structure during its copy.
214762306a36Sopenharmony_ci	 */
214862306a36Sopenharmony_ci	mvpp2_gather_hw_statistics(&port->stats_work.work);
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	mutex_lock(&port->gather_stats_lock);
215162306a36Sopenharmony_ci	memcpy(data, port->ethtool_stats,
215262306a36Sopenharmony_ci	       sizeof(u64) * MVPP2_N_ETHTOOL_STATS(port->ntxqs, port->nrxqs));
215362306a36Sopenharmony_ci	mutex_unlock(&port->gather_stats_lock);
215462306a36Sopenharmony_ci}
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_cistatic int mvpp2_ethtool_get_sset_count(struct net_device *dev, int sset)
215762306a36Sopenharmony_ci{
215862306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	if (sset == ETH_SS_STATS)
216162306a36Sopenharmony_ci		return MVPP2_N_ETHTOOL_STATS(port->ntxqs, port->nrxqs);
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	return -EOPNOTSUPP;
216462306a36Sopenharmony_ci}
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_cistatic void mvpp2_mac_reset_assert(struct mvpp2_port *port)
216762306a36Sopenharmony_ci{
216862306a36Sopenharmony_ci	u32 val;
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	val = readl(port->base + MVPP2_GMAC_CTRL_2_REG) |
217162306a36Sopenharmony_ci	      MVPP2_GMAC_PORT_RESET_MASK;
217262306a36Sopenharmony_ci	writel(val, port->base + MVPP2_GMAC_CTRL_2_REG);
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	if (port->priv->hw_version >= MVPP22 && port->gop_id == 0) {
217562306a36Sopenharmony_ci		val = readl(port->base + MVPP22_XLG_CTRL0_REG) &
217662306a36Sopenharmony_ci		      ~MVPP22_XLG_CTRL0_MAC_RESET_DIS;
217762306a36Sopenharmony_ci		writel(val, port->base + MVPP22_XLG_CTRL0_REG);
217862306a36Sopenharmony_ci	}
217962306a36Sopenharmony_ci}
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_cistatic void mvpp22_pcs_reset_assert(struct mvpp2_port *port)
218262306a36Sopenharmony_ci{
218362306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
218462306a36Sopenharmony_ci	void __iomem *mpcs, *xpcs;
218562306a36Sopenharmony_ci	u32 val;
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21 || port->gop_id != 0)
218862306a36Sopenharmony_ci		return;
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	mpcs = priv->iface_base + MVPP22_MPCS_BASE(port->gop_id);
219162306a36Sopenharmony_ci	xpcs = priv->iface_base + MVPP22_XPCS_BASE(port->gop_id);
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	val = readl(mpcs + MVPP22_MPCS_CLK_RESET);
219462306a36Sopenharmony_ci	val &= ~(MAC_CLK_RESET_MAC | MAC_CLK_RESET_SD_RX | MAC_CLK_RESET_SD_TX);
219562306a36Sopenharmony_ci	val |= MVPP22_MPCS_CLK_RESET_DIV_SET;
219662306a36Sopenharmony_ci	writel(val, mpcs + MVPP22_MPCS_CLK_RESET);
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	val = readl(xpcs + MVPP22_XPCS_CFG0);
219962306a36Sopenharmony_ci	writel(val & ~MVPP22_XPCS_CFG0_RESET_DIS, xpcs + MVPP22_XPCS_CFG0);
220062306a36Sopenharmony_ci}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_cistatic void mvpp22_pcs_reset_deassert(struct mvpp2_port *port,
220362306a36Sopenharmony_ci				      phy_interface_t interface)
220462306a36Sopenharmony_ci{
220562306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
220662306a36Sopenharmony_ci	void __iomem *mpcs, *xpcs;
220762306a36Sopenharmony_ci	u32 val;
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21 || port->gop_id != 0)
221062306a36Sopenharmony_ci		return;
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	mpcs = priv->iface_base + MVPP22_MPCS_BASE(port->gop_id);
221362306a36Sopenharmony_ci	xpcs = priv->iface_base + MVPP22_XPCS_BASE(port->gop_id);
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	switch (interface) {
221662306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_5GBASER:
221762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_10GBASER:
221862306a36Sopenharmony_ci		val = readl(mpcs + MVPP22_MPCS_CLK_RESET);
221962306a36Sopenharmony_ci		val |= MAC_CLK_RESET_MAC | MAC_CLK_RESET_SD_RX |
222062306a36Sopenharmony_ci		       MAC_CLK_RESET_SD_TX;
222162306a36Sopenharmony_ci		val &= ~MVPP22_MPCS_CLK_RESET_DIV_SET;
222262306a36Sopenharmony_ci		writel(val, mpcs + MVPP22_MPCS_CLK_RESET);
222362306a36Sopenharmony_ci		break;
222462306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_XAUI:
222562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RXAUI:
222662306a36Sopenharmony_ci		val = readl(xpcs + MVPP22_XPCS_CFG0);
222762306a36Sopenharmony_ci		writel(val | MVPP22_XPCS_CFG0_RESET_DIS, xpcs + MVPP22_XPCS_CFG0);
222862306a36Sopenharmony_ci		break;
222962306a36Sopenharmony_ci	default:
223062306a36Sopenharmony_ci		break;
223162306a36Sopenharmony_ci	}
223262306a36Sopenharmony_ci}
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci/* Change maximum receive size of the port */
223562306a36Sopenharmony_cistatic inline void mvpp2_gmac_max_rx_size_set(struct mvpp2_port *port)
223662306a36Sopenharmony_ci{
223762306a36Sopenharmony_ci	u32 val;
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	val = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
224062306a36Sopenharmony_ci	val &= ~MVPP2_GMAC_MAX_RX_SIZE_MASK;
224162306a36Sopenharmony_ci	val |= (((port->pkt_size - MVPP2_MH_SIZE) / 2) <<
224262306a36Sopenharmony_ci		    MVPP2_GMAC_MAX_RX_SIZE_OFFS);
224362306a36Sopenharmony_ci	writel(val, port->base + MVPP2_GMAC_CTRL_0_REG);
224462306a36Sopenharmony_ci}
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci/* Change maximum receive size of the port */
224762306a36Sopenharmony_cistatic inline void mvpp2_xlg_max_rx_size_set(struct mvpp2_port *port)
224862306a36Sopenharmony_ci{
224962306a36Sopenharmony_ci	u32 val;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	val =  readl(port->base + MVPP22_XLG_CTRL1_REG);
225262306a36Sopenharmony_ci	val &= ~MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK;
225362306a36Sopenharmony_ci	val |= ((port->pkt_size - MVPP2_MH_SIZE) / 2) <<
225462306a36Sopenharmony_ci	       MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS;
225562306a36Sopenharmony_ci	writel(val, port->base + MVPP22_XLG_CTRL1_REG);
225662306a36Sopenharmony_ci}
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci/* Set defaults to the MVPP2 port */
225962306a36Sopenharmony_cistatic void mvpp2_defaults_set(struct mvpp2_port *port)
226062306a36Sopenharmony_ci{
226162306a36Sopenharmony_ci	int tx_port_num, val, queue, lrxq;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21) {
226462306a36Sopenharmony_ci		/* Update TX FIFO MIN Threshold */
226562306a36Sopenharmony_ci		val = readl(port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG);
226662306a36Sopenharmony_ci		val &= ~MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK;
226762306a36Sopenharmony_ci		/* Min. TX threshold must be less than minimal packet length */
226862306a36Sopenharmony_ci		val |= MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(64 - 4 - 2);
226962306a36Sopenharmony_ci		writel(val, port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG);
227062306a36Sopenharmony_ci	}
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	/* Disable Legacy WRR, Disable EJP, Release from reset */
227362306a36Sopenharmony_ci	tx_port_num = mvpp2_egress_port(port);
227462306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG,
227562306a36Sopenharmony_ci		    tx_port_num);
227662306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_CMD_1_REG, 0);
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	/* Set TXQ scheduling to Round-Robin */
227962306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_FIXED_PRIO_REG, 0);
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	/* Close bandwidth for all queues */
228262306a36Sopenharmony_ci	for (queue = 0; queue < MVPP2_MAX_TXQ; queue++)
228362306a36Sopenharmony_ci		mvpp2_write(port->priv,
228462306a36Sopenharmony_ci			    MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(queue), 0);
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	/* Set refill period to 1 usec, refill tokens
228762306a36Sopenharmony_ci	 * and bucket size to maximum
228862306a36Sopenharmony_ci	 */
228962306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_PERIOD_REG,
229062306a36Sopenharmony_ci		    port->priv->tclk / USEC_PER_SEC);
229162306a36Sopenharmony_ci	val = mvpp2_read(port->priv, MVPP2_TXP_SCHED_REFILL_REG);
229262306a36Sopenharmony_ci	val &= ~MVPP2_TXP_REFILL_PERIOD_ALL_MASK;
229362306a36Sopenharmony_ci	val |= MVPP2_TXP_REFILL_PERIOD_MASK(1);
229462306a36Sopenharmony_ci	val |= MVPP2_TXP_REFILL_TOKENS_ALL_MASK;
229562306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_REFILL_REG, val);
229662306a36Sopenharmony_ci	val = MVPP2_TXP_TOKEN_SIZE_MAX;
229762306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val);
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	/* Set MaximumLowLatencyPacketSize value to 256 */
230062306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_RX_CTRL_REG(port->id),
230162306a36Sopenharmony_ci		    MVPP2_RX_USE_PSEUDO_FOR_CSUM_MASK |
230262306a36Sopenharmony_ci		    MVPP2_RX_LOW_LATENCY_PKT_SIZE(256));
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	/* Enable Rx cache snoop */
230562306a36Sopenharmony_ci	for (lrxq = 0; lrxq < port->nrxqs; lrxq++) {
230662306a36Sopenharmony_ci		queue = port->rxqs[lrxq]->id;
230762306a36Sopenharmony_ci		val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue));
230862306a36Sopenharmony_ci		val |= MVPP2_SNOOP_PKT_SIZE_MASK |
230962306a36Sopenharmony_ci			   MVPP2_SNOOP_BUF_HDR_MASK;
231062306a36Sopenharmony_ci		mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(queue), val);
231162306a36Sopenharmony_ci	}
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	/* At default, mask all interrupts to all present cpus */
231462306a36Sopenharmony_ci	mvpp2_interrupts_disable(port);
231562306a36Sopenharmony_ci}
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci/* Enable/disable receiving packets */
231862306a36Sopenharmony_cistatic void mvpp2_ingress_enable(struct mvpp2_port *port)
231962306a36Sopenharmony_ci{
232062306a36Sopenharmony_ci	u32 val;
232162306a36Sopenharmony_ci	int lrxq, queue;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	for (lrxq = 0; lrxq < port->nrxqs; lrxq++) {
232462306a36Sopenharmony_ci		queue = port->rxqs[lrxq]->id;
232562306a36Sopenharmony_ci		val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue));
232662306a36Sopenharmony_ci		val &= ~MVPP2_RXQ_DISABLE_MASK;
232762306a36Sopenharmony_ci		mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(queue), val);
232862306a36Sopenharmony_ci	}
232962306a36Sopenharmony_ci}
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_cistatic void mvpp2_ingress_disable(struct mvpp2_port *port)
233262306a36Sopenharmony_ci{
233362306a36Sopenharmony_ci	u32 val;
233462306a36Sopenharmony_ci	int lrxq, queue;
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci	for (lrxq = 0; lrxq < port->nrxqs; lrxq++) {
233762306a36Sopenharmony_ci		queue = port->rxqs[lrxq]->id;
233862306a36Sopenharmony_ci		val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue));
233962306a36Sopenharmony_ci		val |= MVPP2_RXQ_DISABLE_MASK;
234062306a36Sopenharmony_ci		mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(queue), val);
234162306a36Sopenharmony_ci	}
234262306a36Sopenharmony_ci}
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci/* Enable transmit via physical egress queue
234562306a36Sopenharmony_ci * - HW starts take descriptors from DRAM
234662306a36Sopenharmony_ci */
234762306a36Sopenharmony_cistatic void mvpp2_egress_enable(struct mvpp2_port *port)
234862306a36Sopenharmony_ci{
234962306a36Sopenharmony_ci	u32 qmap;
235062306a36Sopenharmony_ci	int queue;
235162306a36Sopenharmony_ci	int tx_port_num = mvpp2_egress_port(port);
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	/* Enable all initialized TXs. */
235462306a36Sopenharmony_ci	qmap = 0;
235562306a36Sopenharmony_ci	for (queue = 0; queue < port->ntxqs; queue++) {
235662306a36Sopenharmony_ci		struct mvpp2_tx_queue *txq = port->txqs[queue];
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci		if (txq->descs)
235962306a36Sopenharmony_ci			qmap |= (1 << queue);
236062306a36Sopenharmony_ci	}
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num);
236362306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG, qmap);
236462306a36Sopenharmony_ci}
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci/* Disable transmit via physical egress queue
236762306a36Sopenharmony_ci * - HW doesn't take descriptors from DRAM
236862306a36Sopenharmony_ci */
236962306a36Sopenharmony_cistatic void mvpp2_egress_disable(struct mvpp2_port *port)
237062306a36Sopenharmony_ci{
237162306a36Sopenharmony_ci	u32 reg_data;
237262306a36Sopenharmony_ci	int delay;
237362306a36Sopenharmony_ci	int tx_port_num = mvpp2_egress_port(port);
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci	/* Issue stop command for active channels only */
237662306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num);
237762306a36Sopenharmony_ci	reg_data = (mvpp2_read(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG)) &
237862306a36Sopenharmony_ci		    MVPP2_TXP_SCHED_ENQ_MASK;
237962306a36Sopenharmony_ci	if (reg_data != 0)
238062306a36Sopenharmony_ci		mvpp2_write(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG,
238162306a36Sopenharmony_ci			    (reg_data << MVPP2_TXP_SCHED_DISQ_OFFSET));
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	/* Wait for all Tx activity to terminate. */
238462306a36Sopenharmony_ci	delay = 0;
238562306a36Sopenharmony_ci	do {
238662306a36Sopenharmony_ci		if (delay >= MVPP2_TX_DISABLE_TIMEOUT_MSEC) {
238762306a36Sopenharmony_ci			netdev_warn(port->dev,
238862306a36Sopenharmony_ci				    "Tx stop timed out, status=0x%08x\n",
238962306a36Sopenharmony_ci				    reg_data);
239062306a36Sopenharmony_ci			break;
239162306a36Sopenharmony_ci		}
239262306a36Sopenharmony_ci		mdelay(1);
239362306a36Sopenharmony_ci		delay++;
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci		/* Check port TX Command register that all
239662306a36Sopenharmony_ci		 * Tx queues are stopped
239762306a36Sopenharmony_ci		 */
239862306a36Sopenharmony_ci		reg_data = mvpp2_read(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG);
239962306a36Sopenharmony_ci	} while (reg_data & MVPP2_TXP_SCHED_ENQ_MASK);
240062306a36Sopenharmony_ci}
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci/* Rx descriptors helper methods */
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci/* Get number of Rx descriptors occupied by received packets */
240562306a36Sopenharmony_cistatic inline int
240662306a36Sopenharmony_cimvpp2_rxq_received(struct mvpp2_port *port, int rxq_id)
240762306a36Sopenharmony_ci{
240862306a36Sopenharmony_ci	u32 val = mvpp2_read(port->priv, MVPP2_RXQ_STATUS_REG(rxq_id));
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	return val & MVPP2_RXQ_OCCUPIED_MASK;
241162306a36Sopenharmony_ci}
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci/* Update Rx queue status with the number of occupied and available
241462306a36Sopenharmony_ci * Rx descriptor slots.
241562306a36Sopenharmony_ci */
241662306a36Sopenharmony_cistatic inline void
241762306a36Sopenharmony_cimvpp2_rxq_status_update(struct mvpp2_port *port, int rxq_id,
241862306a36Sopenharmony_ci			int used_count, int free_count)
241962306a36Sopenharmony_ci{
242062306a36Sopenharmony_ci	/* Decrement the number of used descriptors and increment count
242162306a36Sopenharmony_ci	 * increment the number of free descriptors.
242262306a36Sopenharmony_ci	 */
242362306a36Sopenharmony_ci	u32 val = used_count | (free_count << MVPP2_RXQ_NUM_NEW_OFFSET);
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_RXQ_STATUS_UPDATE_REG(rxq_id), val);
242662306a36Sopenharmony_ci}
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci/* Get pointer to next RX descriptor to be processed by SW */
242962306a36Sopenharmony_cistatic inline struct mvpp2_rx_desc *
243062306a36Sopenharmony_cimvpp2_rxq_next_desc_get(struct mvpp2_rx_queue *rxq)
243162306a36Sopenharmony_ci{
243262306a36Sopenharmony_ci	int rx_desc = rxq->next_desc_to_proc;
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	rxq->next_desc_to_proc = MVPP2_QUEUE_NEXT_DESC(rxq, rx_desc);
243562306a36Sopenharmony_ci	prefetch(rxq->descs + rxq->next_desc_to_proc);
243662306a36Sopenharmony_ci	return rxq->descs + rx_desc;
243762306a36Sopenharmony_ci}
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci/* Set rx queue offset */
244062306a36Sopenharmony_cistatic void mvpp2_rxq_offset_set(struct mvpp2_port *port,
244162306a36Sopenharmony_ci				 int prxq, int offset)
244262306a36Sopenharmony_ci{
244362306a36Sopenharmony_ci	u32 val;
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	/* Convert offset from bytes to units of 32 bytes */
244662306a36Sopenharmony_ci	offset = offset >> 5;
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq));
244962306a36Sopenharmony_ci	val &= ~MVPP2_RXQ_PACKET_OFFSET_MASK;
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	/* Offset is in */
245262306a36Sopenharmony_ci	val |= ((offset << MVPP2_RXQ_PACKET_OFFSET_OFFS) &
245362306a36Sopenharmony_ci		    MVPP2_RXQ_PACKET_OFFSET_MASK);
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(prxq), val);
245662306a36Sopenharmony_ci}
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci/* Tx descriptors helper methods */
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci/* Get pointer to next Tx descriptor to be processed (send) by HW */
246162306a36Sopenharmony_cistatic struct mvpp2_tx_desc *
246262306a36Sopenharmony_cimvpp2_txq_next_desc_get(struct mvpp2_tx_queue *txq)
246362306a36Sopenharmony_ci{
246462306a36Sopenharmony_ci	int tx_desc = txq->next_desc_to_proc;
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	txq->next_desc_to_proc = MVPP2_QUEUE_NEXT_DESC(txq, tx_desc);
246762306a36Sopenharmony_ci	return txq->descs + tx_desc;
246862306a36Sopenharmony_ci}
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci/* Update HW with number of aggregated Tx descriptors to be sent
247162306a36Sopenharmony_ci *
247262306a36Sopenharmony_ci * Called only from mvpp2_tx(), so migration is disabled, using
247362306a36Sopenharmony_ci * smp_processor_id() is OK.
247462306a36Sopenharmony_ci */
247562306a36Sopenharmony_cistatic void mvpp2_aggr_txq_pend_desc_add(struct mvpp2_port *port, int pending)
247662306a36Sopenharmony_ci{
247762306a36Sopenharmony_ci	/* aggregated access - relevant TXQ number is written in TX desc */
247862306a36Sopenharmony_ci	mvpp2_thread_write(port->priv,
247962306a36Sopenharmony_ci			   mvpp2_cpu_to_thread(port->priv, smp_processor_id()),
248062306a36Sopenharmony_ci			   MVPP2_AGGR_TXQ_UPDATE_REG, pending);
248162306a36Sopenharmony_ci}
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci/* Check if there are enough free descriptors in aggregated txq.
248462306a36Sopenharmony_ci * If not, update the number of occupied descriptors and repeat the check.
248562306a36Sopenharmony_ci *
248662306a36Sopenharmony_ci * Called only from mvpp2_tx(), so migration is disabled, using
248762306a36Sopenharmony_ci * smp_processor_id() is OK.
248862306a36Sopenharmony_ci */
248962306a36Sopenharmony_cistatic int mvpp2_aggr_desc_num_check(struct mvpp2_port *port,
249062306a36Sopenharmony_ci				     struct mvpp2_tx_queue *aggr_txq, int num)
249162306a36Sopenharmony_ci{
249262306a36Sopenharmony_ci	if ((aggr_txq->count + num) > MVPP2_AGGR_TXQ_SIZE) {
249362306a36Sopenharmony_ci		/* Update number of occupied aggregated Tx descriptors */
249462306a36Sopenharmony_ci		unsigned int thread =
249562306a36Sopenharmony_ci			mvpp2_cpu_to_thread(port->priv, smp_processor_id());
249662306a36Sopenharmony_ci		u32 val = mvpp2_read_relaxed(port->priv,
249762306a36Sopenharmony_ci					     MVPP2_AGGR_TXQ_STATUS_REG(thread));
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci		aggr_txq->count = val & MVPP2_AGGR_TXQ_PENDING_MASK;
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci		if ((aggr_txq->count + num) > MVPP2_AGGR_TXQ_SIZE)
250262306a36Sopenharmony_ci			return -ENOMEM;
250362306a36Sopenharmony_ci	}
250462306a36Sopenharmony_ci	return 0;
250562306a36Sopenharmony_ci}
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci/* Reserved Tx descriptors allocation request
250862306a36Sopenharmony_ci *
250962306a36Sopenharmony_ci * Called only from mvpp2_txq_reserved_desc_num_proc(), itself called
251062306a36Sopenharmony_ci * only by mvpp2_tx(), so migration is disabled, using
251162306a36Sopenharmony_ci * smp_processor_id() is OK.
251262306a36Sopenharmony_ci */
251362306a36Sopenharmony_cistatic int mvpp2_txq_alloc_reserved_desc(struct mvpp2_port *port,
251462306a36Sopenharmony_ci					 struct mvpp2_tx_queue *txq, int num)
251562306a36Sopenharmony_ci{
251662306a36Sopenharmony_ci	unsigned int thread = mvpp2_cpu_to_thread(port->priv, smp_processor_id());
251762306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
251862306a36Sopenharmony_ci	u32 val;
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	val = (txq->id << MVPP2_TXQ_RSVD_REQ_Q_OFFSET) | num;
252162306a36Sopenharmony_ci	mvpp2_thread_write_relaxed(priv, thread, MVPP2_TXQ_RSVD_REQ_REG, val);
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci	val = mvpp2_thread_read_relaxed(priv, thread, MVPP2_TXQ_RSVD_RSLT_REG);
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	return val & MVPP2_TXQ_RSVD_RSLT_MASK;
252662306a36Sopenharmony_ci}
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci/* Check if there are enough reserved descriptors for transmission.
252962306a36Sopenharmony_ci * If not, request chunk of reserved descriptors and check again.
253062306a36Sopenharmony_ci */
253162306a36Sopenharmony_cistatic int mvpp2_txq_reserved_desc_num_proc(struct mvpp2_port *port,
253262306a36Sopenharmony_ci					    struct mvpp2_tx_queue *txq,
253362306a36Sopenharmony_ci					    struct mvpp2_txq_pcpu *txq_pcpu,
253462306a36Sopenharmony_ci					    int num)
253562306a36Sopenharmony_ci{
253662306a36Sopenharmony_ci	int req, desc_count;
253762306a36Sopenharmony_ci	unsigned int thread;
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ci	if (txq_pcpu->reserved_num >= num)
254062306a36Sopenharmony_ci		return 0;
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	/* Not enough descriptors reserved! Update the reserved descriptor
254362306a36Sopenharmony_ci	 * count and check again.
254462306a36Sopenharmony_ci	 */
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci	desc_count = 0;
254762306a36Sopenharmony_ci	/* Compute total of used descriptors */
254862306a36Sopenharmony_ci	for (thread = 0; thread < port->priv->nthreads; thread++) {
254962306a36Sopenharmony_ci		struct mvpp2_txq_pcpu *txq_pcpu_aux;
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci		txq_pcpu_aux = per_cpu_ptr(txq->pcpu, thread);
255262306a36Sopenharmony_ci		desc_count += txq_pcpu_aux->count;
255362306a36Sopenharmony_ci		desc_count += txq_pcpu_aux->reserved_num;
255462306a36Sopenharmony_ci	}
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci	req = max(MVPP2_CPU_DESC_CHUNK, num - txq_pcpu->reserved_num);
255762306a36Sopenharmony_ci	desc_count += req;
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	if (desc_count >
256062306a36Sopenharmony_ci	   (txq->size - (MVPP2_MAX_THREADS * MVPP2_CPU_DESC_CHUNK)))
256162306a36Sopenharmony_ci		return -ENOMEM;
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci	txq_pcpu->reserved_num += mvpp2_txq_alloc_reserved_desc(port, txq, req);
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	/* OK, the descriptor could have been updated: check again. */
256662306a36Sopenharmony_ci	if (txq_pcpu->reserved_num < num)
256762306a36Sopenharmony_ci		return -ENOMEM;
256862306a36Sopenharmony_ci	return 0;
256962306a36Sopenharmony_ci}
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci/* Release the last allocated Tx descriptor. Useful to handle DMA
257262306a36Sopenharmony_ci * mapping failures in the Tx path.
257362306a36Sopenharmony_ci */
257462306a36Sopenharmony_cistatic void mvpp2_txq_desc_put(struct mvpp2_tx_queue *txq)
257562306a36Sopenharmony_ci{
257662306a36Sopenharmony_ci	if (txq->next_desc_to_proc == 0)
257762306a36Sopenharmony_ci		txq->next_desc_to_proc = txq->last_desc - 1;
257862306a36Sopenharmony_ci	else
257962306a36Sopenharmony_ci		txq->next_desc_to_proc--;
258062306a36Sopenharmony_ci}
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci/* Set Tx descriptors fields relevant for CSUM calculation */
258362306a36Sopenharmony_cistatic u32 mvpp2_txq_desc_csum(int l3_offs, __be16 l3_proto,
258462306a36Sopenharmony_ci			       int ip_hdr_len, int l4_proto)
258562306a36Sopenharmony_ci{
258662306a36Sopenharmony_ci	u32 command;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	/* fields: L3_offset, IP_hdrlen, L3_type, G_IPv4_chk,
258962306a36Sopenharmony_ci	 * G_L4_chk, L4_type required only for checksum calculation
259062306a36Sopenharmony_ci	 */
259162306a36Sopenharmony_ci	command = (l3_offs << MVPP2_TXD_L3_OFF_SHIFT);
259262306a36Sopenharmony_ci	command |= (ip_hdr_len << MVPP2_TXD_IP_HLEN_SHIFT);
259362306a36Sopenharmony_ci	command |= MVPP2_TXD_IP_CSUM_DISABLE;
259462306a36Sopenharmony_ci
259562306a36Sopenharmony_ci	if (l3_proto == htons(ETH_P_IP)) {
259662306a36Sopenharmony_ci		command &= ~MVPP2_TXD_IP_CSUM_DISABLE;	/* enable IPv4 csum */
259762306a36Sopenharmony_ci		command &= ~MVPP2_TXD_L3_IP6;		/* enable IPv4 */
259862306a36Sopenharmony_ci	} else {
259962306a36Sopenharmony_ci		command |= MVPP2_TXD_L3_IP6;		/* enable IPv6 */
260062306a36Sopenharmony_ci	}
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	if (l4_proto == IPPROTO_TCP) {
260362306a36Sopenharmony_ci		command &= ~MVPP2_TXD_L4_UDP;		/* enable TCP */
260462306a36Sopenharmony_ci		command &= ~MVPP2_TXD_L4_CSUM_FRAG;	/* generate L4 csum */
260562306a36Sopenharmony_ci	} else if (l4_proto == IPPROTO_UDP) {
260662306a36Sopenharmony_ci		command |= MVPP2_TXD_L4_UDP;		/* enable UDP */
260762306a36Sopenharmony_ci		command &= ~MVPP2_TXD_L4_CSUM_FRAG;	/* generate L4 csum */
260862306a36Sopenharmony_ci	} else {
260962306a36Sopenharmony_ci		command |= MVPP2_TXD_L4_CSUM_NOT;
261062306a36Sopenharmony_ci	}
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	return command;
261362306a36Sopenharmony_ci}
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci/* Get number of sent descriptors and decrement counter.
261662306a36Sopenharmony_ci * The number of sent descriptors is returned.
261762306a36Sopenharmony_ci * Per-thread access
261862306a36Sopenharmony_ci *
261962306a36Sopenharmony_ci * Called only from mvpp2_txq_done(), called from mvpp2_tx()
262062306a36Sopenharmony_ci * (migration disabled) and from the TX completion tasklet (migration
262162306a36Sopenharmony_ci * disabled) so using smp_processor_id() is OK.
262262306a36Sopenharmony_ci */
262362306a36Sopenharmony_cistatic inline int mvpp2_txq_sent_desc_proc(struct mvpp2_port *port,
262462306a36Sopenharmony_ci					   struct mvpp2_tx_queue *txq)
262562306a36Sopenharmony_ci{
262662306a36Sopenharmony_ci	u32 val;
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	/* Reading status reg resets transmitted descriptor counter */
262962306a36Sopenharmony_ci	val = mvpp2_thread_read_relaxed(port->priv,
263062306a36Sopenharmony_ci					mvpp2_cpu_to_thread(port->priv, smp_processor_id()),
263162306a36Sopenharmony_ci					MVPP2_TXQ_SENT_REG(txq->id));
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci	return (val & MVPP2_TRANSMITTED_COUNT_MASK) >>
263462306a36Sopenharmony_ci		MVPP2_TRANSMITTED_COUNT_OFFSET;
263562306a36Sopenharmony_ci}
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci/* Called through on_each_cpu(), so runs on all CPUs, with migration
263862306a36Sopenharmony_ci * disabled, therefore using smp_processor_id() is OK.
263962306a36Sopenharmony_ci */
264062306a36Sopenharmony_cistatic void mvpp2_txq_sent_counter_clear(void *arg)
264162306a36Sopenharmony_ci{
264262306a36Sopenharmony_ci	struct mvpp2_port *port = arg;
264362306a36Sopenharmony_ci	int queue;
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci	/* If the thread isn't used, don't do anything */
264662306a36Sopenharmony_ci	if (smp_processor_id() >= port->priv->nthreads)
264762306a36Sopenharmony_ci		return;
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci	for (queue = 0; queue < port->ntxqs; queue++) {
265062306a36Sopenharmony_ci		int id = port->txqs[queue]->id;
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci		mvpp2_thread_read(port->priv,
265362306a36Sopenharmony_ci				  mvpp2_cpu_to_thread(port->priv, smp_processor_id()),
265462306a36Sopenharmony_ci				  MVPP2_TXQ_SENT_REG(id));
265562306a36Sopenharmony_ci	}
265662306a36Sopenharmony_ci}
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci/* Set max sizes for Tx queues */
265962306a36Sopenharmony_cistatic void mvpp2_txp_max_tx_size_set(struct mvpp2_port *port)
266062306a36Sopenharmony_ci{
266162306a36Sopenharmony_ci	u32	val, size, mtu;
266262306a36Sopenharmony_ci	int	txq, tx_port_num;
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci	mtu = port->pkt_size * 8;
266562306a36Sopenharmony_ci	if (mtu > MVPP2_TXP_MTU_MAX)
266662306a36Sopenharmony_ci		mtu = MVPP2_TXP_MTU_MAX;
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci	/* WA for wrong Token bucket update: Set MTU value = 3*real MTU value */
266962306a36Sopenharmony_ci	mtu = 3 * mtu;
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci	/* Indirect access to registers */
267262306a36Sopenharmony_ci	tx_port_num = mvpp2_egress_port(port);
267362306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num);
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ci	/* Set MTU */
267662306a36Sopenharmony_ci	val = mvpp2_read(port->priv, MVPP2_TXP_SCHED_MTU_REG);
267762306a36Sopenharmony_ci	val &= ~MVPP2_TXP_MTU_MAX;
267862306a36Sopenharmony_ci	val |= mtu;
267962306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_MTU_REG, val);
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	/* TXP token size and all TXQs token size must be larger that MTU */
268262306a36Sopenharmony_ci	val = mvpp2_read(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG);
268362306a36Sopenharmony_ci	size = val & MVPP2_TXP_TOKEN_SIZE_MAX;
268462306a36Sopenharmony_ci	if (size < mtu) {
268562306a36Sopenharmony_ci		size = mtu;
268662306a36Sopenharmony_ci		val &= ~MVPP2_TXP_TOKEN_SIZE_MAX;
268762306a36Sopenharmony_ci		val |= size;
268862306a36Sopenharmony_ci		mvpp2_write(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val);
268962306a36Sopenharmony_ci	}
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci	for (txq = 0; txq < port->ntxqs; txq++) {
269262306a36Sopenharmony_ci		val = mvpp2_read(port->priv,
269362306a36Sopenharmony_ci				 MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq));
269462306a36Sopenharmony_ci		size = val & MVPP2_TXQ_TOKEN_SIZE_MAX;
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci		if (size < mtu) {
269762306a36Sopenharmony_ci			size = mtu;
269862306a36Sopenharmony_ci			val &= ~MVPP2_TXQ_TOKEN_SIZE_MAX;
269962306a36Sopenharmony_ci			val |= size;
270062306a36Sopenharmony_ci			mvpp2_write(port->priv,
270162306a36Sopenharmony_ci				    MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq),
270262306a36Sopenharmony_ci				    val);
270362306a36Sopenharmony_ci		}
270462306a36Sopenharmony_ci	}
270562306a36Sopenharmony_ci}
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci/* Set the number of non-occupied descriptors threshold */
270862306a36Sopenharmony_cistatic void mvpp2_set_rxq_free_tresh(struct mvpp2_port *port,
270962306a36Sopenharmony_ci				     struct mvpp2_rx_queue *rxq)
271062306a36Sopenharmony_ci{
271162306a36Sopenharmony_ci	u32 val;
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id);
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci	val = mvpp2_read(port->priv, MVPP2_RXQ_THRESH_REG);
271662306a36Sopenharmony_ci	val &= ~MVPP2_RXQ_NON_OCCUPIED_MASK;
271762306a36Sopenharmony_ci	val |= MSS_THRESHOLD_STOP << MVPP2_RXQ_NON_OCCUPIED_OFFSET;
271862306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_RXQ_THRESH_REG, val);
271962306a36Sopenharmony_ci}
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci/* Set the number of packets that will be received before Rx interrupt
272262306a36Sopenharmony_ci * will be generated by HW.
272362306a36Sopenharmony_ci */
272462306a36Sopenharmony_cistatic void mvpp2_rx_pkts_coal_set(struct mvpp2_port *port,
272562306a36Sopenharmony_ci				   struct mvpp2_rx_queue *rxq)
272662306a36Sopenharmony_ci{
272762306a36Sopenharmony_ci	unsigned int thread = mvpp2_cpu_to_thread(port->priv, get_cpu());
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_ci	if (rxq->pkts_coal > MVPP2_OCCUPIED_THRESH_MASK)
273062306a36Sopenharmony_ci		rxq->pkts_coal = MVPP2_OCCUPIED_THRESH_MASK;
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_RXQ_NUM_REG, rxq->id);
273362306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_RXQ_THRESH_REG,
273462306a36Sopenharmony_ci			   rxq->pkts_coal);
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci	put_cpu();
273762306a36Sopenharmony_ci}
273862306a36Sopenharmony_ci
273962306a36Sopenharmony_ci/* For some reason in the LSP this is done on each CPU. Why ? */
274062306a36Sopenharmony_cistatic void mvpp2_tx_pkts_coal_set(struct mvpp2_port *port,
274162306a36Sopenharmony_ci				   struct mvpp2_tx_queue *txq)
274262306a36Sopenharmony_ci{
274362306a36Sopenharmony_ci	unsigned int thread;
274462306a36Sopenharmony_ci	u32 val;
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	if (txq->done_pkts_coal > MVPP2_TXQ_THRESH_MASK)
274762306a36Sopenharmony_ci		txq->done_pkts_coal = MVPP2_TXQ_THRESH_MASK;
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	val = (txq->done_pkts_coal << MVPP2_TXQ_THRESH_OFFSET);
275062306a36Sopenharmony_ci	/* PKT-coalescing registers are per-queue + per-thread */
275162306a36Sopenharmony_ci	for (thread = 0; thread < MVPP2_MAX_THREADS; thread++) {
275262306a36Sopenharmony_ci		mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_NUM_REG, txq->id);
275362306a36Sopenharmony_ci		mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_THRESH_REG, val);
275462306a36Sopenharmony_ci	}
275562306a36Sopenharmony_ci}
275662306a36Sopenharmony_ci
275762306a36Sopenharmony_cistatic u32 mvpp2_usec_to_cycles(u32 usec, unsigned long clk_hz)
275862306a36Sopenharmony_ci{
275962306a36Sopenharmony_ci	u64 tmp = (u64)clk_hz * usec;
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	do_div(tmp, USEC_PER_SEC);
276262306a36Sopenharmony_ci
276362306a36Sopenharmony_ci	return tmp > U32_MAX ? U32_MAX : tmp;
276462306a36Sopenharmony_ci}
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_cistatic u32 mvpp2_cycles_to_usec(u32 cycles, unsigned long clk_hz)
276762306a36Sopenharmony_ci{
276862306a36Sopenharmony_ci	u64 tmp = (u64)cycles * USEC_PER_SEC;
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	do_div(tmp, clk_hz);
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	return tmp > U32_MAX ? U32_MAX : tmp;
277362306a36Sopenharmony_ci}
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci/* Set the time delay in usec before Rx interrupt */
277662306a36Sopenharmony_cistatic void mvpp2_rx_time_coal_set(struct mvpp2_port *port,
277762306a36Sopenharmony_ci				   struct mvpp2_rx_queue *rxq)
277862306a36Sopenharmony_ci{
277962306a36Sopenharmony_ci	unsigned long freq = port->priv->tclk;
278062306a36Sopenharmony_ci	u32 val = mvpp2_usec_to_cycles(rxq->time_coal, freq);
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci	if (val > MVPP2_MAX_ISR_RX_THRESHOLD) {
278362306a36Sopenharmony_ci		rxq->time_coal =
278462306a36Sopenharmony_ci			mvpp2_cycles_to_usec(MVPP2_MAX_ISR_RX_THRESHOLD, freq);
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci		/* re-evaluate to get actual register value */
278762306a36Sopenharmony_ci		val = mvpp2_usec_to_cycles(rxq->time_coal, freq);
278862306a36Sopenharmony_ci	}
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_ISR_RX_THRESHOLD_REG(rxq->id), val);
279162306a36Sopenharmony_ci}
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_cistatic void mvpp2_tx_time_coal_set(struct mvpp2_port *port)
279462306a36Sopenharmony_ci{
279562306a36Sopenharmony_ci	unsigned long freq = port->priv->tclk;
279662306a36Sopenharmony_ci	u32 val = mvpp2_usec_to_cycles(port->tx_time_coal, freq);
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	if (val > MVPP2_MAX_ISR_TX_THRESHOLD) {
279962306a36Sopenharmony_ci		port->tx_time_coal =
280062306a36Sopenharmony_ci			mvpp2_cycles_to_usec(MVPP2_MAX_ISR_TX_THRESHOLD, freq);
280162306a36Sopenharmony_ci
280262306a36Sopenharmony_ci		/* re-evaluate to get actual register value */
280362306a36Sopenharmony_ci		val = mvpp2_usec_to_cycles(port->tx_time_coal, freq);
280462306a36Sopenharmony_ci	}
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_ISR_TX_THRESHOLD_REG(port->id), val);
280762306a36Sopenharmony_ci}
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci/* Free Tx queue skbuffs */
281062306a36Sopenharmony_cistatic void mvpp2_txq_bufs_free(struct mvpp2_port *port,
281162306a36Sopenharmony_ci				struct mvpp2_tx_queue *txq,
281262306a36Sopenharmony_ci				struct mvpp2_txq_pcpu *txq_pcpu, int num)
281362306a36Sopenharmony_ci{
281462306a36Sopenharmony_ci	struct xdp_frame_bulk bq;
281562306a36Sopenharmony_ci	int i;
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	xdp_frame_bulk_init(&bq);
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_ci	rcu_read_lock(); /* need for xdp_return_frame_bulk */
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci	for (i = 0; i < num; i++) {
282262306a36Sopenharmony_ci		struct mvpp2_txq_pcpu_buf *tx_buf =
282362306a36Sopenharmony_ci			txq_pcpu->buffs + txq_pcpu->txq_get_index;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci		if (!IS_TSO_HEADER(txq_pcpu, tx_buf->dma) &&
282662306a36Sopenharmony_ci		    tx_buf->type != MVPP2_TYPE_XDP_TX)
282762306a36Sopenharmony_ci			dma_unmap_single(port->dev->dev.parent, tx_buf->dma,
282862306a36Sopenharmony_ci					 tx_buf->size, DMA_TO_DEVICE);
282962306a36Sopenharmony_ci		if (tx_buf->type == MVPP2_TYPE_SKB && tx_buf->skb)
283062306a36Sopenharmony_ci			dev_kfree_skb_any(tx_buf->skb);
283162306a36Sopenharmony_ci		else if (tx_buf->type == MVPP2_TYPE_XDP_TX ||
283262306a36Sopenharmony_ci			 tx_buf->type == MVPP2_TYPE_XDP_NDO)
283362306a36Sopenharmony_ci			xdp_return_frame_bulk(tx_buf->xdpf, &bq);
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci		mvpp2_txq_inc_get(txq_pcpu);
283662306a36Sopenharmony_ci	}
283762306a36Sopenharmony_ci	xdp_flush_frame_bulk(&bq);
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_ci	rcu_read_unlock();
284062306a36Sopenharmony_ci}
284162306a36Sopenharmony_ci
284262306a36Sopenharmony_cistatic inline struct mvpp2_rx_queue *mvpp2_get_rx_queue(struct mvpp2_port *port,
284362306a36Sopenharmony_ci							u32 cause)
284462306a36Sopenharmony_ci{
284562306a36Sopenharmony_ci	int queue = fls(cause) - 1;
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	return port->rxqs[queue];
284862306a36Sopenharmony_ci}
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_cistatic inline struct mvpp2_tx_queue *mvpp2_get_tx_queue(struct mvpp2_port *port,
285162306a36Sopenharmony_ci							u32 cause)
285262306a36Sopenharmony_ci{
285362306a36Sopenharmony_ci	int queue = fls(cause) - 1;
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_ci	return port->txqs[queue];
285662306a36Sopenharmony_ci}
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci/* Handle end of transmission */
285962306a36Sopenharmony_cistatic void mvpp2_txq_done(struct mvpp2_port *port, struct mvpp2_tx_queue *txq,
286062306a36Sopenharmony_ci			   struct mvpp2_txq_pcpu *txq_pcpu)
286162306a36Sopenharmony_ci{
286262306a36Sopenharmony_ci	struct netdev_queue *nq = netdev_get_tx_queue(port->dev, txq->log_id);
286362306a36Sopenharmony_ci	int tx_done;
286462306a36Sopenharmony_ci
286562306a36Sopenharmony_ci	if (txq_pcpu->thread != mvpp2_cpu_to_thread(port->priv, smp_processor_id()))
286662306a36Sopenharmony_ci		netdev_err(port->dev, "wrong cpu on the end of Tx processing\n");
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci	tx_done = mvpp2_txq_sent_desc_proc(port, txq);
286962306a36Sopenharmony_ci	if (!tx_done)
287062306a36Sopenharmony_ci		return;
287162306a36Sopenharmony_ci	mvpp2_txq_bufs_free(port, txq, txq_pcpu, tx_done);
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci	txq_pcpu->count -= tx_done;
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	if (netif_tx_queue_stopped(nq))
287662306a36Sopenharmony_ci		if (txq_pcpu->count <= txq_pcpu->wake_threshold)
287762306a36Sopenharmony_ci			netif_tx_wake_queue(nq);
287862306a36Sopenharmony_ci}
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_cistatic unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause,
288162306a36Sopenharmony_ci				  unsigned int thread)
288262306a36Sopenharmony_ci{
288362306a36Sopenharmony_ci	struct mvpp2_tx_queue *txq;
288462306a36Sopenharmony_ci	struct mvpp2_txq_pcpu *txq_pcpu;
288562306a36Sopenharmony_ci	unsigned int tx_todo = 0;
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci	while (cause) {
288862306a36Sopenharmony_ci		txq = mvpp2_get_tx_queue(port, cause);
288962306a36Sopenharmony_ci		if (!txq)
289062306a36Sopenharmony_ci			break;
289162306a36Sopenharmony_ci
289262306a36Sopenharmony_ci		txq_pcpu = per_cpu_ptr(txq->pcpu, thread);
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci		if (txq_pcpu->count) {
289562306a36Sopenharmony_ci			mvpp2_txq_done(port, txq, txq_pcpu);
289662306a36Sopenharmony_ci			tx_todo += txq_pcpu->count;
289762306a36Sopenharmony_ci		}
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci		cause &= ~(1 << txq->log_id);
290062306a36Sopenharmony_ci	}
290162306a36Sopenharmony_ci	return tx_todo;
290262306a36Sopenharmony_ci}
290362306a36Sopenharmony_ci
290462306a36Sopenharmony_ci/* Rx/Tx queue initialization/cleanup methods */
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci/* Allocate and initialize descriptors for aggr TXQ */
290762306a36Sopenharmony_cistatic int mvpp2_aggr_txq_init(struct platform_device *pdev,
290862306a36Sopenharmony_ci			       struct mvpp2_tx_queue *aggr_txq,
290962306a36Sopenharmony_ci			       unsigned int thread, struct mvpp2 *priv)
291062306a36Sopenharmony_ci{
291162306a36Sopenharmony_ci	u32 txq_dma;
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	/* Allocate memory for TX descriptors */
291462306a36Sopenharmony_ci	aggr_txq->descs = dma_alloc_coherent(&pdev->dev,
291562306a36Sopenharmony_ci					     MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE,
291662306a36Sopenharmony_ci					     &aggr_txq->descs_dma, GFP_KERNEL);
291762306a36Sopenharmony_ci	if (!aggr_txq->descs)
291862306a36Sopenharmony_ci		return -ENOMEM;
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	aggr_txq->last_desc = MVPP2_AGGR_TXQ_SIZE - 1;
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_ci	/* Aggr TXQ no reset WA */
292362306a36Sopenharmony_ci	aggr_txq->next_desc_to_proc = mvpp2_read(priv,
292462306a36Sopenharmony_ci						 MVPP2_AGGR_TXQ_INDEX_REG(thread));
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci	/* Set Tx descriptors queue starting address indirect
292762306a36Sopenharmony_ci	 * access
292862306a36Sopenharmony_ci	 */
292962306a36Sopenharmony_ci	if (priv->hw_version == MVPP21)
293062306a36Sopenharmony_ci		txq_dma = aggr_txq->descs_dma;
293162306a36Sopenharmony_ci	else
293262306a36Sopenharmony_ci		txq_dma = aggr_txq->descs_dma >>
293362306a36Sopenharmony_ci			MVPP22_AGGR_TXQ_DESC_ADDR_OFFS;
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_ADDR_REG(thread), txq_dma);
293662306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_SIZE_REG(thread),
293762306a36Sopenharmony_ci		    MVPP2_AGGR_TXQ_SIZE);
293862306a36Sopenharmony_ci
293962306a36Sopenharmony_ci	return 0;
294062306a36Sopenharmony_ci}
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci/* Create a specified Rx queue */
294362306a36Sopenharmony_cistatic int mvpp2_rxq_init(struct mvpp2_port *port,
294462306a36Sopenharmony_ci			  struct mvpp2_rx_queue *rxq)
294562306a36Sopenharmony_ci{
294662306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
294762306a36Sopenharmony_ci	unsigned int thread;
294862306a36Sopenharmony_ci	u32 rxq_dma;
294962306a36Sopenharmony_ci	int err;
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	rxq->size = port->rx_ring_size;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	/* Allocate memory for RX descriptors */
295462306a36Sopenharmony_ci	rxq->descs = dma_alloc_coherent(port->dev->dev.parent,
295562306a36Sopenharmony_ci					rxq->size * MVPP2_DESC_ALIGNED_SIZE,
295662306a36Sopenharmony_ci					&rxq->descs_dma, GFP_KERNEL);
295762306a36Sopenharmony_ci	if (!rxq->descs)
295862306a36Sopenharmony_ci		return -ENOMEM;
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	rxq->last_desc = rxq->size - 1;
296162306a36Sopenharmony_ci
296262306a36Sopenharmony_ci	/* Zero occupied and non-occupied counters - direct access */
296362306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_RXQ_STATUS_REG(rxq->id), 0);
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_ci	/* Set Rx descriptors queue starting address - indirect access */
296662306a36Sopenharmony_ci	thread = mvpp2_cpu_to_thread(port->priv, get_cpu());
296762306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_RXQ_NUM_REG, rxq->id);
296862306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21)
296962306a36Sopenharmony_ci		rxq_dma = rxq->descs_dma;
297062306a36Sopenharmony_ci	else
297162306a36Sopenharmony_ci		rxq_dma = rxq->descs_dma >> MVPP22_DESC_ADDR_OFFS;
297262306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_RXQ_DESC_ADDR_REG, rxq_dma);
297362306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_RXQ_DESC_SIZE_REG, rxq->size);
297462306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_RXQ_INDEX_REG, 0);
297562306a36Sopenharmony_ci	put_cpu();
297662306a36Sopenharmony_ci
297762306a36Sopenharmony_ci	/* Set Offset */
297862306a36Sopenharmony_ci	mvpp2_rxq_offset_set(port, rxq->id, MVPP2_SKB_HEADROOM);
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_ci	/* Set coalescing pkts and time */
298162306a36Sopenharmony_ci	mvpp2_rx_pkts_coal_set(port, rxq);
298262306a36Sopenharmony_ci	mvpp2_rx_time_coal_set(port, rxq);
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci	/* Set the number of non occupied descriptors threshold */
298562306a36Sopenharmony_ci	mvpp2_set_rxq_free_tresh(port, rxq);
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_ci	/* Add number of descriptors ready for receiving packets */
298862306a36Sopenharmony_ci	mvpp2_rxq_status_update(port, rxq->id, 0, rxq->size);
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_ci	if (priv->percpu_pools) {
299162306a36Sopenharmony_ci		err = xdp_rxq_info_reg(&rxq->xdp_rxq_short, port->dev, rxq->logic_rxq, 0);
299262306a36Sopenharmony_ci		if (err < 0)
299362306a36Sopenharmony_ci			goto err_free_dma;
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci		err = xdp_rxq_info_reg(&rxq->xdp_rxq_long, port->dev, rxq->logic_rxq, 0);
299662306a36Sopenharmony_ci		if (err < 0)
299762306a36Sopenharmony_ci			goto err_unregister_rxq_short;
299862306a36Sopenharmony_ci
299962306a36Sopenharmony_ci		/* Every RXQ has a pool for short and another for long packets */
300062306a36Sopenharmony_ci		err = xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq_short,
300162306a36Sopenharmony_ci						 MEM_TYPE_PAGE_POOL,
300262306a36Sopenharmony_ci						 priv->page_pool[rxq->logic_rxq]);
300362306a36Sopenharmony_ci		if (err < 0)
300462306a36Sopenharmony_ci			goto err_unregister_rxq_long;
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_ci		err = xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq_long,
300762306a36Sopenharmony_ci						 MEM_TYPE_PAGE_POOL,
300862306a36Sopenharmony_ci						 priv->page_pool[rxq->logic_rxq +
300962306a36Sopenharmony_ci								 port->nrxqs]);
301062306a36Sopenharmony_ci		if (err < 0)
301162306a36Sopenharmony_ci			goto err_unregister_mem_rxq_short;
301262306a36Sopenharmony_ci	}
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ci	return 0;
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_cierr_unregister_mem_rxq_short:
301762306a36Sopenharmony_ci	xdp_rxq_info_unreg_mem_model(&rxq->xdp_rxq_short);
301862306a36Sopenharmony_cierr_unregister_rxq_long:
301962306a36Sopenharmony_ci	xdp_rxq_info_unreg(&rxq->xdp_rxq_long);
302062306a36Sopenharmony_cierr_unregister_rxq_short:
302162306a36Sopenharmony_ci	xdp_rxq_info_unreg(&rxq->xdp_rxq_short);
302262306a36Sopenharmony_cierr_free_dma:
302362306a36Sopenharmony_ci	dma_free_coherent(port->dev->dev.parent,
302462306a36Sopenharmony_ci			  rxq->size * MVPP2_DESC_ALIGNED_SIZE,
302562306a36Sopenharmony_ci			  rxq->descs, rxq->descs_dma);
302662306a36Sopenharmony_ci	return err;
302762306a36Sopenharmony_ci}
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci/* Push packets received by the RXQ to BM pool */
303062306a36Sopenharmony_cistatic void mvpp2_rxq_drop_pkts(struct mvpp2_port *port,
303162306a36Sopenharmony_ci				struct mvpp2_rx_queue *rxq)
303262306a36Sopenharmony_ci{
303362306a36Sopenharmony_ci	int rx_received, i;
303462306a36Sopenharmony_ci
303562306a36Sopenharmony_ci	rx_received = mvpp2_rxq_received(port, rxq->id);
303662306a36Sopenharmony_ci	if (!rx_received)
303762306a36Sopenharmony_ci		return;
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ci	for (i = 0; i < rx_received; i++) {
304062306a36Sopenharmony_ci		struct mvpp2_rx_desc *rx_desc = mvpp2_rxq_next_desc_get(rxq);
304162306a36Sopenharmony_ci		u32 status = mvpp2_rxdesc_status_get(port, rx_desc);
304262306a36Sopenharmony_ci		int pool;
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ci		pool = (status & MVPP2_RXD_BM_POOL_ID_MASK) >>
304562306a36Sopenharmony_ci			MVPP2_RXD_BM_POOL_ID_OFFS;
304662306a36Sopenharmony_ci
304762306a36Sopenharmony_ci		mvpp2_bm_pool_put(port, pool,
304862306a36Sopenharmony_ci				  mvpp2_rxdesc_dma_addr_get(port, rx_desc),
304962306a36Sopenharmony_ci				  mvpp2_rxdesc_cookie_get(port, rx_desc));
305062306a36Sopenharmony_ci	}
305162306a36Sopenharmony_ci	mvpp2_rxq_status_update(port, rxq->id, rx_received, rx_received);
305262306a36Sopenharmony_ci}
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci/* Cleanup Rx queue */
305562306a36Sopenharmony_cistatic void mvpp2_rxq_deinit(struct mvpp2_port *port,
305662306a36Sopenharmony_ci			     struct mvpp2_rx_queue *rxq)
305762306a36Sopenharmony_ci{
305862306a36Sopenharmony_ci	unsigned int thread;
305962306a36Sopenharmony_ci
306062306a36Sopenharmony_ci	if (xdp_rxq_info_is_reg(&rxq->xdp_rxq_short))
306162306a36Sopenharmony_ci		xdp_rxq_info_unreg(&rxq->xdp_rxq_short);
306262306a36Sopenharmony_ci
306362306a36Sopenharmony_ci	if (xdp_rxq_info_is_reg(&rxq->xdp_rxq_long))
306462306a36Sopenharmony_ci		xdp_rxq_info_unreg(&rxq->xdp_rxq_long);
306562306a36Sopenharmony_ci
306662306a36Sopenharmony_ci	mvpp2_rxq_drop_pkts(port, rxq);
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci	if (rxq->descs)
306962306a36Sopenharmony_ci		dma_free_coherent(port->dev->dev.parent,
307062306a36Sopenharmony_ci				  rxq->size * MVPP2_DESC_ALIGNED_SIZE,
307162306a36Sopenharmony_ci				  rxq->descs,
307262306a36Sopenharmony_ci				  rxq->descs_dma);
307362306a36Sopenharmony_ci
307462306a36Sopenharmony_ci	rxq->descs             = NULL;
307562306a36Sopenharmony_ci	rxq->last_desc         = 0;
307662306a36Sopenharmony_ci	rxq->next_desc_to_proc = 0;
307762306a36Sopenharmony_ci	rxq->descs_dma         = 0;
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_ci	/* Clear Rx descriptors queue starting address and size;
308062306a36Sopenharmony_ci	 * free descriptor number
308162306a36Sopenharmony_ci	 */
308262306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_RXQ_STATUS_REG(rxq->id), 0);
308362306a36Sopenharmony_ci	thread = mvpp2_cpu_to_thread(port->priv, get_cpu());
308462306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_RXQ_NUM_REG, rxq->id);
308562306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_RXQ_DESC_ADDR_REG, 0);
308662306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_RXQ_DESC_SIZE_REG, 0);
308762306a36Sopenharmony_ci	put_cpu();
308862306a36Sopenharmony_ci}
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci/* Create and initialize a Tx queue */
309162306a36Sopenharmony_cistatic int mvpp2_txq_init(struct mvpp2_port *port,
309262306a36Sopenharmony_ci			  struct mvpp2_tx_queue *txq)
309362306a36Sopenharmony_ci{
309462306a36Sopenharmony_ci	u32 val;
309562306a36Sopenharmony_ci	unsigned int thread;
309662306a36Sopenharmony_ci	int desc, desc_per_txq, tx_port_num;
309762306a36Sopenharmony_ci	struct mvpp2_txq_pcpu *txq_pcpu;
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_ci	txq->size = port->tx_ring_size;
310062306a36Sopenharmony_ci
310162306a36Sopenharmony_ci	/* Allocate memory for Tx descriptors */
310262306a36Sopenharmony_ci	txq->descs = dma_alloc_coherent(port->dev->dev.parent,
310362306a36Sopenharmony_ci				txq->size * MVPP2_DESC_ALIGNED_SIZE,
310462306a36Sopenharmony_ci				&txq->descs_dma, GFP_KERNEL);
310562306a36Sopenharmony_ci	if (!txq->descs)
310662306a36Sopenharmony_ci		return -ENOMEM;
310762306a36Sopenharmony_ci
310862306a36Sopenharmony_ci	txq->last_desc = txq->size - 1;
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ci	/* Set Tx descriptors queue starting address - indirect access */
311162306a36Sopenharmony_ci	thread = mvpp2_cpu_to_thread(port->priv, get_cpu());
311262306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_NUM_REG, txq->id);
311362306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_DESC_ADDR_REG,
311462306a36Sopenharmony_ci			   txq->descs_dma);
311562306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_DESC_SIZE_REG,
311662306a36Sopenharmony_ci			   txq->size & MVPP2_TXQ_DESC_SIZE_MASK);
311762306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_INDEX_REG, 0);
311862306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_RSVD_CLR_REG,
311962306a36Sopenharmony_ci			   txq->id << MVPP2_TXQ_RSVD_CLR_OFFSET);
312062306a36Sopenharmony_ci	val = mvpp2_thread_read(port->priv, thread, MVPP2_TXQ_PENDING_REG);
312162306a36Sopenharmony_ci	val &= ~MVPP2_TXQ_PENDING_MASK;
312262306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_PENDING_REG, val);
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_ci	/* Calculate base address in prefetch buffer. We reserve 16 descriptors
312562306a36Sopenharmony_ci	 * for each existing TXQ.
312662306a36Sopenharmony_ci	 * TCONTS for PON port must be continuous from 0 to MVPP2_MAX_TCONT
312762306a36Sopenharmony_ci	 * GBE ports assumed to be continuous from 0 to MVPP2_MAX_PORTS
312862306a36Sopenharmony_ci	 */
312962306a36Sopenharmony_ci	desc_per_txq = 16;
313062306a36Sopenharmony_ci	desc = (port->id * MVPP2_MAX_TXQ * desc_per_txq) +
313162306a36Sopenharmony_ci	       (txq->log_id * desc_per_txq);
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_PREF_BUF_REG,
313462306a36Sopenharmony_ci			   MVPP2_PREF_BUF_PTR(desc) | MVPP2_PREF_BUF_SIZE_16 |
313562306a36Sopenharmony_ci			   MVPP2_PREF_BUF_THRESH(desc_per_txq / 2));
313662306a36Sopenharmony_ci	put_cpu();
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci	/* WRR / EJP configuration - indirect access */
313962306a36Sopenharmony_ci	tx_port_num = mvpp2_egress_port(port);
314062306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num);
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ci	val = mvpp2_read(port->priv, MVPP2_TXQ_SCHED_REFILL_REG(txq->log_id));
314362306a36Sopenharmony_ci	val &= ~MVPP2_TXQ_REFILL_PERIOD_ALL_MASK;
314462306a36Sopenharmony_ci	val |= MVPP2_TXQ_REFILL_PERIOD_MASK(1);
314562306a36Sopenharmony_ci	val |= MVPP2_TXQ_REFILL_TOKENS_ALL_MASK;
314662306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXQ_SCHED_REFILL_REG(txq->log_id), val);
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_ci	val = MVPP2_TXQ_TOKEN_SIZE_MAX;
314962306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq->log_id),
315062306a36Sopenharmony_ci		    val);
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	for (thread = 0; thread < port->priv->nthreads; thread++) {
315362306a36Sopenharmony_ci		txq_pcpu = per_cpu_ptr(txq->pcpu, thread);
315462306a36Sopenharmony_ci		txq_pcpu->size = txq->size;
315562306a36Sopenharmony_ci		txq_pcpu->buffs = kmalloc_array(txq_pcpu->size,
315662306a36Sopenharmony_ci						sizeof(*txq_pcpu->buffs),
315762306a36Sopenharmony_ci						GFP_KERNEL);
315862306a36Sopenharmony_ci		if (!txq_pcpu->buffs)
315962306a36Sopenharmony_ci			return -ENOMEM;
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci		txq_pcpu->count = 0;
316262306a36Sopenharmony_ci		txq_pcpu->reserved_num = 0;
316362306a36Sopenharmony_ci		txq_pcpu->txq_put_index = 0;
316462306a36Sopenharmony_ci		txq_pcpu->txq_get_index = 0;
316562306a36Sopenharmony_ci		txq_pcpu->tso_headers = NULL;
316662306a36Sopenharmony_ci
316762306a36Sopenharmony_ci		txq_pcpu->stop_threshold = txq->size - MVPP2_MAX_SKB_DESCS;
316862306a36Sopenharmony_ci		txq_pcpu->wake_threshold = txq_pcpu->stop_threshold / 2;
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci		txq_pcpu->tso_headers =
317162306a36Sopenharmony_ci			dma_alloc_coherent(port->dev->dev.parent,
317262306a36Sopenharmony_ci					   txq_pcpu->size * TSO_HEADER_SIZE,
317362306a36Sopenharmony_ci					   &txq_pcpu->tso_headers_dma,
317462306a36Sopenharmony_ci					   GFP_KERNEL);
317562306a36Sopenharmony_ci		if (!txq_pcpu->tso_headers)
317662306a36Sopenharmony_ci			return -ENOMEM;
317762306a36Sopenharmony_ci	}
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_ci	return 0;
318062306a36Sopenharmony_ci}
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ci/* Free allocated TXQ resources */
318362306a36Sopenharmony_cistatic void mvpp2_txq_deinit(struct mvpp2_port *port,
318462306a36Sopenharmony_ci			     struct mvpp2_tx_queue *txq)
318562306a36Sopenharmony_ci{
318662306a36Sopenharmony_ci	struct mvpp2_txq_pcpu *txq_pcpu;
318762306a36Sopenharmony_ci	unsigned int thread;
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_ci	for (thread = 0; thread < port->priv->nthreads; thread++) {
319062306a36Sopenharmony_ci		txq_pcpu = per_cpu_ptr(txq->pcpu, thread);
319162306a36Sopenharmony_ci		kfree(txq_pcpu->buffs);
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci		if (txq_pcpu->tso_headers)
319462306a36Sopenharmony_ci			dma_free_coherent(port->dev->dev.parent,
319562306a36Sopenharmony_ci					  txq_pcpu->size * TSO_HEADER_SIZE,
319662306a36Sopenharmony_ci					  txq_pcpu->tso_headers,
319762306a36Sopenharmony_ci					  txq_pcpu->tso_headers_dma);
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci		txq_pcpu->tso_headers = NULL;
320062306a36Sopenharmony_ci	}
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci	if (txq->descs)
320362306a36Sopenharmony_ci		dma_free_coherent(port->dev->dev.parent,
320462306a36Sopenharmony_ci				  txq->size * MVPP2_DESC_ALIGNED_SIZE,
320562306a36Sopenharmony_ci				  txq->descs, txq->descs_dma);
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ci	txq->descs             = NULL;
320862306a36Sopenharmony_ci	txq->last_desc         = 0;
320962306a36Sopenharmony_ci	txq->next_desc_to_proc = 0;
321062306a36Sopenharmony_ci	txq->descs_dma         = 0;
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	/* Set minimum bandwidth for disabled TXQs */
321362306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(txq->log_id), 0);
321462306a36Sopenharmony_ci
321562306a36Sopenharmony_ci	/* Set Tx descriptors queue starting address and size */
321662306a36Sopenharmony_ci	thread = mvpp2_cpu_to_thread(port->priv, get_cpu());
321762306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_NUM_REG, txq->id);
321862306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_DESC_ADDR_REG, 0);
321962306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_DESC_SIZE_REG, 0);
322062306a36Sopenharmony_ci	put_cpu();
322162306a36Sopenharmony_ci}
322262306a36Sopenharmony_ci
322362306a36Sopenharmony_ci/* Cleanup Tx ports */
322462306a36Sopenharmony_cistatic void mvpp2_txq_clean(struct mvpp2_port *port, struct mvpp2_tx_queue *txq)
322562306a36Sopenharmony_ci{
322662306a36Sopenharmony_ci	struct mvpp2_txq_pcpu *txq_pcpu;
322762306a36Sopenharmony_ci	int delay, pending;
322862306a36Sopenharmony_ci	unsigned int thread = mvpp2_cpu_to_thread(port->priv, get_cpu());
322962306a36Sopenharmony_ci	u32 val;
323062306a36Sopenharmony_ci
323162306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_NUM_REG, txq->id);
323262306a36Sopenharmony_ci	val = mvpp2_thread_read(port->priv, thread, MVPP2_TXQ_PREF_BUF_REG);
323362306a36Sopenharmony_ci	val |= MVPP2_TXQ_DRAIN_EN_MASK;
323462306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_PREF_BUF_REG, val);
323562306a36Sopenharmony_ci
323662306a36Sopenharmony_ci	/* The napi queue has been stopped so wait for all packets
323762306a36Sopenharmony_ci	 * to be transmitted.
323862306a36Sopenharmony_ci	 */
323962306a36Sopenharmony_ci	delay = 0;
324062306a36Sopenharmony_ci	do {
324162306a36Sopenharmony_ci		if (delay >= MVPP2_TX_PENDING_TIMEOUT_MSEC) {
324262306a36Sopenharmony_ci			netdev_warn(port->dev,
324362306a36Sopenharmony_ci				    "port %d: cleaning queue %d timed out\n",
324462306a36Sopenharmony_ci				    port->id, txq->log_id);
324562306a36Sopenharmony_ci			break;
324662306a36Sopenharmony_ci		}
324762306a36Sopenharmony_ci		mdelay(1);
324862306a36Sopenharmony_ci		delay++;
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_ci		pending = mvpp2_thread_read(port->priv, thread,
325162306a36Sopenharmony_ci					    MVPP2_TXQ_PENDING_REG);
325262306a36Sopenharmony_ci		pending &= MVPP2_TXQ_PENDING_MASK;
325362306a36Sopenharmony_ci	} while (pending);
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci	val &= ~MVPP2_TXQ_DRAIN_EN_MASK;
325662306a36Sopenharmony_ci	mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_PREF_BUF_REG, val);
325762306a36Sopenharmony_ci	put_cpu();
325862306a36Sopenharmony_ci
325962306a36Sopenharmony_ci	for (thread = 0; thread < port->priv->nthreads; thread++) {
326062306a36Sopenharmony_ci		txq_pcpu = per_cpu_ptr(txq->pcpu, thread);
326162306a36Sopenharmony_ci
326262306a36Sopenharmony_ci		/* Release all packets */
326362306a36Sopenharmony_ci		mvpp2_txq_bufs_free(port, txq, txq_pcpu, txq_pcpu->count);
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_ci		/* Reset queue */
326662306a36Sopenharmony_ci		txq_pcpu->count = 0;
326762306a36Sopenharmony_ci		txq_pcpu->txq_put_index = 0;
326862306a36Sopenharmony_ci		txq_pcpu->txq_get_index = 0;
326962306a36Sopenharmony_ci	}
327062306a36Sopenharmony_ci}
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci/* Cleanup all Tx queues */
327362306a36Sopenharmony_cistatic void mvpp2_cleanup_txqs(struct mvpp2_port *port)
327462306a36Sopenharmony_ci{
327562306a36Sopenharmony_ci	struct mvpp2_tx_queue *txq;
327662306a36Sopenharmony_ci	int queue;
327762306a36Sopenharmony_ci	u32 val;
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	val = mvpp2_read(port->priv, MVPP2_TX_PORT_FLUSH_REG);
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ci	/* Reset Tx ports and delete Tx queues */
328262306a36Sopenharmony_ci	val |= MVPP2_TX_PORT_FLUSH_MASK(port->id);
328362306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TX_PORT_FLUSH_REG, val);
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_ci	for (queue = 0; queue < port->ntxqs; queue++) {
328662306a36Sopenharmony_ci		txq = port->txqs[queue];
328762306a36Sopenharmony_ci		mvpp2_txq_clean(port, txq);
328862306a36Sopenharmony_ci		mvpp2_txq_deinit(port, txq);
328962306a36Sopenharmony_ci	}
329062306a36Sopenharmony_ci
329162306a36Sopenharmony_ci	on_each_cpu(mvpp2_txq_sent_counter_clear, port, 1);
329262306a36Sopenharmony_ci
329362306a36Sopenharmony_ci	val &= ~MVPP2_TX_PORT_FLUSH_MASK(port->id);
329462306a36Sopenharmony_ci	mvpp2_write(port->priv, MVPP2_TX_PORT_FLUSH_REG, val);
329562306a36Sopenharmony_ci}
329662306a36Sopenharmony_ci
329762306a36Sopenharmony_ci/* Cleanup all Rx queues */
329862306a36Sopenharmony_cistatic void mvpp2_cleanup_rxqs(struct mvpp2_port *port)
329962306a36Sopenharmony_ci{
330062306a36Sopenharmony_ci	int queue;
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci	for (queue = 0; queue < port->nrxqs; queue++)
330362306a36Sopenharmony_ci		mvpp2_rxq_deinit(port, port->rxqs[queue]);
330462306a36Sopenharmony_ci
330562306a36Sopenharmony_ci	if (port->tx_fc)
330662306a36Sopenharmony_ci		mvpp2_rxq_disable_fc(port);
330762306a36Sopenharmony_ci}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci/* Init all Rx queues for port */
331062306a36Sopenharmony_cistatic int mvpp2_setup_rxqs(struct mvpp2_port *port)
331162306a36Sopenharmony_ci{
331262306a36Sopenharmony_ci	int queue, err;
331362306a36Sopenharmony_ci
331462306a36Sopenharmony_ci	for (queue = 0; queue < port->nrxqs; queue++) {
331562306a36Sopenharmony_ci		err = mvpp2_rxq_init(port, port->rxqs[queue]);
331662306a36Sopenharmony_ci		if (err)
331762306a36Sopenharmony_ci			goto err_cleanup;
331862306a36Sopenharmony_ci	}
331962306a36Sopenharmony_ci
332062306a36Sopenharmony_ci	if (port->tx_fc)
332162306a36Sopenharmony_ci		mvpp2_rxq_enable_fc(port);
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	return 0;
332462306a36Sopenharmony_ci
332562306a36Sopenharmony_cierr_cleanup:
332662306a36Sopenharmony_ci	mvpp2_cleanup_rxqs(port);
332762306a36Sopenharmony_ci	return err;
332862306a36Sopenharmony_ci}
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci/* Init all tx queues for port */
333162306a36Sopenharmony_cistatic int mvpp2_setup_txqs(struct mvpp2_port *port)
333262306a36Sopenharmony_ci{
333362306a36Sopenharmony_ci	struct mvpp2_tx_queue *txq;
333462306a36Sopenharmony_ci	int queue, err;
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	for (queue = 0; queue < port->ntxqs; queue++) {
333762306a36Sopenharmony_ci		txq = port->txqs[queue];
333862306a36Sopenharmony_ci		err = mvpp2_txq_init(port, txq);
333962306a36Sopenharmony_ci		if (err)
334062306a36Sopenharmony_ci			goto err_cleanup;
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_ci		/* Assign this queue to a CPU */
334362306a36Sopenharmony_ci		if (queue < num_possible_cpus())
334462306a36Sopenharmony_ci			netif_set_xps_queue(port->dev, cpumask_of(queue), queue);
334562306a36Sopenharmony_ci	}
334662306a36Sopenharmony_ci
334762306a36Sopenharmony_ci	if (port->has_tx_irqs) {
334862306a36Sopenharmony_ci		mvpp2_tx_time_coal_set(port);
334962306a36Sopenharmony_ci		for (queue = 0; queue < port->ntxqs; queue++) {
335062306a36Sopenharmony_ci			txq = port->txqs[queue];
335162306a36Sopenharmony_ci			mvpp2_tx_pkts_coal_set(port, txq);
335262306a36Sopenharmony_ci		}
335362306a36Sopenharmony_ci	}
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_ci	on_each_cpu(mvpp2_txq_sent_counter_clear, port, 1);
335662306a36Sopenharmony_ci	return 0;
335762306a36Sopenharmony_ci
335862306a36Sopenharmony_cierr_cleanup:
335962306a36Sopenharmony_ci	mvpp2_cleanup_txqs(port);
336062306a36Sopenharmony_ci	return err;
336162306a36Sopenharmony_ci}
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_ci/* The callback for per-port interrupt */
336462306a36Sopenharmony_cistatic irqreturn_t mvpp2_isr(int irq, void *dev_id)
336562306a36Sopenharmony_ci{
336662306a36Sopenharmony_ci	struct mvpp2_queue_vector *qv = dev_id;
336762306a36Sopenharmony_ci
336862306a36Sopenharmony_ci	mvpp2_qvec_interrupt_disable(qv);
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci	napi_schedule(&qv->napi);
337162306a36Sopenharmony_ci
337262306a36Sopenharmony_ci	return IRQ_HANDLED;
337362306a36Sopenharmony_ci}
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_cistatic void mvpp2_isr_handle_ptp_queue(struct mvpp2_port *port, int nq)
337662306a36Sopenharmony_ci{
337762306a36Sopenharmony_ci	struct skb_shared_hwtstamps shhwtstamps;
337862306a36Sopenharmony_ci	struct mvpp2_hwtstamp_queue *queue;
337962306a36Sopenharmony_ci	struct sk_buff *skb;
338062306a36Sopenharmony_ci	void __iomem *ptp_q;
338162306a36Sopenharmony_ci	unsigned int id;
338262306a36Sopenharmony_ci	u32 r0, r1, r2;
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci	ptp_q = port->priv->iface_base + MVPP22_PTP_BASE(port->gop_id);
338562306a36Sopenharmony_ci	if (nq)
338662306a36Sopenharmony_ci		ptp_q += MVPP22_PTP_TX_Q1_R0 - MVPP22_PTP_TX_Q0_R0;
338762306a36Sopenharmony_ci
338862306a36Sopenharmony_ci	queue = &port->tx_hwtstamp_queue[nq];
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_ci	while (1) {
339162306a36Sopenharmony_ci		r0 = readl_relaxed(ptp_q + MVPP22_PTP_TX_Q0_R0) & 0xffff;
339262306a36Sopenharmony_ci		if (!r0)
339362306a36Sopenharmony_ci			break;
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci		r1 = readl_relaxed(ptp_q + MVPP22_PTP_TX_Q0_R1) & 0xffff;
339662306a36Sopenharmony_ci		r2 = readl_relaxed(ptp_q + MVPP22_PTP_TX_Q0_R2) & 0xffff;
339762306a36Sopenharmony_ci
339862306a36Sopenharmony_ci		id = (r0 >> 1) & 31;
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ci		skb = queue->skb[id];
340162306a36Sopenharmony_ci		queue->skb[id] = NULL;
340262306a36Sopenharmony_ci		if (skb) {
340362306a36Sopenharmony_ci			u32 ts = r2 << 19 | r1 << 3 | r0 >> 13;
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci			mvpp22_tai_tstamp(port->priv->tai, ts, &shhwtstamps);
340662306a36Sopenharmony_ci			skb_tstamp_tx(skb, &shhwtstamps);
340762306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
340862306a36Sopenharmony_ci		}
340962306a36Sopenharmony_ci	}
341062306a36Sopenharmony_ci}
341162306a36Sopenharmony_ci
341262306a36Sopenharmony_cistatic void mvpp2_isr_handle_ptp(struct mvpp2_port *port)
341362306a36Sopenharmony_ci{
341462306a36Sopenharmony_ci	void __iomem *ptp;
341562306a36Sopenharmony_ci	u32 val;
341662306a36Sopenharmony_ci
341762306a36Sopenharmony_ci	ptp = port->priv->iface_base + MVPP22_PTP_BASE(port->gop_id);
341862306a36Sopenharmony_ci	val = readl(ptp + MVPP22_PTP_INT_CAUSE);
341962306a36Sopenharmony_ci	if (val & MVPP22_PTP_INT_CAUSE_QUEUE0)
342062306a36Sopenharmony_ci		mvpp2_isr_handle_ptp_queue(port, 0);
342162306a36Sopenharmony_ci	if (val & MVPP22_PTP_INT_CAUSE_QUEUE1)
342262306a36Sopenharmony_ci		mvpp2_isr_handle_ptp_queue(port, 1);
342362306a36Sopenharmony_ci}
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_cistatic void mvpp2_isr_handle_link(struct mvpp2_port *port, bool link)
342662306a36Sopenharmony_ci{
342762306a36Sopenharmony_ci	struct net_device *dev = port->dev;
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ci	if (port->phylink) {
343062306a36Sopenharmony_ci		phylink_mac_change(port->phylink, link);
343162306a36Sopenharmony_ci		return;
343262306a36Sopenharmony_ci	}
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_ci	if (!netif_running(dev))
343562306a36Sopenharmony_ci		return;
343662306a36Sopenharmony_ci
343762306a36Sopenharmony_ci	if (link) {
343862306a36Sopenharmony_ci		mvpp2_interrupts_enable(port);
343962306a36Sopenharmony_ci
344062306a36Sopenharmony_ci		mvpp2_egress_enable(port);
344162306a36Sopenharmony_ci		mvpp2_ingress_enable(port);
344262306a36Sopenharmony_ci		netif_carrier_on(dev);
344362306a36Sopenharmony_ci		netif_tx_wake_all_queues(dev);
344462306a36Sopenharmony_ci	} else {
344562306a36Sopenharmony_ci		netif_tx_stop_all_queues(dev);
344662306a36Sopenharmony_ci		netif_carrier_off(dev);
344762306a36Sopenharmony_ci		mvpp2_ingress_disable(port);
344862306a36Sopenharmony_ci		mvpp2_egress_disable(port);
344962306a36Sopenharmony_ci
345062306a36Sopenharmony_ci		mvpp2_interrupts_disable(port);
345162306a36Sopenharmony_ci	}
345262306a36Sopenharmony_ci}
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_cistatic void mvpp2_isr_handle_xlg(struct mvpp2_port *port)
345562306a36Sopenharmony_ci{
345662306a36Sopenharmony_ci	bool link;
345762306a36Sopenharmony_ci	u32 val;
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ci	val = readl(port->base + MVPP22_XLG_INT_STAT);
346062306a36Sopenharmony_ci	if (val & MVPP22_XLG_INT_STAT_LINK) {
346162306a36Sopenharmony_ci		val = readl(port->base + MVPP22_XLG_STATUS);
346262306a36Sopenharmony_ci		link = (val & MVPP22_XLG_STATUS_LINK_UP);
346362306a36Sopenharmony_ci		mvpp2_isr_handle_link(port, link);
346462306a36Sopenharmony_ci	}
346562306a36Sopenharmony_ci}
346662306a36Sopenharmony_ci
346762306a36Sopenharmony_cistatic void mvpp2_isr_handle_gmac_internal(struct mvpp2_port *port)
346862306a36Sopenharmony_ci{
346962306a36Sopenharmony_ci	bool link;
347062306a36Sopenharmony_ci	u32 val;
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci	if (phy_interface_mode_is_rgmii(port->phy_interface) ||
347362306a36Sopenharmony_ci	    phy_interface_mode_is_8023z(port->phy_interface) ||
347462306a36Sopenharmony_ci	    port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
347562306a36Sopenharmony_ci		val = readl(port->base + MVPP22_GMAC_INT_STAT);
347662306a36Sopenharmony_ci		if (val & MVPP22_GMAC_INT_STAT_LINK) {
347762306a36Sopenharmony_ci			val = readl(port->base + MVPP2_GMAC_STATUS0);
347862306a36Sopenharmony_ci			link = (val & MVPP2_GMAC_STATUS0_LINK_UP);
347962306a36Sopenharmony_ci			mvpp2_isr_handle_link(port, link);
348062306a36Sopenharmony_ci		}
348162306a36Sopenharmony_ci	}
348262306a36Sopenharmony_ci}
348362306a36Sopenharmony_ci
348462306a36Sopenharmony_ci/* Per-port interrupt for link status changes */
348562306a36Sopenharmony_cistatic irqreturn_t mvpp2_port_isr(int irq, void *dev_id)
348662306a36Sopenharmony_ci{
348762306a36Sopenharmony_ci	struct mvpp2_port *port = (struct mvpp2_port *)dev_id;
348862306a36Sopenharmony_ci	u32 val;
348962306a36Sopenharmony_ci
349062306a36Sopenharmony_ci	mvpp22_gop_mask_irq(port);
349162306a36Sopenharmony_ci
349262306a36Sopenharmony_ci	if (mvpp2_port_supports_xlg(port) &&
349362306a36Sopenharmony_ci	    mvpp2_is_xlg(port->phy_interface)) {
349462306a36Sopenharmony_ci		/* Check the external status register */
349562306a36Sopenharmony_ci		val = readl(port->base + MVPP22_XLG_EXT_INT_STAT);
349662306a36Sopenharmony_ci		if (val & MVPP22_XLG_EXT_INT_STAT_XLG)
349762306a36Sopenharmony_ci			mvpp2_isr_handle_xlg(port);
349862306a36Sopenharmony_ci		if (val & MVPP22_XLG_EXT_INT_STAT_PTP)
349962306a36Sopenharmony_ci			mvpp2_isr_handle_ptp(port);
350062306a36Sopenharmony_ci	} else {
350162306a36Sopenharmony_ci		/* If it's not the XLG, we must be using the GMAC.
350262306a36Sopenharmony_ci		 * Check the summary status.
350362306a36Sopenharmony_ci		 */
350462306a36Sopenharmony_ci		val = readl(port->base + MVPP22_GMAC_INT_SUM_STAT);
350562306a36Sopenharmony_ci		if (val & MVPP22_GMAC_INT_SUM_STAT_INTERNAL)
350662306a36Sopenharmony_ci			mvpp2_isr_handle_gmac_internal(port);
350762306a36Sopenharmony_ci		if (val & MVPP22_GMAC_INT_SUM_STAT_PTP)
350862306a36Sopenharmony_ci			mvpp2_isr_handle_ptp(port);
350962306a36Sopenharmony_ci	}
351062306a36Sopenharmony_ci
351162306a36Sopenharmony_ci	mvpp22_gop_unmask_irq(port);
351262306a36Sopenharmony_ci	return IRQ_HANDLED;
351362306a36Sopenharmony_ci}
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_cistatic enum hrtimer_restart mvpp2_hr_timer_cb(struct hrtimer *timer)
351662306a36Sopenharmony_ci{
351762306a36Sopenharmony_ci	struct net_device *dev;
351862306a36Sopenharmony_ci	struct mvpp2_port *port;
351962306a36Sopenharmony_ci	struct mvpp2_port_pcpu *port_pcpu;
352062306a36Sopenharmony_ci	unsigned int tx_todo, cause;
352162306a36Sopenharmony_ci
352262306a36Sopenharmony_ci	port_pcpu = container_of(timer, struct mvpp2_port_pcpu, tx_done_timer);
352362306a36Sopenharmony_ci	dev = port_pcpu->dev;
352462306a36Sopenharmony_ci
352562306a36Sopenharmony_ci	if (!netif_running(dev))
352662306a36Sopenharmony_ci		return HRTIMER_NORESTART;
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci	port_pcpu->timer_scheduled = false;
352962306a36Sopenharmony_ci	port = netdev_priv(dev);
353062306a36Sopenharmony_ci
353162306a36Sopenharmony_ci	/* Process all the Tx queues */
353262306a36Sopenharmony_ci	cause = (1 << port->ntxqs) - 1;
353362306a36Sopenharmony_ci	tx_todo = mvpp2_tx_done(port, cause,
353462306a36Sopenharmony_ci				mvpp2_cpu_to_thread(port->priv, smp_processor_id()));
353562306a36Sopenharmony_ci
353662306a36Sopenharmony_ci	/* Set the timer in case not all the packets were processed */
353762306a36Sopenharmony_ci	if (tx_todo && !port_pcpu->timer_scheduled) {
353862306a36Sopenharmony_ci		port_pcpu->timer_scheduled = true;
353962306a36Sopenharmony_ci		hrtimer_forward_now(&port_pcpu->tx_done_timer,
354062306a36Sopenharmony_ci				    MVPP2_TXDONE_HRTIMER_PERIOD_NS);
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci		return HRTIMER_RESTART;
354362306a36Sopenharmony_ci	}
354462306a36Sopenharmony_ci	return HRTIMER_NORESTART;
354562306a36Sopenharmony_ci}
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ci/* Main RX/TX processing routines */
354862306a36Sopenharmony_ci
354962306a36Sopenharmony_ci/* Display more error info */
355062306a36Sopenharmony_cistatic void mvpp2_rx_error(struct mvpp2_port *port,
355162306a36Sopenharmony_ci			   struct mvpp2_rx_desc *rx_desc)
355262306a36Sopenharmony_ci{
355362306a36Sopenharmony_ci	u32 status = mvpp2_rxdesc_status_get(port, rx_desc);
355462306a36Sopenharmony_ci	size_t sz = mvpp2_rxdesc_size_get(port, rx_desc);
355562306a36Sopenharmony_ci	char *err_str = NULL;
355662306a36Sopenharmony_ci
355762306a36Sopenharmony_ci	switch (status & MVPP2_RXD_ERR_CODE_MASK) {
355862306a36Sopenharmony_ci	case MVPP2_RXD_ERR_CRC:
355962306a36Sopenharmony_ci		err_str = "crc";
356062306a36Sopenharmony_ci		break;
356162306a36Sopenharmony_ci	case MVPP2_RXD_ERR_OVERRUN:
356262306a36Sopenharmony_ci		err_str = "overrun";
356362306a36Sopenharmony_ci		break;
356462306a36Sopenharmony_ci	case MVPP2_RXD_ERR_RESOURCE:
356562306a36Sopenharmony_ci		err_str = "resource";
356662306a36Sopenharmony_ci		break;
356762306a36Sopenharmony_ci	}
356862306a36Sopenharmony_ci	if (err_str && net_ratelimit())
356962306a36Sopenharmony_ci		netdev_err(port->dev,
357062306a36Sopenharmony_ci			   "bad rx status %08x (%s error), size=%zu\n",
357162306a36Sopenharmony_ci			   status, err_str, sz);
357262306a36Sopenharmony_ci}
357362306a36Sopenharmony_ci
357462306a36Sopenharmony_ci/* Handle RX checksum offload */
357562306a36Sopenharmony_cistatic int mvpp2_rx_csum(struct mvpp2_port *port, u32 status)
357662306a36Sopenharmony_ci{
357762306a36Sopenharmony_ci	if (((status & MVPP2_RXD_L3_IP4) &&
357862306a36Sopenharmony_ci	     !(status & MVPP2_RXD_IP4_HEADER_ERR)) ||
357962306a36Sopenharmony_ci	    (status & MVPP2_RXD_L3_IP6))
358062306a36Sopenharmony_ci		if (((status & MVPP2_RXD_L4_UDP) ||
358162306a36Sopenharmony_ci		     (status & MVPP2_RXD_L4_TCP)) &&
358262306a36Sopenharmony_ci		     (status & MVPP2_RXD_L4_CSUM_OK))
358362306a36Sopenharmony_ci			return CHECKSUM_UNNECESSARY;
358462306a36Sopenharmony_ci
358562306a36Sopenharmony_ci	return CHECKSUM_NONE;
358662306a36Sopenharmony_ci}
358762306a36Sopenharmony_ci
358862306a36Sopenharmony_ci/* Allocate a new skb and add it to BM pool */
358962306a36Sopenharmony_cistatic int mvpp2_rx_refill(struct mvpp2_port *port,
359062306a36Sopenharmony_ci			   struct mvpp2_bm_pool *bm_pool,
359162306a36Sopenharmony_ci			   struct page_pool *page_pool, int pool)
359262306a36Sopenharmony_ci{
359362306a36Sopenharmony_ci	dma_addr_t dma_addr;
359462306a36Sopenharmony_ci	phys_addr_t phys_addr;
359562306a36Sopenharmony_ci	void *buf;
359662306a36Sopenharmony_ci
359762306a36Sopenharmony_ci	buf = mvpp2_buf_alloc(port, bm_pool, page_pool,
359862306a36Sopenharmony_ci			      &dma_addr, &phys_addr, GFP_ATOMIC);
359962306a36Sopenharmony_ci	if (!buf)
360062306a36Sopenharmony_ci		return -ENOMEM;
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_ci	mvpp2_bm_pool_put(port, pool, dma_addr, phys_addr);
360362306a36Sopenharmony_ci
360462306a36Sopenharmony_ci	return 0;
360562306a36Sopenharmony_ci}
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci/* Handle tx checksum */
360862306a36Sopenharmony_cistatic u32 mvpp2_skb_tx_csum(struct mvpp2_port *port, struct sk_buff *skb)
360962306a36Sopenharmony_ci{
361062306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
361162306a36Sopenharmony_ci		int ip_hdr_len = 0;
361262306a36Sopenharmony_ci		u8 l4_proto;
361362306a36Sopenharmony_ci		__be16 l3_proto = vlan_get_protocol(skb);
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci		if (l3_proto == htons(ETH_P_IP)) {
361662306a36Sopenharmony_ci			struct iphdr *ip4h = ip_hdr(skb);
361762306a36Sopenharmony_ci
361862306a36Sopenharmony_ci			/* Calculate IPv4 checksum and L4 checksum */
361962306a36Sopenharmony_ci			ip_hdr_len = ip4h->ihl;
362062306a36Sopenharmony_ci			l4_proto = ip4h->protocol;
362162306a36Sopenharmony_ci		} else if (l3_proto == htons(ETH_P_IPV6)) {
362262306a36Sopenharmony_ci			struct ipv6hdr *ip6h = ipv6_hdr(skb);
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ci			/* Read l4_protocol from one of IPv6 extra headers */
362562306a36Sopenharmony_ci			if (skb_network_header_len(skb) > 0)
362662306a36Sopenharmony_ci				ip_hdr_len = (skb_network_header_len(skb) >> 2);
362762306a36Sopenharmony_ci			l4_proto = ip6h->nexthdr;
362862306a36Sopenharmony_ci		} else {
362962306a36Sopenharmony_ci			return MVPP2_TXD_L4_CSUM_NOT;
363062306a36Sopenharmony_ci		}
363162306a36Sopenharmony_ci
363262306a36Sopenharmony_ci		return mvpp2_txq_desc_csum(skb_network_offset(skb),
363362306a36Sopenharmony_ci					   l3_proto, ip_hdr_len, l4_proto);
363462306a36Sopenharmony_ci	}
363562306a36Sopenharmony_ci
363662306a36Sopenharmony_ci	return MVPP2_TXD_L4_CSUM_NOT | MVPP2_TXD_IP_CSUM_DISABLE;
363762306a36Sopenharmony_ci}
363862306a36Sopenharmony_ci
363962306a36Sopenharmony_cistatic void mvpp2_xdp_finish_tx(struct mvpp2_port *port, u16 txq_id, int nxmit, int nxmit_byte)
364062306a36Sopenharmony_ci{
364162306a36Sopenharmony_ci	unsigned int thread = mvpp2_cpu_to_thread(port->priv, smp_processor_id());
364262306a36Sopenharmony_ci	struct mvpp2_tx_queue *aggr_txq;
364362306a36Sopenharmony_ci	struct mvpp2_txq_pcpu *txq_pcpu;
364462306a36Sopenharmony_ci	struct mvpp2_tx_queue *txq;
364562306a36Sopenharmony_ci	struct netdev_queue *nq;
364662306a36Sopenharmony_ci
364762306a36Sopenharmony_ci	txq = port->txqs[txq_id];
364862306a36Sopenharmony_ci	txq_pcpu = per_cpu_ptr(txq->pcpu, thread);
364962306a36Sopenharmony_ci	nq = netdev_get_tx_queue(port->dev, txq_id);
365062306a36Sopenharmony_ci	aggr_txq = &port->priv->aggr_txqs[thread];
365162306a36Sopenharmony_ci
365262306a36Sopenharmony_ci	txq_pcpu->reserved_num -= nxmit;
365362306a36Sopenharmony_ci	txq_pcpu->count += nxmit;
365462306a36Sopenharmony_ci	aggr_txq->count += nxmit;
365562306a36Sopenharmony_ci
365662306a36Sopenharmony_ci	/* Enable transmit */
365762306a36Sopenharmony_ci	wmb();
365862306a36Sopenharmony_ci	mvpp2_aggr_txq_pend_desc_add(port, nxmit);
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci	if (txq_pcpu->count >= txq_pcpu->stop_threshold)
366162306a36Sopenharmony_ci		netif_tx_stop_queue(nq);
366262306a36Sopenharmony_ci
366362306a36Sopenharmony_ci	/* Finalize TX processing */
366462306a36Sopenharmony_ci	if (!port->has_tx_irqs && txq_pcpu->count >= txq->done_pkts_coal)
366562306a36Sopenharmony_ci		mvpp2_txq_done(port, txq, txq_pcpu);
366662306a36Sopenharmony_ci}
366762306a36Sopenharmony_ci
366862306a36Sopenharmony_cistatic int
366962306a36Sopenharmony_cimvpp2_xdp_submit_frame(struct mvpp2_port *port, u16 txq_id,
367062306a36Sopenharmony_ci		       struct xdp_frame *xdpf, bool dma_map)
367162306a36Sopenharmony_ci{
367262306a36Sopenharmony_ci	unsigned int thread = mvpp2_cpu_to_thread(port->priv, smp_processor_id());
367362306a36Sopenharmony_ci	u32 tx_cmd = MVPP2_TXD_L4_CSUM_NOT | MVPP2_TXD_IP_CSUM_DISABLE |
367462306a36Sopenharmony_ci		     MVPP2_TXD_F_DESC | MVPP2_TXD_L_DESC;
367562306a36Sopenharmony_ci	enum mvpp2_tx_buf_type buf_type;
367662306a36Sopenharmony_ci	struct mvpp2_txq_pcpu *txq_pcpu;
367762306a36Sopenharmony_ci	struct mvpp2_tx_queue *aggr_txq;
367862306a36Sopenharmony_ci	struct mvpp2_tx_desc *tx_desc;
367962306a36Sopenharmony_ci	struct mvpp2_tx_queue *txq;
368062306a36Sopenharmony_ci	int ret = MVPP2_XDP_TX;
368162306a36Sopenharmony_ci	dma_addr_t dma_addr;
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_ci	txq = port->txqs[txq_id];
368462306a36Sopenharmony_ci	txq_pcpu = per_cpu_ptr(txq->pcpu, thread);
368562306a36Sopenharmony_ci	aggr_txq = &port->priv->aggr_txqs[thread];
368662306a36Sopenharmony_ci
368762306a36Sopenharmony_ci	/* Check number of available descriptors */
368862306a36Sopenharmony_ci	if (mvpp2_aggr_desc_num_check(port, aggr_txq, 1) ||
368962306a36Sopenharmony_ci	    mvpp2_txq_reserved_desc_num_proc(port, txq, txq_pcpu, 1)) {
369062306a36Sopenharmony_ci		ret = MVPP2_XDP_DROPPED;
369162306a36Sopenharmony_ci		goto out;
369262306a36Sopenharmony_ci	}
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci	/* Get a descriptor for the first part of the packet */
369562306a36Sopenharmony_ci	tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
369662306a36Sopenharmony_ci	mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
369762306a36Sopenharmony_ci	mvpp2_txdesc_size_set(port, tx_desc, xdpf->len);
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci	if (dma_map) {
370062306a36Sopenharmony_ci		/* XDP_REDIRECT or AF_XDP */
370162306a36Sopenharmony_ci		dma_addr = dma_map_single(port->dev->dev.parent, xdpf->data,
370262306a36Sopenharmony_ci					  xdpf->len, DMA_TO_DEVICE);
370362306a36Sopenharmony_ci
370462306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(port->dev->dev.parent, dma_addr))) {
370562306a36Sopenharmony_ci			mvpp2_txq_desc_put(txq);
370662306a36Sopenharmony_ci			ret = MVPP2_XDP_DROPPED;
370762306a36Sopenharmony_ci			goto out;
370862306a36Sopenharmony_ci		}
370962306a36Sopenharmony_ci
371062306a36Sopenharmony_ci		buf_type = MVPP2_TYPE_XDP_NDO;
371162306a36Sopenharmony_ci	} else {
371262306a36Sopenharmony_ci		/* XDP_TX */
371362306a36Sopenharmony_ci		struct page *page = virt_to_page(xdpf->data);
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_ci		dma_addr = page_pool_get_dma_addr(page) +
371662306a36Sopenharmony_ci			   sizeof(*xdpf) + xdpf->headroom;
371762306a36Sopenharmony_ci		dma_sync_single_for_device(port->dev->dev.parent, dma_addr,
371862306a36Sopenharmony_ci					   xdpf->len, DMA_BIDIRECTIONAL);
371962306a36Sopenharmony_ci
372062306a36Sopenharmony_ci		buf_type = MVPP2_TYPE_XDP_TX;
372162306a36Sopenharmony_ci	}
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci	mvpp2_txdesc_dma_addr_set(port, tx_desc, dma_addr);
372462306a36Sopenharmony_ci
372562306a36Sopenharmony_ci	mvpp2_txdesc_cmd_set(port, tx_desc, tx_cmd);
372662306a36Sopenharmony_ci	mvpp2_txq_inc_put(port, txq_pcpu, xdpf, tx_desc, buf_type);
372762306a36Sopenharmony_ci
372862306a36Sopenharmony_ciout:
372962306a36Sopenharmony_ci	return ret;
373062306a36Sopenharmony_ci}
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_cistatic int
373362306a36Sopenharmony_cimvpp2_xdp_xmit_back(struct mvpp2_port *port, struct xdp_buff *xdp)
373462306a36Sopenharmony_ci{
373562306a36Sopenharmony_ci	struct mvpp2_pcpu_stats *stats = this_cpu_ptr(port->stats);
373662306a36Sopenharmony_ci	struct xdp_frame *xdpf;
373762306a36Sopenharmony_ci	u16 txq_id;
373862306a36Sopenharmony_ci	int ret;
373962306a36Sopenharmony_ci
374062306a36Sopenharmony_ci	xdpf = xdp_convert_buff_to_frame(xdp);
374162306a36Sopenharmony_ci	if (unlikely(!xdpf))
374262306a36Sopenharmony_ci		return MVPP2_XDP_DROPPED;
374362306a36Sopenharmony_ci
374462306a36Sopenharmony_ci	/* The first of the TX queues are used for XPS,
374562306a36Sopenharmony_ci	 * the second half for XDP_TX
374662306a36Sopenharmony_ci	 */
374762306a36Sopenharmony_ci	txq_id = mvpp2_cpu_to_thread(port->priv, smp_processor_id()) + (port->ntxqs / 2);
374862306a36Sopenharmony_ci
374962306a36Sopenharmony_ci	ret = mvpp2_xdp_submit_frame(port, txq_id, xdpf, false);
375062306a36Sopenharmony_ci	if (ret == MVPP2_XDP_TX) {
375162306a36Sopenharmony_ci		u64_stats_update_begin(&stats->syncp);
375262306a36Sopenharmony_ci		stats->tx_bytes += xdpf->len;
375362306a36Sopenharmony_ci		stats->tx_packets++;
375462306a36Sopenharmony_ci		stats->xdp_tx++;
375562306a36Sopenharmony_ci		u64_stats_update_end(&stats->syncp);
375662306a36Sopenharmony_ci
375762306a36Sopenharmony_ci		mvpp2_xdp_finish_tx(port, txq_id, 1, xdpf->len);
375862306a36Sopenharmony_ci	} else {
375962306a36Sopenharmony_ci		u64_stats_update_begin(&stats->syncp);
376062306a36Sopenharmony_ci		stats->xdp_tx_err++;
376162306a36Sopenharmony_ci		u64_stats_update_end(&stats->syncp);
376262306a36Sopenharmony_ci	}
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_ci	return ret;
376562306a36Sopenharmony_ci}
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_cistatic int
376862306a36Sopenharmony_cimvpp2_xdp_xmit(struct net_device *dev, int num_frame,
376962306a36Sopenharmony_ci	       struct xdp_frame **frames, u32 flags)
377062306a36Sopenharmony_ci{
377162306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
377262306a36Sopenharmony_ci	int i, nxmit_byte = 0, nxmit = 0;
377362306a36Sopenharmony_ci	struct mvpp2_pcpu_stats *stats;
377462306a36Sopenharmony_ci	u16 txq_id;
377562306a36Sopenharmony_ci	u32 ret;
377662306a36Sopenharmony_ci
377762306a36Sopenharmony_ci	if (unlikely(test_bit(0, &port->state)))
377862306a36Sopenharmony_ci		return -ENETDOWN;
377962306a36Sopenharmony_ci
378062306a36Sopenharmony_ci	if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
378162306a36Sopenharmony_ci		return -EINVAL;
378262306a36Sopenharmony_ci
378362306a36Sopenharmony_ci	/* The first of the TX queues are used for XPS,
378462306a36Sopenharmony_ci	 * the second half for XDP_TX
378562306a36Sopenharmony_ci	 */
378662306a36Sopenharmony_ci	txq_id = mvpp2_cpu_to_thread(port->priv, smp_processor_id()) + (port->ntxqs / 2);
378762306a36Sopenharmony_ci
378862306a36Sopenharmony_ci	for (i = 0; i < num_frame; i++) {
378962306a36Sopenharmony_ci		ret = mvpp2_xdp_submit_frame(port, txq_id, frames[i], true);
379062306a36Sopenharmony_ci		if (ret != MVPP2_XDP_TX)
379162306a36Sopenharmony_ci			break;
379262306a36Sopenharmony_ci
379362306a36Sopenharmony_ci		nxmit_byte += frames[i]->len;
379462306a36Sopenharmony_ci		nxmit++;
379562306a36Sopenharmony_ci	}
379662306a36Sopenharmony_ci
379762306a36Sopenharmony_ci	if (likely(nxmit > 0))
379862306a36Sopenharmony_ci		mvpp2_xdp_finish_tx(port, txq_id, nxmit, nxmit_byte);
379962306a36Sopenharmony_ci
380062306a36Sopenharmony_ci	stats = this_cpu_ptr(port->stats);
380162306a36Sopenharmony_ci	u64_stats_update_begin(&stats->syncp);
380262306a36Sopenharmony_ci	stats->tx_bytes += nxmit_byte;
380362306a36Sopenharmony_ci	stats->tx_packets += nxmit;
380462306a36Sopenharmony_ci	stats->xdp_xmit += nxmit;
380562306a36Sopenharmony_ci	stats->xdp_xmit_err += num_frame - nxmit;
380662306a36Sopenharmony_ci	u64_stats_update_end(&stats->syncp);
380762306a36Sopenharmony_ci
380862306a36Sopenharmony_ci	return nxmit;
380962306a36Sopenharmony_ci}
381062306a36Sopenharmony_ci
381162306a36Sopenharmony_cistatic int
381262306a36Sopenharmony_cimvpp2_run_xdp(struct mvpp2_port *port, struct bpf_prog *prog,
381362306a36Sopenharmony_ci	      struct xdp_buff *xdp, struct page_pool *pp,
381462306a36Sopenharmony_ci	      struct mvpp2_pcpu_stats *stats)
381562306a36Sopenharmony_ci{
381662306a36Sopenharmony_ci	unsigned int len, sync, err;
381762306a36Sopenharmony_ci	struct page *page;
381862306a36Sopenharmony_ci	u32 ret, act;
381962306a36Sopenharmony_ci
382062306a36Sopenharmony_ci	len = xdp->data_end - xdp->data_hard_start - MVPP2_SKB_HEADROOM;
382162306a36Sopenharmony_ci	act = bpf_prog_run_xdp(prog, xdp);
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_ci	/* Due xdp_adjust_tail: DMA sync for_device cover max len CPU touch */
382462306a36Sopenharmony_ci	sync = xdp->data_end - xdp->data_hard_start - MVPP2_SKB_HEADROOM;
382562306a36Sopenharmony_ci	sync = max(sync, len);
382662306a36Sopenharmony_ci
382762306a36Sopenharmony_ci	switch (act) {
382862306a36Sopenharmony_ci	case XDP_PASS:
382962306a36Sopenharmony_ci		stats->xdp_pass++;
383062306a36Sopenharmony_ci		ret = MVPP2_XDP_PASS;
383162306a36Sopenharmony_ci		break;
383262306a36Sopenharmony_ci	case XDP_REDIRECT:
383362306a36Sopenharmony_ci		err = xdp_do_redirect(port->dev, xdp, prog);
383462306a36Sopenharmony_ci		if (unlikely(err)) {
383562306a36Sopenharmony_ci			ret = MVPP2_XDP_DROPPED;
383662306a36Sopenharmony_ci			page = virt_to_head_page(xdp->data);
383762306a36Sopenharmony_ci			page_pool_put_page(pp, page, sync, true);
383862306a36Sopenharmony_ci		} else {
383962306a36Sopenharmony_ci			ret = MVPP2_XDP_REDIR;
384062306a36Sopenharmony_ci			stats->xdp_redirect++;
384162306a36Sopenharmony_ci		}
384262306a36Sopenharmony_ci		break;
384362306a36Sopenharmony_ci	case XDP_TX:
384462306a36Sopenharmony_ci		ret = mvpp2_xdp_xmit_back(port, xdp);
384562306a36Sopenharmony_ci		if (ret != MVPP2_XDP_TX) {
384662306a36Sopenharmony_ci			page = virt_to_head_page(xdp->data);
384762306a36Sopenharmony_ci			page_pool_put_page(pp, page, sync, true);
384862306a36Sopenharmony_ci		}
384962306a36Sopenharmony_ci		break;
385062306a36Sopenharmony_ci	default:
385162306a36Sopenharmony_ci		bpf_warn_invalid_xdp_action(port->dev, prog, act);
385262306a36Sopenharmony_ci		fallthrough;
385362306a36Sopenharmony_ci	case XDP_ABORTED:
385462306a36Sopenharmony_ci		trace_xdp_exception(port->dev, prog, act);
385562306a36Sopenharmony_ci		fallthrough;
385662306a36Sopenharmony_ci	case XDP_DROP:
385762306a36Sopenharmony_ci		page = virt_to_head_page(xdp->data);
385862306a36Sopenharmony_ci		page_pool_put_page(pp, page, sync, true);
385962306a36Sopenharmony_ci		ret = MVPP2_XDP_DROPPED;
386062306a36Sopenharmony_ci		stats->xdp_drop++;
386162306a36Sopenharmony_ci		break;
386262306a36Sopenharmony_ci	}
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci	return ret;
386562306a36Sopenharmony_ci}
386662306a36Sopenharmony_ci
386762306a36Sopenharmony_cistatic void mvpp2_buff_hdr_pool_put(struct mvpp2_port *port, struct mvpp2_rx_desc *rx_desc,
386862306a36Sopenharmony_ci				    int pool, u32 rx_status)
386962306a36Sopenharmony_ci{
387062306a36Sopenharmony_ci	phys_addr_t phys_addr, phys_addr_next;
387162306a36Sopenharmony_ci	dma_addr_t dma_addr, dma_addr_next;
387262306a36Sopenharmony_ci	struct mvpp2_buff_hdr *buff_hdr;
387362306a36Sopenharmony_ci
387462306a36Sopenharmony_ci	phys_addr = mvpp2_rxdesc_dma_addr_get(port, rx_desc);
387562306a36Sopenharmony_ci	dma_addr = mvpp2_rxdesc_cookie_get(port, rx_desc);
387662306a36Sopenharmony_ci
387762306a36Sopenharmony_ci	do {
387862306a36Sopenharmony_ci		buff_hdr = (struct mvpp2_buff_hdr *)phys_to_virt(phys_addr);
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ci		phys_addr_next = le32_to_cpu(buff_hdr->next_phys_addr);
388162306a36Sopenharmony_ci		dma_addr_next = le32_to_cpu(buff_hdr->next_dma_addr);
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci		if (port->priv->hw_version >= MVPP22) {
388462306a36Sopenharmony_ci			phys_addr_next |= ((u64)buff_hdr->next_phys_addr_high << 32);
388562306a36Sopenharmony_ci			dma_addr_next |= ((u64)buff_hdr->next_dma_addr_high << 32);
388662306a36Sopenharmony_ci		}
388762306a36Sopenharmony_ci
388862306a36Sopenharmony_ci		mvpp2_bm_pool_put(port, pool, dma_addr, phys_addr);
388962306a36Sopenharmony_ci
389062306a36Sopenharmony_ci		phys_addr = phys_addr_next;
389162306a36Sopenharmony_ci		dma_addr = dma_addr_next;
389262306a36Sopenharmony_ci
389362306a36Sopenharmony_ci	} while (!MVPP2_B_HDR_INFO_IS_LAST(le16_to_cpu(buff_hdr->info)));
389462306a36Sopenharmony_ci}
389562306a36Sopenharmony_ci
389662306a36Sopenharmony_ci/* Main rx processing */
389762306a36Sopenharmony_cistatic int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi,
389862306a36Sopenharmony_ci		    int rx_todo, struct mvpp2_rx_queue *rxq)
389962306a36Sopenharmony_ci{
390062306a36Sopenharmony_ci	struct net_device *dev = port->dev;
390162306a36Sopenharmony_ci	struct mvpp2_pcpu_stats ps = {};
390262306a36Sopenharmony_ci	enum dma_data_direction dma_dir;
390362306a36Sopenharmony_ci	struct bpf_prog *xdp_prog;
390462306a36Sopenharmony_ci	struct xdp_buff xdp;
390562306a36Sopenharmony_ci	int rx_received;
390662306a36Sopenharmony_ci	int rx_done = 0;
390762306a36Sopenharmony_ci	u32 xdp_ret = 0;
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci	xdp_prog = READ_ONCE(port->xdp_prog);
391062306a36Sopenharmony_ci
391162306a36Sopenharmony_ci	/* Get number of received packets and clamp the to-do */
391262306a36Sopenharmony_ci	rx_received = mvpp2_rxq_received(port, rxq->id);
391362306a36Sopenharmony_ci	if (rx_todo > rx_received)
391462306a36Sopenharmony_ci		rx_todo = rx_received;
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_ci	while (rx_done < rx_todo) {
391762306a36Sopenharmony_ci		struct mvpp2_rx_desc *rx_desc = mvpp2_rxq_next_desc_get(rxq);
391862306a36Sopenharmony_ci		struct mvpp2_bm_pool *bm_pool;
391962306a36Sopenharmony_ci		struct page_pool *pp = NULL;
392062306a36Sopenharmony_ci		struct sk_buff *skb;
392162306a36Sopenharmony_ci		unsigned int frag_size;
392262306a36Sopenharmony_ci		dma_addr_t dma_addr;
392362306a36Sopenharmony_ci		phys_addr_t phys_addr;
392462306a36Sopenharmony_ci		u32 rx_status, timestamp;
392562306a36Sopenharmony_ci		int pool, rx_bytes, err, ret;
392662306a36Sopenharmony_ci		struct page *page;
392762306a36Sopenharmony_ci		void *data;
392862306a36Sopenharmony_ci
392962306a36Sopenharmony_ci		phys_addr = mvpp2_rxdesc_cookie_get(port, rx_desc);
393062306a36Sopenharmony_ci		data = (void *)phys_to_virt(phys_addr);
393162306a36Sopenharmony_ci		page = virt_to_page(data);
393262306a36Sopenharmony_ci		prefetch(page);
393362306a36Sopenharmony_ci
393462306a36Sopenharmony_ci		rx_done++;
393562306a36Sopenharmony_ci		rx_status = mvpp2_rxdesc_status_get(port, rx_desc);
393662306a36Sopenharmony_ci		rx_bytes = mvpp2_rxdesc_size_get(port, rx_desc);
393762306a36Sopenharmony_ci		rx_bytes -= MVPP2_MH_SIZE;
393862306a36Sopenharmony_ci		dma_addr = mvpp2_rxdesc_dma_addr_get(port, rx_desc);
393962306a36Sopenharmony_ci
394062306a36Sopenharmony_ci		pool = (rx_status & MVPP2_RXD_BM_POOL_ID_MASK) >>
394162306a36Sopenharmony_ci			MVPP2_RXD_BM_POOL_ID_OFFS;
394262306a36Sopenharmony_ci		bm_pool = &port->priv->bm_pools[pool];
394362306a36Sopenharmony_ci
394462306a36Sopenharmony_ci		if (port->priv->percpu_pools) {
394562306a36Sopenharmony_ci			pp = port->priv->page_pool[pool];
394662306a36Sopenharmony_ci			dma_dir = page_pool_get_dma_dir(pp);
394762306a36Sopenharmony_ci		} else {
394862306a36Sopenharmony_ci			dma_dir = DMA_FROM_DEVICE;
394962306a36Sopenharmony_ci		}
395062306a36Sopenharmony_ci
395162306a36Sopenharmony_ci		dma_sync_single_for_cpu(dev->dev.parent, dma_addr,
395262306a36Sopenharmony_ci					rx_bytes + MVPP2_MH_SIZE,
395362306a36Sopenharmony_ci					dma_dir);
395462306a36Sopenharmony_ci
395562306a36Sopenharmony_ci		/* Buffer header not supported */
395662306a36Sopenharmony_ci		if (rx_status & MVPP2_RXD_BUF_HDR)
395762306a36Sopenharmony_ci			goto err_drop_frame;
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_ci		/* In case of an error, release the requested buffer pointer
396062306a36Sopenharmony_ci		 * to the Buffer Manager. This request process is controlled
396162306a36Sopenharmony_ci		 * by the hardware, and the information about the buffer is
396262306a36Sopenharmony_ci		 * comprised by the RX descriptor.
396362306a36Sopenharmony_ci		 */
396462306a36Sopenharmony_ci		if (rx_status & MVPP2_RXD_ERR_SUMMARY)
396562306a36Sopenharmony_ci			goto err_drop_frame;
396662306a36Sopenharmony_ci
396762306a36Sopenharmony_ci		/* Prefetch header */
396862306a36Sopenharmony_ci		prefetch(data + MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM);
396962306a36Sopenharmony_ci
397062306a36Sopenharmony_ci		if (bm_pool->frag_size > PAGE_SIZE)
397162306a36Sopenharmony_ci			frag_size = 0;
397262306a36Sopenharmony_ci		else
397362306a36Sopenharmony_ci			frag_size = bm_pool->frag_size;
397462306a36Sopenharmony_ci
397562306a36Sopenharmony_ci		if (xdp_prog) {
397662306a36Sopenharmony_ci			struct xdp_rxq_info *xdp_rxq;
397762306a36Sopenharmony_ci
397862306a36Sopenharmony_ci			if (bm_pool->pkt_size == MVPP2_BM_SHORT_PKT_SIZE)
397962306a36Sopenharmony_ci				xdp_rxq = &rxq->xdp_rxq_short;
398062306a36Sopenharmony_ci			else
398162306a36Sopenharmony_ci				xdp_rxq = &rxq->xdp_rxq_long;
398262306a36Sopenharmony_ci
398362306a36Sopenharmony_ci			xdp_init_buff(&xdp, PAGE_SIZE, xdp_rxq);
398462306a36Sopenharmony_ci			xdp_prepare_buff(&xdp, data,
398562306a36Sopenharmony_ci					 MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM,
398662306a36Sopenharmony_ci					 rx_bytes, false);
398762306a36Sopenharmony_ci
398862306a36Sopenharmony_ci			ret = mvpp2_run_xdp(port, xdp_prog, &xdp, pp, &ps);
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_ci			if (ret) {
399162306a36Sopenharmony_ci				xdp_ret |= ret;
399262306a36Sopenharmony_ci				err = mvpp2_rx_refill(port, bm_pool, pp, pool);
399362306a36Sopenharmony_ci				if (err) {
399462306a36Sopenharmony_ci					netdev_err(port->dev, "failed to refill BM pools\n");
399562306a36Sopenharmony_ci					goto err_drop_frame;
399662306a36Sopenharmony_ci				}
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci				ps.rx_packets++;
399962306a36Sopenharmony_ci				ps.rx_bytes += rx_bytes;
400062306a36Sopenharmony_ci				continue;
400162306a36Sopenharmony_ci			}
400262306a36Sopenharmony_ci		}
400362306a36Sopenharmony_ci
400462306a36Sopenharmony_ci		skb = build_skb(data, frag_size);
400562306a36Sopenharmony_ci		if (!skb) {
400662306a36Sopenharmony_ci			netdev_warn(port->dev, "skb build failed\n");
400762306a36Sopenharmony_ci			goto err_drop_frame;
400862306a36Sopenharmony_ci		}
400962306a36Sopenharmony_ci
401062306a36Sopenharmony_ci		/* If we have RX hardware timestamping enabled, grab the
401162306a36Sopenharmony_ci		 * timestamp from the queue and convert.
401262306a36Sopenharmony_ci		 */
401362306a36Sopenharmony_ci		if (mvpp22_rx_hwtstamping(port)) {
401462306a36Sopenharmony_ci			timestamp = le32_to_cpu(rx_desc->pp22.timestamp);
401562306a36Sopenharmony_ci			mvpp22_tai_tstamp(port->priv->tai, timestamp,
401662306a36Sopenharmony_ci					 skb_hwtstamps(skb));
401762306a36Sopenharmony_ci		}
401862306a36Sopenharmony_ci
401962306a36Sopenharmony_ci		err = mvpp2_rx_refill(port, bm_pool, pp, pool);
402062306a36Sopenharmony_ci		if (err) {
402162306a36Sopenharmony_ci			netdev_err(port->dev, "failed to refill BM pools\n");
402262306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
402362306a36Sopenharmony_ci			goto err_drop_frame;
402462306a36Sopenharmony_ci		}
402562306a36Sopenharmony_ci
402662306a36Sopenharmony_ci		if (pp)
402762306a36Sopenharmony_ci			skb_mark_for_recycle(skb);
402862306a36Sopenharmony_ci		else
402962306a36Sopenharmony_ci			dma_unmap_single_attrs(dev->dev.parent, dma_addr,
403062306a36Sopenharmony_ci					       bm_pool->buf_size, DMA_FROM_DEVICE,
403162306a36Sopenharmony_ci					       DMA_ATTR_SKIP_CPU_SYNC);
403262306a36Sopenharmony_ci
403362306a36Sopenharmony_ci		ps.rx_packets++;
403462306a36Sopenharmony_ci		ps.rx_bytes += rx_bytes;
403562306a36Sopenharmony_ci
403662306a36Sopenharmony_ci		skb_reserve(skb, MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM);
403762306a36Sopenharmony_ci		skb_put(skb, rx_bytes);
403862306a36Sopenharmony_ci		skb->ip_summed = mvpp2_rx_csum(port, rx_status);
403962306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, dev);
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci		napi_gro_receive(napi, skb);
404262306a36Sopenharmony_ci		continue;
404362306a36Sopenharmony_ci
404462306a36Sopenharmony_cierr_drop_frame:
404562306a36Sopenharmony_ci		dev->stats.rx_errors++;
404662306a36Sopenharmony_ci		mvpp2_rx_error(port, rx_desc);
404762306a36Sopenharmony_ci		/* Return the buffer to the pool */
404862306a36Sopenharmony_ci		if (rx_status & MVPP2_RXD_BUF_HDR)
404962306a36Sopenharmony_ci			mvpp2_buff_hdr_pool_put(port, rx_desc, pool, rx_status);
405062306a36Sopenharmony_ci		else
405162306a36Sopenharmony_ci			mvpp2_bm_pool_put(port, pool, dma_addr, phys_addr);
405262306a36Sopenharmony_ci	}
405362306a36Sopenharmony_ci
405462306a36Sopenharmony_ci	if (xdp_ret & MVPP2_XDP_REDIR)
405562306a36Sopenharmony_ci		xdp_do_flush_map();
405662306a36Sopenharmony_ci
405762306a36Sopenharmony_ci	if (ps.rx_packets) {
405862306a36Sopenharmony_ci		struct mvpp2_pcpu_stats *stats = this_cpu_ptr(port->stats);
405962306a36Sopenharmony_ci
406062306a36Sopenharmony_ci		u64_stats_update_begin(&stats->syncp);
406162306a36Sopenharmony_ci		stats->rx_packets += ps.rx_packets;
406262306a36Sopenharmony_ci		stats->rx_bytes   += ps.rx_bytes;
406362306a36Sopenharmony_ci		/* xdp */
406462306a36Sopenharmony_ci		stats->xdp_redirect += ps.xdp_redirect;
406562306a36Sopenharmony_ci		stats->xdp_pass += ps.xdp_pass;
406662306a36Sopenharmony_ci		stats->xdp_drop += ps.xdp_drop;
406762306a36Sopenharmony_ci		u64_stats_update_end(&stats->syncp);
406862306a36Sopenharmony_ci	}
406962306a36Sopenharmony_ci
407062306a36Sopenharmony_ci	/* Update Rx queue management counters */
407162306a36Sopenharmony_ci	wmb();
407262306a36Sopenharmony_ci	mvpp2_rxq_status_update(port, rxq->id, rx_done, rx_done);
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci	return rx_todo;
407562306a36Sopenharmony_ci}
407662306a36Sopenharmony_ci
407762306a36Sopenharmony_cistatic inline void
407862306a36Sopenharmony_citx_desc_unmap_put(struct mvpp2_port *port, struct mvpp2_tx_queue *txq,
407962306a36Sopenharmony_ci		  struct mvpp2_tx_desc *desc)
408062306a36Sopenharmony_ci{
408162306a36Sopenharmony_ci	unsigned int thread = mvpp2_cpu_to_thread(port->priv, smp_processor_id());
408262306a36Sopenharmony_ci	struct mvpp2_txq_pcpu *txq_pcpu = per_cpu_ptr(txq->pcpu, thread);
408362306a36Sopenharmony_ci
408462306a36Sopenharmony_ci	dma_addr_t buf_dma_addr =
408562306a36Sopenharmony_ci		mvpp2_txdesc_dma_addr_get(port, desc);
408662306a36Sopenharmony_ci	size_t buf_sz =
408762306a36Sopenharmony_ci		mvpp2_txdesc_size_get(port, desc);
408862306a36Sopenharmony_ci	if (!IS_TSO_HEADER(txq_pcpu, buf_dma_addr))
408962306a36Sopenharmony_ci		dma_unmap_single(port->dev->dev.parent, buf_dma_addr,
409062306a36Sopenharmony_ci				 buf_sz, DMA_TO_DEVICE);
409162306a36Sopenharmony_ci	mvpp2_txq_desc_put(txq);
409262306a36Sopenharmony_ci}
409362306a36Sopenharmony_ci
409462306a36Sopenharmony_cistatic void mvpp2_txdesc_clear_ptp(struct mvpp2_port *port,
409562306a36Sopenharmony_ci				   struct mvpp2_tx_desc *desc)
409662306a36Sopenharmony_ci{
409762306a36Sopenharmony_ci	/* We only need to clear the low bits */
409862306a36Sopenharmony_ci	if (port->priv->hw_version >= MVPP22)
409962306a36Sopenharmony_ci		desc->pp22.ptp_descriptor &=
410062306a36Sopenharmony_ci			cpu_to_le32(~MVPP22_PTP_DESC_MASK_LOW);
410162306a36Sopenharmony_ci}
410262306a36Sopenharmony_ci
410362306a36Sopenharmony_cistatic bool mvpp2_tx_hw_tstamp(struct mvpp2_port *port,
410462306a36Sopenharmony_ci			       struct mvpp2_tx_desc *tx_desc,
410562306a36Sopenharmony_ci			       struct sk_buff *skb)
410662306a36Sopenharmony_ci{
410762306a36Sopenharmony_ci	struct mvpp2_hwtstamp_queue *queue;
410862306a36Sopenharmony_ci	unsigned int mtype, type, i;
410962306a36Sopenharmony_ci	struct ptp_header *hdr;
411062306a36Sopenharmony_ci	u64 ptpdesc;
411162306a36Sopenharmony_ci
411262306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21 ||
411362306a36Sopenharmony_ci	    port->tx_hwtstamp_type == HWTSTAMP_TX_OFF)
411462306a36Sopenharmony_ci		return false;
411562306a36Sopenharmony_ci
411662306a36Sopenharmony_ci	type = ptp_classify_raw(skb);
411762306a36Sopenharmony_ci	if (!type)
411862306a36Sopenharmony_ci		return false;
411962306a36Sopenharmony_ci
412062306a36Sopenharmony_ci	hdr = ptp_parse_header(skb, type);
412162306a36Sopenharmony_ci	if (!hdr)
412262306a36Sopenharmony_ci		return false;
412362306a36Sopenharmony_ci
412462306a36Sopenharmony_ci	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
412562306a36Sopenharmony_ci
412662306a36Sopenharmony_ci	ptpdesc = MVPP22_PTP_MACTIMESTAMPINGEN |
412762306a36Sopenharmony_ci		  MVPP22_PTP_ACTION_CAPTURE;
412862306a36Sopenharmony_ci	queue = &port->tx_hwtstamp_queue[0];
412962306a36Sopenharmony_ci
413062306a36Sopenharmony_ci	switch (type & PTP_CLASS_VMASK) {
413162306a36Sopenharmony_ci	case PTP_CLASS_V1:
413262306a36Sopenharmony_ci		ptpdesc |= MVPP22_PTP_PACKETFORMAT(MVPP22_PTP_PKT_FMT_PTPV1);
413362306a36Sopenharmony_ci		break;
413462306a36Sopenharmony_ci
413562306a36Sopenharmony_ci	case PTP_CLASS_V2:
413662306a36Sopenharmony_ci		ptpdesc |= MVPP22_PTP_PACKETFORMAT(MVPP22_PTP_PKT_FMT_PTPV2);
413762306a36Sopenharmony_ci		mtype = hdr->tsmt & 15;
413862306a36Sopenharmony_ci		/* Direct PTP Sync messages to queue 1 */
413962306a36Sopenharmony_ci		if (mtype == 0) {
414062306a36Sopenharmony_ci			ptpdesc |= MVPP22_PTP_TIMESTAMPQUEUESELECT;
414162306a36Sopenharmony_ci			queue = &port->tx_hwtstamp_queue[1];
414262306a36Sopenharmony_ci		}
414362306a36Sopenharmony_ci		break;
414462306a36Sopenharmony_ci	}
414562306a36Sopenharmony_ci
414662306a36Sopenharmony_ci	/* Take a reference on the skb and insert into our queue */
414762306a36Sopenharmony_ci	i = queue->next;
414862306a36Sopenharmony_ci	queue->next = (i + 1) & 31;
414962306a36Sopenharmony_ci	if (queue->skb[i])
415062306a36Sopenharmony_ci		dev_kfree_skb_any(queue->skb[i]);
415162306a36Sopenharmony_ci	queue->skb[i] = skb_get(skb);
415262306a36Sopenharmony_ci
415362306a36Sopenharmony_ci	ptpdesc |= MVPP22_PTP_TIMESTAMPENTRYID(i);
415462306a36Sopenharmony_ci
415562306a36Sopenharmony_ci	/*
415662306a36Sopenharmony_ci	 * 3:0		- PTPAction
415762306a36Sopenharmony_ci	 * 6:4		- PTPPacketFormat
415862306a36Sopenharmony_ci	 * 7		- PTP_CF_WraparoundCheckEn
415962306a36Sopenharmony_ci	 * 9:8		- IngressTimestampSeconds[1:0]
416062306a36Sopenharmony_ci	 * 10		- Reserved
416162306a36Sopenharmony_ci	 * 11		- MACTimestampingEn
416262306a36Sopenharmony_ci	 * 17:12	- PTP_TimestampQueueEntryID[5:0]
416362306a36Sopenharmony_ci	 * 18		- PTPTimestampQueueSelect
416462306a36Sopenharmony_ci	 * 19		- UDPChecksumUpdateEn
416562306a36Sopenharmony_ci	 * 27:20	- TimestampOffset
416662306a36Sopenharmony_ci	 *			PTP, NTPTransmit, OWAMP/TWAMP - L3 to PTP header
416762306a36Sopenharmony_ci	 *			NTPTs, Y.1731 - L3 to timestamp entry
416862306a36Sopenharmony_ci	 * 35:28	- UDP Checksum Offset
416962306a36Sopenharmony_ci	 *
417062306a36Sopenharmony_ci	 * stored in tx descriptor bits 75:64 (11:0) and 191:168 (35:12)
417162306a36Sopenharmony_ci	 */
417262306a36Sopenharmony_ci	tx_desc->pp22.ptp_descriptor &=
417362306a36Sopenharmony_ci		cpu_to_le32(~MVPP22_PTP_DESC_MASK_LOW);
417462306a36Sopenharmony_ci	tx_desc->pp22.ptp_descriptor |=
417562306a36Sopenharmony_ci		cpu_to_le32(ptpdesc & MVPP22_PTP_DESC_MASK_LOW);
417662306a36Sopenharmony_ci	tx_desc->pp22.buf_dma_addr_ptp &= cpu_to_le64(~0xffffff0000000000ULL);
417762306a36Sopenharmony_ci	tx_desc->pp22.buf_dma_addr_ptp |= cpu_to_le64((ptpdesc >> 12) << 40);
417862306a36Sopenharmony_ci
417962306a36Sopenharmony_ci	return true;
418062306a36Sopenharmony_ci}
418162306a36Sopenharmony_ci
418262306a36Sopenharmony_ci/* Handle tx fragmentation processing */
418362306a36Sopenharmony_cistatic int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb,
418462306a36Sopenharmony_ci				 struct mvpp2_tx_queue *aggr_txq,
418562306a36Sopenharmony_ci				 struct mvpp2_tx_queue *txq)
418662306a36Sopenharmony_ci{
418762306a36Sopenharmony_ci	unsigned int thread = mvpp2_cpu_to_thread(port->priv, smp_processor_id());
418862306a36Sopenharmony_ci	struct mvpp2_txq_pcpu *txq_pcpu = per_cpu_ptr(txq->pcpu, thread);
418962306a36Sopenharmony_ci	struct mvpp2_tx_desc *tx_desc;
419062306a36Sopenharmony_ci	int i;
419162306a36Sopenharmony_ci	dma_addr_t buf_dma_addr;
419262306a36Sopenharmony_ci
419362306a36Sopenharmony_ci	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
419462306a36Sopenharmony_ci		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
419562306a36Sopenharmony_ci		void *addr = skb_frag_address(frag);
419662306a36Sopenharmony_ci
419762306a36Sopenharmony_ci		tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
419862306a36Sopenharmony_ci		mvpp2_txdesc_clear_ptp(port, tx_desc);
419962306a36Sopenharmony_ci		mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
420062306a36Sopenharmony_ci		mvpp2_txdesc_size_set(port, tx_desc, skb_frag_size(frag));
420162306a36Sopenharmony_ci
420262306a36Sopenharmony_ci		buf_dma_addr = dma_map_single(port->dev->dev.parent, addr,
420362306a36Sopenharmony_ci					      skb_frag_size(frag),
420462306a36Sopenharmony_ci					      DMA_TO_DEVICE);
420562306a36Sopenharmony_ci		if (dma_mapping_error(port->dev->dev.parent, buf_dma_addr)) {
420662306a36Sopenharmony_ci			mvpp2_txq_desc_put(txq);
420762306a36Sopenharmony_ci			goto cleanup;
420862306a36Sopenharmony_ci		}
420962306a36Sopenharmony_ci
421062306a36Sopenharmony_ci		mvpp2_txdesc_dma_addr_set(port, tx_desc, buf_dma_addr);
421162306a36Sopenharmony_ci
421262306a36Sopenharmony_ci		if (i == (skb_shinfo(skb)->nr_frags - 1)) {
421362306a36Sopenharmony_ci			/* Last descriptor */
421462306a36Sopenharmony_ci			mvpp2_txdesc_cmd_set(port, tx_desc,
421562306a36Sopenharmony_ci					     MVPP2_TXD_L_DESC);
421662306a36Sopenharmony_ci			mvpp2_txq_inc_put(port, txq_pcpu, skb, tx_desc, MVPP2_TYPE_SKB);
421762306a36Sopenharmony_ci		} else {
421862306a36Sopenharmony_ci			/* Descriptor in the middle: Not First, Not Last */
421962306a36Sopenharmony_ci			mvpp2_txdesc_cmd_set(port, tx_desc, 0);
422062306a36Sopenharmony_ci			mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc, MVPP2_TYPE_SKB);
422162306a36Sopenharmony_ci		}
422262306a36Sopenharmony_ci	}
422362306a36Sopenharmony_ci
422462306a36Sopenharmony_ci	return 0;
422562306a36Sopenharmony_cicleanup:
422662306a36Sopenharmony_ci	/* Release all descriptors that were used to map fragments of
422762306a36Sopenharmony_ci	 * this packet, as well as the corresponding DMA mappings
422862306a36Sopenharmony_ci	 */
422962306a36Sopenharmony_ci	for (i = i - 1; i >= 0; i--) {
423062306a36Sopenharmony_ci		tx_desc = txq->descs + i;
423162306a36Sopenharmony_ci		tx_desc_unmap_put(port, txq, tx_desc);
423262306a36Sopenharmony_ci	}
423362306a36Sopenharmony_ci
423462306a36Sopenharmony_ci	return -ENOMEM;
423562306a36Sopenharmony_ci}
423662306a36Sopenharmony_ci
423762306a36Sopenharmony_cistatic inline void mvpp2_tso_put_hdr(struct sk_buff *skb,
423862306a36Sopenharmony_ci				     struct net_device *dev,
423962306a36Sopenharmony_ci				     struct mvpp2_tx_queue *txq,
424062306a36Sopenharmony_ci				     struct mvpp2_tx_queue *aggr_txq,
424162306a36Sopenharmony_ci				     struct mvpp2_txq_pcpu *txq_pcpu,
424262306a36Sopenharmony_ci				     int hdr_sz)
424362306a36Sopenharmony_ci{
424462306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
424562306a36Sopenharmony_ci	struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
424662306a36Sopenharmony_ci	dma_addr_t addr;
424762306a36Sopenharmony_ci
424862306a36Sopenharmony_ci	mvpp2_txdesc_clear_ptp(port, tx_desc);
424962306a36Sopenharmony_ci	mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
425062306a36Sopenharmony_ci	mvpp2_txdesc_size_set(port, tx_desc, hdr_sz);
425162306a36Sopenharmony_ci
425262306a36Sopenharmony_ci	addr = txq_pcpu->tso_headers_dma +
425362306a36Sopenharmony_ci	       txq_pcpu->txq_put_index * TSO_HEADER_SIZE;
425462306a36Sopenharmony_ci	mvpp2_txdesc_dma_addr_set(port, tx_desc, addr);
425562306a36Sopenharmony_ci
425662306a36Sopenharmony_ci	mvpp2_txdesc_cmd_set(port, tx_desc, mvpp2_skb_tx_csum(port, skb) |
425762306a36Sopenharmony_ci					    MVPP2_TXD_F_DESC |
425862306a36Sopenharmony_ci					    MVPP2_TXD_PADDING_DISABLE);
425962306a36Sopenharmony_ci	mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc, MVPP2_TYPE_SKB);
426062306a36Sopenharmony_ci}
426162306a36Sopenharmony_ci
426262306a36Sopenharmony_cistatic inline int mvpp2_tso_put_data(struct sk_buff *skb,
426362306a36Sopenharmony_ci				     struct net_device *dev, struct tso_t *tso,
426462306a36Sopenharmony_ci				     struct mvpp2_tx_queue *txq,
426562306a36Sopenharmony_ci				     struct mvpp2_tx_queue *aggr_txq,
426662306a36Sopenharmony_ci				     struct mvpp2_txq_pcpu *txq_pcpu,
426762306a36Sopenharmony_ci				     int sz, bool left, bool last)
426862306a36Sopenharmony_ci{
426962306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
427062306a36Sopenharmony_ci	struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
427162306a36Sopenharmony_ci	dma_addr_t buf_dma_addr;
427262306a36Sopenharmony_ci
427362306a36Sopenharmony_ci	mvpp2_txdesc_clear_ptp(port, tx_desc);
427462306a36Sopenharmony_ci	mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
427562306a36Sopenharmony_ci	mvpp2_txdesc_size_set(port, tx_desc, sz);
427662306a36Sopenharmony_ci
427762306a36Sopenharmony_ci	buf_dma_addr = dma_map_single(dev->dev.parent, tso->data, sz,
427862306a36Sopenharmony_ci				      DMA_TO_DEVICE);
427962306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(dev->dev.parent, buf_dma_addr))) {
428062306a36Sopenharmony_ci		mvpp2_txq_desc_put(txq);
428162306a36Sopenharmony_ci		return -ENOMEM;
428262306a36Sopenharmony_ci	}
428362306a36Sopenharmony_ci
428462306a36Sopenharmony_ci	mvpp2_txdesc_dma_addr_set(port, tx_desc, buf_dma_addr);
428562306a36Sopenharmony_ci
428662306a36Sopenharmony_ci	if (!left) {
428762306a36Sopenharmony_ci		mvpp2_txdesc_cmd_set(port, tx_desc, MVPP2_TXD_L_DESC);
428862306a36Sopenharmony_ci		if (last) {
428962306a36Sopenharmony_ci			mvpp2_txq_inc_put(port, txq_pcpu, skb, tx_desc, MVPP2_TYPE_SKB);
429062306a36Sopenharmony_ci			return 0;
429162306a36Sopenharmony_ci		}
429262306a36Sopenharmony_ci	} else {
429362306a36Sopenharmony_ci		mvpp2_txdesc_cmd_set(port, tx_desc, 0);
429462306a36Sopenharmony_ci	}
429562306a36Sopenharmony_ci
429662306a36Sopenharmony_ci	mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc, MVPP2_TYPE_SKB);
429762306a36Sopenharmony_ci	return 0;
429862306a36Sopenharmony_ci}
429962306a36Sopenharmony_ci
430062306a36Sopenharmony_cistatic int mvpp2_tx_tso(struct sk_buff *skb, struct net_device *dev,
430162306a36Sopenharmony_ci			struct mvpp2_tx_queue *txq,
430262306a36Sopenharmony_ci			struct mvpp2_tx_queue *aggr_txq,
430362306a36Sopenharmony_ci			struct mvpp2_txq_pcpu *txq_pcpu)
430462306a36Sopenharmony_ci{
430562306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
430662306a36Sopenharmony_ci	int hdr_sz, i, len, descs = 0;
430762306a36Sopenharmony_ci	struct tso_t tso;
430862306a36Sopenharmony_ci
430962306a36Sopenharmony_ci	/* Check number of available descriptors */
431062306a36Sopenharmony_ci	if (mvpp2_aggr_desc_num_check(port, aggr_txq, tso_count_descs(skb)) ||
431162306a36Sopenharmony_ci	    mvpp2_txq_reserved_desc_num_proc(port, txq, txq_pcpu,
431262306a36Sopenharmony_ci					     tso_count_descs(skb)))
431362306a36Sopenharmony_ci		return 0;
431462306a36Sopenharmony_ci
431562306a36Sopenharmony_ci	hdr_sz = tso_start(skb, &tso);
431662306a36Sopenharmony_ci
431762306a36Sopenharmony_ci	len = skb->len - hdr_sz;
431862306a36Sopenharmony_ci	while (len > 0) {
431962306a36Sopenharmony_ci		int left = min_t(int, skb_shinfo(skb)->gso_size, len);
432062306a36Sopenharmony_ci		char *hdr = txq_pcpu->tso_headers +
432162306a36Sopenharmony_ci			    txq_pcpu->txq_put_index * TSO_HEADER_SIZE;
432262306a36Sopenharmony_ci
432362306a36Sopenharmony_ci		len -= left;
432462306a36Sopenharmony_ci		descs++;
432562306a36Sopenharmony_ci
432662306a36Sopenharmony_ci		tso_build_hdr(skb, hdr, &tso, left, len == 0);
432762306a36Sopenharmony_ci		mvpp2_tso_put_hdr(skb, dev, txq, aggr_txq, txq_pcpu, hdr_sz);
432862306a36Sopenharmony_ci
432962306a36Sopenharmony_ci		while (left > 0) {
433062306a36Sopenharmony_ci			int sz = min_t(int, tso.size, left);
433162306a36Sopenharmony_ci			left -= sz;
433262306a36Sopenharmony_ci			descs++;
433362306a36Sopenharmony_ci
433462306a36Sopenharmony_ci			if (mvpp2_tso_put_data(skb, dev, &tso, txq, aggr_txq,
433562306a36Sopenharmony_ci					       txq_pcpu, sz, left, len == 0))
433662306a36Sopenharmony_ci				goto release;
433762306a36Sopenharmony_ci			tso_build_data(skb, &tso, sz);
433862306a36Sopenharmony_ci		}
433962306a36Sopenharmony_ci	}
434062306a36Sopenharmony_ci
434162306a36Sopenharmony_ci	return descs;
434262306a36Sopenharmony_ci
434362306a36Sopenharmony_cirelease:
434462306a36Sopenharmony_ci	for (i = descs - 1; i >= 0; i--) {
434562306a36Sopenharmony_ci		struct mvpp2_tx_desc *tx_desc = txq->descs + i;
434662306a36Sopenharmony_ci		tx_desc_unmap_put(port, txq, tx_desc);
434762306a36Sopenharmony_ci	}
434862306a36Sopenharmony_ci	return 0;
434962306a36Sopenharmony_ci}
435062306a36Sopenharmony_ci
435162306a36Sopenharmony_ci/* Main tx processing */
435262306a36Sopenharmony_cistatic netdev_tx_t mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
435362306a36Sopenharmony_ci{
435462306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
435562306a36Sopenharmony_ci	struct mvpp2_tx_queue *txq, *aggr_txq;
435662306a36Sopenharmony_ci	struct mvpp2_txq_pcpu *txq_pcpu;
435762306a36Sopenharmony_ci	struct mvpp2_tx_desc *tx_desc;
435862306a36Sopenharmony_ci	dma_addr_t buf_dma_addr;
435962306a36Sopenharmony_ci	unsigned long flags = 0;
436062306a36Sopenharmony_ci	unsigned int thread;
436162306a36Sopenharmony_ci	int frags = 0;
436262306a36Sopenharmony_ci	u16 txq_id;
436362306a36Sopenharmony_ci	u32 tx_cmd;
436462306a36Sopenharmony_ci
436562306a36Sopenharmony_ci	thread = mvpp2_cpu_to_thread(port->priv, smp_processor_id());
436662306a36Sopenharmony_ci
436762306a36Sopenharmony_ci	txq_id = skb_get_queue_mapping(skb);
436862306a36Sopenharmony_ci	txq = port->txqs[txq_id];
436962306a36Sopenharmony_ci	txq_pcpu = per_cpu_ptr(txq->pcpu, thread);
437062306a36Sopenharmony_ci	aggr_txq = &port->priv->aggr_txqs[thread];
437162306a36Sopenharmony_ci
437262306a36Sopenharmony_ci	if (test_bit(thread, &port->priv->lock_map))
437362306a36Sopenharmony_ci		spin_lock_irqsave(&port->tx_lock[thread], flags);
437462306a36Sopenharmony_ci
437562306a36Sopenharmony_ci	if (skb_is_gso(skb)) {
437662306a36Sopenharmony_ci		frags = mvpp2_tx_tso(skb, dev, txq, aggr_txq, txq_pcpu);
437762306a36Sopenharmony_ci		goto out;
437862306a36Sopenharmony_ci	}
437962306a36Sopenharmony_ci	frags = skb_shinfo(skb)->nr_frags + 1;
438062306a36Sopenharmony_ci
438162306a36Sopenharmony_ci	/* Check number of available descriptors */
438262306a36Sopenharmony_ci	if (mvpp2_aggr_desc_num_check(port, aggr_txq, frags) ||
438362306a36Sopenharmony_ci	    mvpp2_txq_reserved_desc_num_proc(port, txq, txq_pcpu, frags)) {
438462306a36Sopenharmony_ci		frags = 0;
438562306a36Sopenharmony_ci		goto out;
438662306a36Sopenharmony_ci	}
438762306a36Sopenharmony_ci
438862306a36Sopenharmony_ci	/* Get a descriptor for the first part of the packet */
438962306a36Sopenharmony_ci	tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
439062306a36Sopenharmony_ci	if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) ||
439162306a36Sopenharmony_ci	    !mvpp2_tx_hw_tstamp(port, tx_desc, skb))
439262306a36Sopenharmony_ci		mvpp2_txdesc_clear_ptp(port, tx_desc);
439362306a36Sopenharmony_ci	mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
439462306a36Sopenharmony_ci	mvpp2_txdesc_size_set(port, tx_desc, skb_headlen(skb));
439562306a36Sopenharmony_ci
439662306a36Sopenharmony_ci	buf_dma_addr = dma_map_single(dev->dev.parent, skb->data,
439762306a36Sopenharmony_ci				      skb_headlen(skb), DMA_TO_DEVICE);
439862306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(dev->dev.parent, buf_dma_addr))) {
439962306a36Sopenharmony_ci		mvpp2_txq_desc_put(txq);
440062306a36Sopenharmony_ci		frags = 0;
440162306a36Sopenharmony_ci		goto out;
440262306a36Sopenharmony_ci	}
440362306a36Sopenharmony_ci
440462306a36Sopenharmony_ci	mvpp2_txdesc_dma_addr_set(port, tx_desc, buf_dma_addr);
440562306a36Sopenharmony_ci
440662306a36Sopenharmony_ci	tx_cmd = mvpp2_skb_tx_csum(port, skb);
440762306a36Sopenharmony_ci
440862306a36Sopenharmony_ci	if (frags == 1) {
440962306a36Sopenharmony_ci		/* First and Last descriptor */
441062306a36Sopenharmony_ci		tx_cmd |= MVPP2_TXD_F_DESC | MVPP2_TXD_L_DESC;
441162306a36Sopenharmony_ci		mvpp2_txdesc_cmd_set(port, tx_desc, tx_cmd);
441262306a36Sopenharmony_ci		mvpp2_txq_inc_put(port, txq_pcpu, skb, tx_desc, MVPP2_TYPE_SKB);
441362306a36Sopenharmony_ci	} else {
441462306a36Sopenharmony_ci		/* First but not Last */
441562306a36Sopenharmony_ci		tx_cmd |= MVPP2_TXD_F_DESC | MVPP2_TXD_PADDING_DISABLE;
441662306a36Sopenharmony_ci		mvpp2_txdesc_cmd_set(port, tx_desc, tx_cmd);
441762306a36Sopenharmony_ci		mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc, MVPP2_TYPE_SKB);
441862306a36Sopenharmony_ci
441962306a36Sopenharmony_ci		/* Continue with other skb fragments */
442062306a36Sopenharmony_ci		if (mvpp2_tx_frag_process(port, skb, aggr_txq, txq)) {
442162306a36Sopenharmony_ci			tx_desc_unmap_put(port, txq, tx_desc);
442262306a36Sopenharmony_ci			frags = 0;
442362306a36Sopenharmony_ci		}
442462306a36Sopenharmony_ci	}
442562306a36Sopenharmony_ci
442662306a36Sopenharmony_ciout:
442762306a36Sopenharmony_ci	if (frags > 0) {
442862306a36Sopenharmony_ci		struct mvpp2_pcpu_stats *stats = per_cpu_ptr(port->stats, thread);
442962306a36Sopenharmony_ci		struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id);
443062306a36Sopenharmony_ci
443162306a36Sopenharmony_ci		txq_pcpu->reserved_num -= frags;
443262306a36Sopenharmony_ci		txq_pcpu->count += frags;
443362306a36Sopenharmony_ci		aggr_txq->count += frags;
443462306a36Sopenharmony_ci
443562306a36Sopenharmony_ci		/* Enable transmit */
443662306a36Sopenharmony_ci		wmb();
443762306a36Sopenharmony_ci		mvpp2_aggr_txq_pend_desc_add(port, frags);
443862306a36Sopenharmony_ci
443962306a36Sopenharmony_ci		if (txq_pcpu->count >= txq_pcpu->stop_threshold)
444062306a36Sopenharmony_ci			netif_tx_stop_queue(nq);
444162306a36Sopenharmony_ci
444262306a36Sopenharmony_ci		u64_stats_update_begin(&stats->syncp);
444362306a36Sopenharmony_ci		stats->tx_packets++;
444462306a36Sopenharmony_ci		stats->tx_bytes += skb->len;
444562306a36Sopenharmony_ci		u64_stats_update_end(&stats->syncp);
444662306a36Sopenharmony_ci	} else {
444762306a36Sopenharmony_ci		dev->stats.tx_dropped++;
444862306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
444962306a36Sopenharmony_ci	}
445062306a36Sopenharmony_ci
445162306a36Sopenharmony_ci	/* Finalize TX processing */
445262306a36Sopenharmony_ci	if (!port->has_tx_irqs && txq_pcpu->count >= txq->done_pkts_coal)
445362306a36Sopenharmony_ci		mvpp2_txq_done(port, txq, txq_pcpu);
445462306a36Sopenharmony_ci
445562306a36Sopenharmony_ci	/* Set the timer in case not all frags were processed */
445662306a36Sopenharmony_ci	if (!port->has_tx_irqs && txq_pcpu->count <= frags &&
445762306a36Sopenharmony_ci	    txq_pcpu->count > 0) {
445862306a36Sopenharmony_ci		struct mvpp2_port_pcpu *port_pcpu = per_cpu_ptr(port->pcpu, thread);
445962306a36Sopenharmony_ci
446062306a36Sopenharmony_ci		if (!port_pcpu->timer_scheduled) {
446162306a36Sopenharmony_ci			port_pcpu->timer_scheduled = true;
446262306a36Sopenharmony_ci			hrtimer_start(&port_pcpu->tx_done_timer,
446362306a36Sopenharmony_ci				      MVPP2_TXDONE_HRTIMER_PERIOD_NS,
446462306a36Sopenharmony_ci				      HRTIMER_MODE_REL_PINNED_SOFT);
446562306a36Sopenharmony_ci		}
446662306a36Sopenharmony_ci	}
446762306a36Sopenharmony_ci
446862306a36Sopenharmony_ci	if (test_bit(thread, &port->priv->lock_map))
446962306a36Sopenharmony_ci		spin_unlock_irqrestore(&port->tx_lock[thread], flags);
447062306a36Sopenharmony_ci
447162306a36Sopenharmony_ci	return NETDEV_TX_OK;
447262306a36Sopenharmony_ci}
447362306a36Sopenharmony_ci
447462306a36Sopenharmony_cistatic inline void mvpp2_cause_error(struct net_device *dev, int cause)
447562306a36Sopenharmony_ci{
447662306a36Sopenharmony_ci	if (cause & MVPP2_CAUSE_FCS_ERR_MASK)
447762306a36Sopenharmony_ci		netdev_err(dev, "FCS error\n");
447862306a36Sopenharmony_ci	if (cause & MVPP2_CAUSE_RX_FIFO_OVERRUN_MASK)
447962306a36Sopenharmony_ci		netdev_err(dev, "rx fifo overrun error\n");
448062306a36Sopenharmony_ci	if (cause & MVPP2_CAUSE_TX_FIFO_UNDERRUN_MASK)
448162306a36Sopenharmony_ci		netdev_err(dev, "tx fifo underrun error\n");
448262306a36Sopenharmony_ci}
448362306a36Sopenharmony_ci
448462306a36Sopenharmony_cistatic int mvpp2_poll(struct napi_struct *napi, int budget)
448562306a36Sopenharmony_ci{
448662306a36Sopenharmony_ci	u32 cause_rx_tx, cause_rx, cause_tx, cause_misc;
448762306a36Sopenharmony_ci	int rx_done = 0;
448862306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(napi->dev);
448962306a36Sopenharmony_ci	struct mvpp2_queue_vector *qv;
449062306a36Sopenharmony_ci	unsigned int thread = mvpp2_cpu_to_thread(port->priv, smp_processor_id());
449162306a36Sopenharmony_ci
449262306a36Sopenharmony_ci	qv = container_of(napi, struct mvpp2_queue_vector, napi);
449362306a36Sopenharmony_ci
449462306a36Sopenharmony_ci	/* Rx/Tx cause register
449562306a36Sopenharmony_ci	 *
449662306a36Sopenharmony_ci	 * Bits 0-15: each bit indicates received packets on the Rx queue
449762306a36Sopenharmony_ci	 * (bit 0 is for Rx queue 0).
449862306a36Sopenharmony_ci	 *
449962306a36Sopenharmony_ci	 * Bits 16-23: each bit indicates transmitted packets on the Tx queue
450062306a36Sopenharmony_ci	 * (bit 16 is for Tx queue 0).
450162306a36Sopenharmony_ci	 *
450262306a36Sopenharmony_ci	 * Each CPU has its own Rx/Tx cause register
450362306a36Sopenharmony_ci	 */
450462306a36Sopenharmony_ci	cause_rx_tx = mvpp2_thread_read_relaxed(port->priv, qv->sw_thread_id,
450562306a36Sopenharmony_ci						MVPP2_ISR_RX_TX_CAUSE_REG(port->id));
450662306a36Sopenharmony_ci
450762306a36Sopenharmony_ci	cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK;
450862306a36Sopenharmony_ci	if (cause_misc) {
450962306a36Sopenharmony_ci		mvpp2_cause_error(port->dev, cause_misc);
451062306a36Sopenharmony_ci
451162306a36Sopenharmony_ci		/* Clear the cause register */
451262306a36Sopenharmony_ci		mvpp2_write(port->priv, MVPP2_ISR_MISC_CAUSE_REG, 0);
451362306a36Sopenharmony_ci		mvpp2_thread_write(port->priv, thread,
451462306a36Sopenharmony_ci				   MVPP2_ISR_RX_TX_CAUSE_REG(port->id),
451562306a36Sopenharmony_ci				   cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK);
451662306a36Sopenharmony_ci	}
451762306a36Sopenharmony_ci
451862306a36Sopenharmony_ci	if (port->has_tx_irqs) {
451962306a36Sopenharmony_ci		cause_tx = cause_rx_tx & MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK;
452062306a36Sopenharmony_ci		if (cause_tx) {
452162306a36Sopenharmony_ci			cause_tx >>= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET;
452262306a36Sopenharmony_ci			mvpp2_tx_done(port, cause_tx, qv->sw_thread_id);
452362306a36Sopenharmony_ci		}
452462306a36Sopenharmony_ci	}
452562306a36Sopenharmony_ci
452662306a36Sopenharmony_ci	/* Process RX packets */
452762306a36Sopenharmony_ci	cause_rx = cause_rx_tx &
452862306a36Sopenharmony_ci		   MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK(port->priv->hw_version);
452962306a36Sopenharmony_ci	cause_rx <<= qv->first_rxq;
453062306a36Sopenharmony_ci	cause_rx |= qv->pending_cause_rx;
453162306a36Sopenharmony_ci	while (cause_rx && budget > 0) {
453262306a36Sopenharmony_ci		int count;
453362306a36Sopenharmony_ci		struct mvpp2_rx_queue *rxq;
453462306a36Sopenharmony_ci
453562306a36Sopenharmony_ci		rxq = mvpp2_get_rx_queue(port, cause_rx);
453662306a36Sopenharmony_ci		if (!rxq)
453762306a36Sopenharmony_ci			break;
453862306a36Sopenharmony_ci
453962306a36Sopenharmony_ci		count = mvpp2_rx(port, napi, budget, rxq);
454062306a36Sopenharmony_ci		rx_done += count;
454162306a36Sopenharmony_ci		budget -= count;
454262306a36Sopenharmony_ci		if (budget > 0) {
454362306a36Sopenharmony_ci			/* Clear the bit associated to this Rx queue
454462306a36Sopenharmony_ci			 * so that next iteration will continue from
454562306a36Sopenharmony_ci			 * the next Rx queue.
454662306a36Sopenharmony_ci			 */
454762306a36Sopenharmony_ci			cause_rx &= ~(1 << rxq->logic_rxq);
454862306a36Sopenharmony_ci		}
454962306a36Sopenharmony_ci	}
455062306a36Sopenharmony_ci
455162306a36Sopenharmony_ci	if (budget > 0) {
455262306a36Sopenharmony_ci		cause_rx = 0;
455362306a36Sopenharmony_ci		napi_complete_done(napi, rx_done);
455462306a36Sopenharmony_ci
455562306a36Sopenharmony_ci		mvpp2_qvec_interrupt_enable(qv);
455662306a36Sopenharmony_ci	}
455762306a36Sopenharmony_ci	qv->pending_cause_rx = cause_rx;
455862306a36Sopenharmony_ci	return rx_done;
455962306a36Sopenharmony_ci}
456062306a36Sopenharmony_ci
456162306a36Sopenharmony_cistatic void mvpp22_mode_reconfigure(struct mvpp2_port *port,
456262306a36Sopenharmony_ci				    phy_interface_t interface)
456362306a36Sopenharmony_ci{
456462306a36Sopenharmony_ci	u32 ctrl3;
456562306a36Sopenharmony_ci
456662306a36Sopenharmony_ci	/* Set the GMAC & XLG MAC in reset */
456762306a36Sopenharmony_ci	mvpp2_mac_reset_assert(port);
456862306a36Sopenharmony_ci
456962306a36Sopenharmony_ci	/* Set the MPCS and XPCS in reset */
457062306a36Sopenharmony_ci	mvpp22_pcs_reset_assert(port);
457162306a36Sopenharmony_ci
457262306a36Sopenharmony_ci	/* comphy reconfiguration */
457362306a36Sopenharmony_ci	mvpp22_comphy_init(port, interface);
457462306a36Sopenharmony_ci
457562306a36Sopenharmony_ci	/* gop reconfiguration */
457662306a36Sopenharmony_ci	mvpp22_gop_init(port, interface);
457762306a36Sopenharmony_ci
457862306a36Sopenharmony_ci	mvpp22_pcs_reset_deassert(port, interface);
457962306a36Sopenharmony_ci
458062306a36Sopenharmony_ci	if (mvpp2_port_supports_xlg(port)) {
458162306a36Sopenharmony_ci		ctrl3 = readl(port->base + MVPP22_XLG_CTRL3_REG);
458262306a36Sopenharmony_ci		ctrl3 &= ~MVPP22_XLG_CTRL3_MACMODESELECT_MASK;
458362306a36Sopenharmony_ci
458462306a36Sopenharmony_ci		if (mvpp2_is_xlg(interface))
458562306a36Sopenharmony_ci			ctrl3 |= MVPP22_XLG_CTRL3_MACMODESELECT_10G;
458662306a36Sopenharmony_ci		else
458762306a36Sopenharmony_ci			ctrl3 |= MVPP22_XLG_CTRL3_MACMODESELECT_GMAC;
458862306a36Sopenharmony_ci
458962306a36Sopenharmony_ci		writel(ctrl3, port->base + MVPP22_XLG_CTRL3_REG);
459062306a36Sopenharmony_ci	}
459162306a36Sopenharmony_ci
459262306a36Sopenharmony_ci	if (mvpp2_port_supports_xlg(port) && mvpp2_is_xlg(interface))
459362306a36Sopenharmony_ci		mvpp2_xlg_max_rx_size_set(port);
459462306a36Sopenharmony_ci	else
459562306a36Sopenharmony_ci		mvpp2_gmac_max_rx_size_set(port);
459662306a36Sopenharmony_ci}
459762306a36Sopenharmony_ci
459862306a36Sopenharmony_ci/* Set hw internals when starting port */
459962306a36Sopenharmony_cistatic void mvpp2_start_dev(struct mvpp2_port *port)
460062306a36Sopenharmony_ci{
460162306a36Sopenharmony_ci	int i;
460262306a36Sopenharmony_ci
460362306a36Sopenharmony_ci	mvpp2_txp_max_tx_size_set(port);
460462306a36Sopenharmony_ci
460562306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++)
460662306a36Sopenharmony_ci		napi_enable(&port->qvecs[i].napi);
460762306a36Sopenharmony_ci
460862306a36Sopenharmony_ci	/* Enable interrupts on all threads */
460962306a36Sopenharmony_ci	mvpp2_interrupts_enable(port);
461062306a36Sopenharmony_ci
461162306a36Sopenharmony_ci	if (port->priv->hw_version >= MVPP22)
461262306a36Sopenharmony_ci		mvpp22_mode_reconfigure(port, port->phy_interface);
461362306a36Sopenharmony_ci
461462306a36Sopenharmony_ci	if (port->phylink) {
461562306a36Sopenharmony_ci		phylink_start(port->phylink);
461662306a36Sopenharmony_ci	} else {
461762306a36Sopenharmony_ci		mvpp2_acpi_start(port);
461862306a36Sopenharmony_ci	}
461962306a36Sopenharmony_ci
462062306a36Sopenharmony_ci	netif_tx_start_all_queues(port->dev);
462162306a36Sopenharmony_ci
462262306a36Sopenharmony_ci	clear_bit(0, &port->state);
462362306a36Sopenharmony_ci}
462462306a36Sopenharmony_ci
462562306a36Sopenharmony_ci/* Set hw internals when stopping port */
462662306a36Sopenharmony_cistatic void mvpp2_stop_dev(struct mvpp2_port *port)
462762306a36Sopenharmony_ci{
462862306a36Sopenharmony_ci	int i;
462962306a36Sopenharmony_ci
463062306a36Sopenharmony_ci	set_bit(0, &port->state);
463162306a36Sopenharmony_ci
463262306a36Sopenharmony_ci	/* Disable interrupts on all threads */
463362306a36Sopenharmony_ci	mvpp2_interrupts_disable(port);
463462306a36Sopenharmony_ci
463562306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++)
463662306a36Sopenharmony_ci		napi_disable(&port->qvecs[i].napi);
463762306a36Sopenharmony_ci
463862306a36Sopenharmony_ci	if (port->phylink)
463962306a36Sopenharmony_ci		phylink_stop(port->phylink);
464062306a36Sopenharmony_ci	phy_power_off(port->comphy);
464162306a36Sopenharmony_ci}
464262306a36Sopenharmony_ci
464362306a36Sopenharmony_cistatic int mvpp2_check_ringparam_valid(struct net_device *dev,
464462306a36Sopenharmony_ci				       struct ethtool_ringparam *ring)
464562306a36Sopenharmony_ci{
464662306a36Sopenharmony_ci	u16 new_rx_pending = ring->rx_pending;
464762306a36Sopenharmony_ci	u16 new_tx_pending = ring->tx_pending;
464862306a36Sopenharmony_ci
464962306a36Sopenharmony_ci	if (ring->rx_pending == 0 || ring->tx_pending == 0)
465062306a36Sopenharmony_ci		return -EINVAL;
465162306a36Sopenharmony_ci
465262306a36Sopenharmony_ci	if (ring->rx_pending > MVPP2_MAX_RXD_MAX)
465362306a36Sopenharmony_ci		new_rx_pending = MVPP2_MAX_RXD_MAX;
465462306a36Sopenharmony_ci	else if (ring->rx_pending < MSS_THRESHOLD_START)
465562306a36Sopenharmony_ci		new_rx_pending = MSS_THRESHOLD_START;
465662306a36Sopenharmony_ci	else if (!IS_ALIGNED(ring->rx_pending, 16))
465762306a36Sopenharmony_ci		new_rx_pending = ALIGN(ring->rx_pending, 16);
465862306a36Sopenharmony_ci
465962306a36Sopenharmony_ci	if (ring->tx_pending > MVPP2_MAX_TXD_MAX)
466062306a36Sopenharmony_ci		new_tx_pending = MVPP2_MAX_TXD_MAX;
466162306a36Sopenharmony_ci	else if (!IS_ALIGNED(ring->tx_pending, 32))
466262306a36Sopenharmony_ci		new_tx_pending = ALIGN(ring->tx_pending, 32);
466362306a36Sopenharmony_ci
466462306a36Sopenharmony_ci	/* The Tx ring size cannot be smaller than the minimum number of
466562306a36Sopenharmony_ci	 * descriptors needed for TSO.
466662306a36Sopenharmony_ci	 */
466762306a36Sopenharmony_ci	if (new_tx_pending < MVPP2_MAX_SKB_DESCS)
466862306a36Sopenharmony_ci		new_tx_pending = ALIGN(MVPP2_MAX_SKB_DESCS, 32);
466962306a36Sopenharmony_ci
467062306a36Sopenharmony_ci	if (ring->rx_pending != new_rx_pending) {
467162306a36Sopenharmony_ci		netdev_info(dev, "illegal Rx ring size value %d, round to %d\n",
467262306a36Sopenharmony_ci			    ring->rx_pending, new_rx_pending);
467362306a36Sopenharmony_ci		ring->rx_pending = new_rx_pending;
467462306a36Sopenharmony_ci	}
467562306a36Sopenharmony_ci
467662306a36Sopenharmony_ci	if (ring->tx_pending != new_tx_pending) {
467762306a36Sopenharmony_ci		netdev_info(dev, "illegal Tx ring size value %d, round to %d\n",
467862306a36Sopenharmony_ci			    ring->tx_pending, new_tx_pending);
467962306a36Sopenharmony_ci		ring->tx_pending = new_tx_pending;
468062306a36Sopenharmony_ci	}
468162306a36Sopenharmony_ci
468262306a36Sopenharmony_ci	return 0;
468362306a36Sopenharmony_ci}
468462306a36Sopenharmony_ci
468562306a36Sopenharmony_cistatic void mvpp21_get_mac_address(struct mvpp2_port *port, unsigned char *addr)
468662306a36Sopenharmony_ci{
468762306a36Sopenharmony_ci	u32 mac_addr_l, mac_addr_m, mac_addr_h;
468862306a36Sopenharmony_ci
468962306a36Sopenharmony_ci	mac_addr_l = readl(port->base + MVPP2_GMAC_CTRL_1_REG);
469062306a36Sopenharmony_ci	mac_addr_m = readl(port->priv->lms_base + MVPP2_SRC_ADDR_MIDDLE);
469162306a36Sopenharmony_ci	mac_addr_h = readl(port->priv->lms_base + MVPP2_SRC_ADDR_HIGH);
469262306a36Sopenharmony_ci	addr[0] = (mac_addr_h >> 24) & 0xFF;
469362306a36Sopenharmony_ci	addr[1] = (mac_addr_h >> 16) & 0xFF;
469462306a36Sopenharmony_ci	addr[2] = (mac_addr_h >> 8) & 0xFF;
469562306a36Sopenharmony_ci	addr[3] = mac_addr_h & 0xFF;
469662306a36Sopenharmony_ci	addr[4] = mac_addr_m & 0xFF;
469762306a36Sopenharmony_ci	addr[5] = (mac_addr_l >> MVPP2_GMAC_SA_LOW_OFFS) & 0xFF;
469862306a36Sopenharmony_ci}
469962306a36Sopenharmony_ci
470062306a36Sopenharmony_cistatic int mvpp2_irqs_init(struct mvpp2_port *port)
470162306a36Sopenharmony_ci{
470262306a36Sopenharmony_ci	int err, i;
470362306a36Sopenharmony_ci
470462306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++) {
470562306a36Sopenharmony_ci		struct mvpp2_queue_vector *qv = port->qvecs + i;
470662306a36Sopenharmony_ci
470762306a36Sopenharmony_ci		if (qv->type == MVPP2_QUEUE_VECTOR_PRIVATE) {
470862306a36Sopenharmony_ci			qv->mask = kzalloc(cpumask_size(), GFP_KERNEL);
470962306a36Sopenharmony_ci			if (!qv->mask) {
471062306a36Sopenharmony_ci				err = -ENOMEM;
471162306a36Sopenharmony_ci				goto err;
471262306a36Sopenharmony_ci			}
471362306a36Sopenharmony_ci
471462306a36Sopenharmony_ci			irq_set_status_flags(qv->irq, IRQ_NO_BALANCING);
471562306a36Sopenharmony_ci		}
471662306a36Sopenharmony_ci
471762306a36Sopenharmony_ci		err = request_irq(qv->irq, mvpp2_isr, 0, port->dev->name, qv);
471862306a36Sopenharmony_ci		if (err)
471962306a36Sopenharmony_ci			goto err;
472062306a36Sopenharmony_ci
472162306a36Sopenharmony_ci		if (qv->type == MVPP2_QUEUE_VECTOR_PRIVATE) {
472262306a36Sopenharmony_ci			unsigned int cpu;
472362306a36Sopenharmony_ci
472462306a36Sopenharmony_ci			for_each_present_cpu(cpu) {
472562306a36Sopenharmony_ci				if (mvpp2_cpu_to_thread(port->priv, cpu) ==
472662306a36Sopenharmony_ci				    qv->sw_thread_id)
472762306a36Sopenharmony_ci					cpumask_set_cpu(cpu, qv->mask);
472862306a36Sopenharmony_ci			}
472962306a36Sopenharmony_ci
473062306a36Sopenharmony_ci			irq_set_affinity_hint(qv->irq, qv->mask);
473162306a36Sopenharmony_ci		}
473262306a36Sopenharmony_ci	}
473362306a36Sopenharmony_ci
473462306a36Sopenharmony_ci	return 0;
473562306a36Sopenharmony_cierr:
473662306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++) {
473762306a36Sopenharmony_ci		struct mvpp2_queue_vector *qv = port->qvecs + i;
473862306a36Sopenharmony_ci
473962306a36Sopenharmony_ci		irq_set_affinity_hint(qv->irq, NULL);
474062306a36Sopenharmony_ci		kfree(qv->mask);
474162306a36Sopenharmony_ci		qv->mask = NULL;
474262306a36Sopenharmony_ci		free_irq(qv->irq, qv);
474362306a36Sopenharmony_ci	}
474462306a36Sopenharmony_ci
474562306a36Sopenharmony_ci	return err;
474662306a36Sopenharmony_ci}
474762306a36Sopenharmony_ci
474862306a36Sopenharmony_cistatic void mvpp2_irqs_deinit(struct mvpp2_port *port)
474962306a36Sopenharmony_ci{
475062306a36Sopenharmony_ci	int i;
475162306a36Sopenharmony_ci
475262306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++) {
475362306a36Sopenharmony_ci		struct mvpp2_queue_vector *qv = port->qvecs + i;
475462306a36Sopenharmony_ci
475562306a36Sopenharmony_ci		irq_set_affinity_hint(qv->irq, NULL);
475662306a36Sopenharmony_ci		kfree(qv->mask);
475762306a36Sopenharmony_ci		qv->mask = NULL;
475862306a36Sopenharmony_ci		irq_clear_status_flags(qv->irq, IRQ_NO_BALANCING);
475962306a36Sopenharmony_ci		free_irq(qv->irq, qv);
476062306a36Sopenharmony_ci	}
476162306a36Sopenharmony_ci}
476262306a36Sopenharmony_ci
476362306a36Sopenharmony_cistatic bool mvpp22_rss_is_supported(struct mvpp2_port *port)
476462306a36Sopenharmony_ci{
476562306a36Sopenharmony_ci	return (queue_mode == MVPP2_QDIST_MULTI_MODE) &&
476662306a36Sopenharmony_ci		!(port->flags & MVPP2_F_LOOPBACK);
476762306a36Sopenharmony_ci}
476862306a36Sopenharmony_ci
476962306a36Sopenharmony_cistatic int mvpp2_open(struct net_device *dev)
477062306a36Sopenharmony_ci{
477162306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
477262306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
477362306a36Sopenharmony_ci	unsigned char mac_bcast[ETH_ALEN] = {
477462306a36Sopenharmony_ci			0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
477562306a36Sopenharmony_ci	bool valid = false;
477662306a36Sopenharmony_ci	int err;
477762306a36Sopenharmony_ci
477862306a36Sopenharmony_ci	err = mvpp2_prs_mac_da_accept(port, mac_bcast, true);
477962306a36Sopenharmony_ci	if (err) {
478062306a36Sopenharmony_ci		netdev_err(dev, "mvpp2_prs_mac_da_accept BC failed\n");
478162306a36Sopenharmony_ci		return err;
478262306a36Sopenharmony_ci	}
478362306a36Sopenharmony_ci	err = mvpp2_prs_mac_da_accept(port, dev->dev_addr, true);
478462306a36Sopenharmony_ci	if (err) {
478562306a36Sopenharmony_ci		netdev_err(dev, "mvpp2_prs_mac_da_accept own addr failed\n");
478662306a36Sopenharmony_ci		return err;
478762306a36Sopenharmony_ci	}
478862306a36Sopenharmony_ci	err = mvpp2_prs_tag_mode_set(port->priv, port->id, MVPP2_TAG_TYPE_MH);
478962306a36Sopenharmony_ci	if (err) {
479062306a36Sopenharmony_ci		netdev_err(dev, "mvpp2_prs_tag_mode_set failed\n");
479162306a36Sopenharmony_ci		return err;
479262306a36Sopenharmony_ci	}
479362306a36Sopenharmony_ci	err = mvpp2_prs_def_flow(port);
479462306a36Sopenharmony_ci	if (err) {
479562306a36Sopenharmony_ci		netdev_err(dev, "mvpp2_prs_def_flow failed\n");
479662306a36Sopenharmony_ci		return err;
479762306a36Sopenharmony_ci	}
479862306a36Sopenharmony_ci
479962306a36Sopenharmony_ci	/* Allocate the Rx/Tx queues */
480062306a36Sopenharmony_ci	err = mvpp2_setup_rxqs(port);
480162306a36Sopenharmony_ci	if (err) {
480262306a36Sopenharmony_ci		netdev_err(port->dev, "cannot allocate Rx queues\n");
480362306a36Sopenharmony_ci		return err;
480462306a36Sopenharmony_ci	}
480562306a36Sopenharmony_ci
480662306a36Sopenharmony_ci	err = mvpp2_setup_txqs(port);
480762306a36Sopenharmony_ci	if (err) {
480862306a36Sopenharmony_ci		netdev_err(port->dev, "cannot allocate Tx queues\n");
480962306a36Sopenharmony_ci		goto err_cleanup_rxqs;
481062306a36Sopenharmony_ci	}
481162306a36Sopenharmony_ci
481262306a36Sopenharmony_ci	err = mvpp2_irqs_init(port);
481362306a36Sopenharmony_ci	if (err) {
481462306a36Sopenharmony_ci		netdev_err(port->dev, "cannot init IRQs\n");
481562306a36Sopenharmony_ci		goto err_cleanup_txqs;
481662306a36Sopenharmony_ci	}
481762306a36Sopenharmony_ci
481862306a36Sopenharmony_ci	if (port->phylink) {
481962306a36Sopenharmony_ci		err = phylink_fwnode_phy_connect(port->phylink, port->fwnode, 0);
482062306a36Sopenharmony_ci		if (err) {
482162306a36Sopenharmony_ci			netdev_err(port->dev, "could not attach PHY (%d)\n",
482262306a36Sopenharmony_ci				   err);
482362306a36Sopenharmony_ci			goto err_free_irq;
482462306a36Sopenharmony_ci		}
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci		valid = true;
482762306a36Sopenharmony_ci	}
482862306a36Sopenharmony_ci
482962306a36Sopenharmony_ci	if (priv->hw_version >= MVPP22 && port->port_irq) {
483062306a36Sopenharmony_ci		err = request_irq(port->port_irq, mvpp2_port_isr, 0,
483162306a36Sopenharmony_ci				  dev->name, port);
483262306a36Sopenharmony_ci		if (err) {
483362306a36Sopenharmony_ci			netdev_err(port->dev,
483462306a36Sopenharmony_ci				   "cannot request port link/ptp IRQ %d\n",
483562306a36Sopenharmony_ci				   port->port_irq);
483662306a36Sopenharmony_ci			goto err_free_irq;
483762306a36Sopenharmony_ci		}
483862306a36Sopenharmony_ci
483962306a36Sopenharmony_ci		mvpp22_gop_setup_irq(port);
484062306a36Sopenharmony_ci
484162306a36Sopenharmony_ci		/* In default link is down */
484262306a36Sopenharmony_ci		netif_carrier_off(port->dev);
484362306a36Sopenharmony_ci
484462306a36Sopenharmony_ci		valid = true;
484562306a36Sopenharmony_ci	} else {
484662306a36Sopenharmony_ci		port->port_irq = 0;
484762306a36Sopenharmony_ci	}
484862306a36Sopenharmony_ci
484962306a36Sopenharmony_ci	if (!valid) {
485062306a36Sopenharmony_ci		netdev_err(port->dev,
485162306a36Sopenharmony_ci			   "invalid configuration: no dt or link IRQ");
485262306a36Sopenharmony_ci		err = -ENOENT;
485362306a36Sopenharmony_ci		goto err_free_irq;
485462306a36Sopenharmony_ci	}
485562306a36Sopenharmony_ci
485662306a36Sopenharmony_ci	/* Unmask interrupts on all CPUs */
485762306a36Sopenharmony_ci	on_each_cpu(mvpp2_interrupts_unmask, port, 1);
485862306a36Sopenharmony_ci	mvpp2_shared_interrupt_mask_unmask(port, false);
485962306a36Sopenharmony_ci
486062306a36Sopenharmony_ci	mvpp2_start_dev(port);
486162306a36Sopenharmony_ci
486262306a36Sopenharmony_ci	/* Start hardware statistics gathering */
486362306a36Sopenharmony_ci	queue_delayed_work(priv->stats_queue, &port->stats_work,
486462306a36Sopenharmony_ci			   MVPP2_MIB_COUNTERS_STATS_DELAY);
486562306a36Sopenharmony_ci
486662306a36Sopenharmony_ci	return 0;
486762306a36Sopenharmony_ci
486862306a36Sopenharmony_cierr_free_irq:
486962306a36Sopenharmony_ci	mvpp2_irqs_deinit(port);
487062306a36Sopenharmony_cierr_cleanup_txqs:
487162306a36Sopenharmony_ci	mvpp2_cleanup_txqs(port);
487262306a36Sopenharmony_cierr_cleanup_rxqs:
487362306a36Sopenharmony_ci	mvpp2_cleanup_rxqs(port);
487462306a36Sopenharmony_ci	return err;
487562306a36Sopenharmony_ci}
487662306a36Sopenharmony_ci
487762306a36Sopenharmony_cistatic int mvpp2_stop(struct net_device *dev)
487862306a36Sopenharmony_ci{
487962306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
488062306a36Sopenharmony_ci	struct mvpp2_port_pcpu *port_pcpu;
488162306a36Sopenharmony_ci	unsigned int thread;
488262306a36Sopenharmony_ci
488362306a36Sopenharmony_ci	mvpp2_stop_dev(port);
488462306a36Sopenharmony_ci
488562306a36Sopenharmony_ci	/* Mask interrupts on all threads */
488662306a36Sopenharmony_ci	on_each_cpu(mvpp2_interrupts_mask, port, 1);
488762306a36Sopenharmony_ci	mvpp2_shared_interrupt_mask_unmask(port, true);
488862306a36Sopenharmony_ci
488962306a36Sopenharmony_ci	if (port->phylink)
489062306a36Sopenharmony_ci		phylink_disconnect_phy(port->phylink);
489162306a36Sopenharmony_ci	if (port->port_irq)
489262306a36Sopenharmony_ci		free_irq(port->port_irq, port);
489362306a36Sopenharmony_ci
489462306a36Sopenharmony_ci	mvpp2_irqs_deinit(port);
489562306a36Sopenharmony_ci	if (!port->has_tx_irqs) {
489662306a36Sopenharmony_ci		for (thread = 0; thread < port->priv->nthreads; thread++) {
489762306a36Sopenharmony_ci			port_pcpu = per_cpu_ptr(port->pcpu, thread);
489862306a36Sopenharmony_ci
489962306a36Sopenharmony_ci			hrtimer_cancel(&port_pcpu->tx_done_timer);
490062306a36Sopenharmony_ci			port_pcpu->timer_scheduled = false;
490162306a36Sopenharmony_ci		}
490262306a36Sopenharmony_ci	}
490362306a36Sopenharmony_ci	mvpp2_cleanup_rxqs(port);
490462306a36Sopenharmony_ci	mvpp2_cleanup_txqs(port);
490562306a36Sopenharmony_ci
490662306a36Sopenharmony_ci	cancel_delayed_work_sync(&port->stats_work);
490762306a36Sopenharmony_ci
490862306a36Sopenharmony_ci	mvpp2_mac_reset_assert(port);
490962306a36Sopenharmony_ci	mvpp22_pcs_reset_assert(port);
491062306a36Sopenharmony_ci
491162306a36Sopenharmony_ci	return 0;
491262306a36Sopenharmony_ci}
491362306a36Sopenharmony_ci
491462306a36Sopenharmony_cistatic int mvpp2_prs_mac_da_accept_list(struct mvpp2_port *port,
491562306a36Sopenharmony_ci					struct netdev_hw_addr_list *list)
491662306a36Sopenharmony_ci{
491762306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
491862306a36Sopenharmony_ci	int ret;
491962306a36Sopenharmony_ci
492062306a36Sopenharmony_ci	netdev_hw_addr_list_for_each(ha, list) {
492162306a36Sopenharmony_ci		ret = mvpp2_prs_mac_da_accept(port, ha->addr, true);
492262306a36Sopenharmony_ci		if (ret)
492362306a36Sopenharmony_ci			return ret;
492462306a36Sopenharmony_ci	}
492562306a36Sopenharmony_ci
492662306a36Sopenharmony_ci	return 0;
492762306a36Sopenharmony_ci}
492862306a36Sopenharmony_ci
492962306a36Sopenharmony_cistatic void mvpp2_set_rx_promisc(struct mvpp2_port *port, bool enable)
493062306a36Sopenharmony_ci{
493162306a36Sopenharmony_ci	if (!enable && (port->dev->features & NETIF_F_HW_VLAN_CTAG_FILTER))
493262306a36Sopenharmony_ci		mvpp2_prs_vid_enable_filtering(port);
493362306a36Sopenharmony_ci	else
493462306a36Sopenharmony_ci		mvpp2_prs_vid_disable_filtering(port);
493562306a36Sopenharmony_ci
493662306a36Sopenharmony_ci	mvpp2_prs_mac_promisc_set(port->priv, port->id,
493762306a36Sopenharmony_ci				  MVPP2_PRS_L2_UNI_CAST, enable);
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_ci	mvpp2_prs_mac_promisc_set(port->priv, port->id,
494062306a36Sopenharmony_ci				  MVPP2_PRS_L2_MULTI_CAST, enable);
494162306a36Sopenharmony_ci}
494262306a36Sopenharmony_ci
494362306a36Sopenharmony_cistatic void mvpp2_set_rx_mode(struct net_device *dev)
494462306a36Sopenharmony_ci{
494562306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
494662306a36Sopenharmony_ci
494762306a36Sopenharmony_ci	/* Clear the whole UC and MC list */
494862306a36Sopenharmony_ci	mvpp2_prs_mac_del_all(port);
494962306a36Sopenharmony_ci
495062306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
495162306a36Sopenharmony_ci		mvpp2_set_rx_promisc(port, true);
495262306a36Sopenharmony_ci		return;
495362306a36Sopenharmony_ci	}
495462306a36Sopenharmony_ci
495562306a36Sopenharmony_ci	mvpp2_set_rx_promisc(port, false);
495662306a36Sopenharmony_ci
495762306a36Sopenharmony_ci	if (netdev_uc_count(dev) > MVPP2_PRS_MAC_UC_FILT_MAX ||
495862306a36Sopenharmony_ci	    mvpp2_prs_mac_da_accept_list(port, &dev->uc))
495962306a36Sopenharmony_ci		mvpp2_prs_mac_promisc_set(port->priv, port->id,
496062306a36Sopenharmony_ci					  MVPP2_PRS_L2_UNI_CAST, true);
496162306a36Sopenharmony_ci
496262306a36Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI) {
496362306a36Sopenharmony_ci		mvpp2_prs_mac_promisc_set(port->priv, port->id,
496462306a36Sopenharmony_ci					  MVPP2_PRS_L2_MULTI_CAST, true);
496562306a36Sopenharmony_ci		return;
496662306a36Sopenharmony_ci	}
496762306a36Sopenharmony_ci
496862306a36Sopenharmony_ci	if (netdev_mc_count(dev) > MVPP2_PRS_MAC_MC_FILT_MAX ||
496962306a36Sopenharmony_ci	    mvpp2_prs_mac_da_accept_list(port, &dev->mc))
497062306a36Sopenharmony_ci		mvpp2_prs_mac_promisc_set(port->priv, port->id,
497162306a36Sopenharmony_ci					  MVPP2_PRS_L2_MULTI_CAST, true);
497262306a36Sopenharmony_ci}
497362306a36Sopenharmony_ci
497462306a36Sopenharmony_cistatic int mvpp2_set_mac_address(struct net_device *dev, void *p)
497562306a36Sopenharmony_ci{
497662306a36Sopenharmony_ci	const struct sockaddr *addr = p;
497762306a36Sopenharmony_ci	int err;
497862306a36Sopenharmony_ci
497962306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
498062306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
498162306a36Sopenharmony_ci
498262306a36Sopenharmony_ci	err = mvpp2_prs_update_mac_da(dev, addr->sa_data);
498362306a36Sopenharmony_ci	if (err) {
498462306a36Sopenharmony_ci		/* Reconfigure parser accept the original MAC address */
498562306a36Sopenharmony_ci		mvpp2_prs_update_mac_da(dev, dev->dev_addr);
498662306a36Sopenharmony_ci		netdev_err(dev, "failed to change MAC address\n");
498762306a36Sopenharmony_ci	}
498862306a36Sopenharmony_ci	return err;
498962306a36Sopenharmony_ci}
499062306a36Sopenharmony_ci
499162306a36Sopenharmony_ci/* Shut down all the ports, reconfigure the pools as percpu or shared,
499262306a36Sopenharmony_ci * then bring up again all ports.
499362306a36Sopenharmony_ci */
499462306a36Sopenharmony_cistatic int mvpp2_bm_switch_buffers(struct mvpp2 *priv, bool percpu)
499562306a36Sopenharmony_ci{
499662306a36Sopenharmony_ci	bool change_percpu = (percpu != priv->percpu_pools);
499762306a36Sopenharmony_ci	int numbufs = MVPP2_BM_POOLS_NUM, i;
499862306a36Sopenharmony_ci	struct mvpp2_port *port = NULL;
499962306a36Sopenharmony_ci	bool status[MVPP2_MAX_PORTS];
500062306a36Sopenharmony_ci
500162306a36Sopenharmony_ci	for (i = 0; i < priv->port_count; i++) {
500262306a36Sopenharmony_ci		port = priv->port_list[i];
500362306a36Sopenharmony_ci		status[i] = netif_running(port->dev);
500462306a36Sopenharmony_ci		if (status[i])
500562306a36Sopenharmony_ci			mvpp2_stop(port->dev);
500662306a36Sopenharmony_ci	}
500762306a36Sopenharmony_ci
500862306a36Sopenharmony_ci	/* nrxqs is the same for all ports */
500962306a36Sopenharmony_ci	if (priv->percpu_pools)
501062306a36Sopenharmony_ci		numbufs = port->nrxqs * 2;
501162306a36Sopenharmony_ci
501262306a36Sopenharmony_ci	if (change_percpu)
501362306a36Sopenharmony_ci		mvpp2_bm_pool_update_priv_fc(priv, false);
501462306a36Sopenharmony_ci
501562306a36Sopenharmony_ci	for (i = 0; i < numbufs; i++)
501662306a36Sopenharmony_ci		mvpp2_bm_pool_destroy(port->dev->dev.parent, priv, &priv->bm_pools[i]);
501762306a36Sopenharmony_ci
501862306a36Sopenharmony_ci	devm_kfree(port->dev->dev.parent, priv->bm_pools);
501962306a36Sopenharmony_ci	priv->percpu_pools = percpu;
502062306a36Sopenharmony_ci	mvpp2_bm_init(port->dev->dev.parent, priv);
502162306a36Sopenharmony_ci
502262306a36Sopenharmony_ci	for (i = 0; i < priv->port_count; i++) {
502362306a36Sopenharmony_ci		port = priv->port_list[i];
502462306a36Sopenharmony_ci		if (percpu && port->ntxqs >= num_possible_cpus() * 2)
502562306a36Sopenharmony_ci			xdp_set_features_flag(port->dev,
502662306a36Sopenharmony_ci					      NETDEV_XDP_ACT_BASIC |
502762306a36Sopenharmony_ci					      NETDEV_XDP_ACT_REDIRECT |
502862306a36Sopenharmony_ci					      NETDEV_XDP_ACT_NDO_XMIT);
502962306a36Sopenharmony_ci		else
503062306a36Sopenharmony_ci			xdp_clear_features_flag(port->dev);
503162306a36Sopenharmony_ci
503262306a36Sopenharmony_ci		mvpp2_swf_bm_pool_init(port);
503362306a36Sopenharmony_ci		if (status[i])
503462306a36Sopenharmony_ci			mvpp2_open(port->dev);
503562306a36Sopenharmony_ci	}
503662306a36Sopenharmony_ci
503762306a36Sopenharmony_ci	if (change_percpu)
503862306a36Sopenharmony_ci		mvpp2_bm_pool_update_priv_fc(priv, true);
503962306a36Sopenharmony_ci
504062306a36Sopenharmony_ci	return 0;
504162306a36Sopenharmony_ci}
504262306a36Sopenharmony_ci
504362306a36Sopenharmony_cistatic int mvpp2_change_mtu(struct net_device *dev, int mtu)
504462306a36Sopenharmony_ci{
504562306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
504662306a36Sopenharmony_ci	bool running = netif_running(dev);
504762306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
504862306a36Sopenharmony_ci	int err;
504962306a36Sopenharmony_ci
505062306a36Sopenharmony_ci	if (!IS_ALIGNED(MVPP2_RX_PKT_SIZE(mtu), 8)) {
505162306a36Sopenharmony_ci		netdev_info(dev, "illegal MTU value %d, round to %d\n", mtu,
505262306a36Sopenharmony_ci			    ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8));
505362306a36Sopenharmony_ci		mtu = ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8);
505462306a36Sopenharmony_ci	}
505562306a36Sopenharmony_ci
505662306a36Sopenharmony_ci	if (port->xdp_prog && mtu > MVPP2_MAX_RX_BUF_SIZE) {
505762306a36Sopenharmony_ci		netdev_err(dev, "Illegal MTU value %d (> %d) for XDP mode\n",
505862306a36Sopenharmony_ci			   mtu, (int)MVPP2_MAX_RX_BUF_SIZE);
505962306a36Sopenharmony_ci		return -EINVAL;
506062306a36Sopenharmony_ci	}
506162306a36Sopenharmony_ci
506262306a36Sopenharmony_ci	if (MVPP2_RX_PKT_SIZE(mtu) > MVPP2_BM_LONG_PKT_SIZE) {
506362306a36Sopenharmony_ci		if (priv->percpu_pools) {
506462306a36Sopenharmony_ci			netdev_warn(dev, "mtu %d too high, switching to shared buffers", mtu);
506562306a36Sopenharmony_ci			mvpp2_bm_switch_buffers(priv, false);
506662306a36Sopenharmony_ci		}
506762306a36Sopenharmony_ci	} else {
506862306a36Sopenharmony_ci		bool jumbo = false;
506962306a36Sopenharmony_ci		int i;
507062306a36Sopenharmony_ci
507162306a36Sopenharmony_ci		for (i = 0; i < priv->port_count; i++)
507262306a36Sopenharmony_ci			if (priv->port_list[i] != port &&
507362306a36Sopenharmony_ci			    MVPP2_RX_PKT_SIZE(priv->port_list[i]->dev->mtu) >
507462306a36Sopenharmony_ci			    MVPP2_BM_LONG_PKT_SIZE) {
507562306a36Sopenharmony_ci				jumbo = true;
507662306a36Sopenharmony_ci				break;
507762306a36Sopenharmony_ci			}
507862306a36Sopenharmony_ci
507962306a36Sopenharmony_ci		/* No port is using jumbo frames */
508062306a36Sopenharmony_ci		if (!jumbo) {
508162306a36Sopenharmony_ci			dev_info(port->dev->dev.parent,
508262306a36Sopenharmony_ci				 "all ports have a low MTU, switching to per-cpu buffers");
508362306a36Sopenharmony_ci			mvpp2_bm_switch_buffers(priv, true);
508462306a36Sopenharmony_ci		}
508562306a36Sopenharmony_ci	}
508662306a36Sopenharmony_ci
508762306a36Sopenharmony_ci	if (running)
508862306a36Sopenharmony_ci		mvpp2_stop_dev(port);
508962306a36Sopenharmony_ci
509062306a36Sopenharmony_ci	err = mvpp2_bm_update_mtu(dev, mtu);
509162306a36Sopenharmony_ci	if (err) {
509262306a36Sopenharmony_ci		netdev_err(dev, "failed to change MTU\n");
509362306a36Sopenharmony_ci		/* Reconfigure BM to the original MTU */
509462306a36Sopenharmony_ci		mvpp2_bm_update_mtu(dev, dev->mtu);
509562306a36Sopenharmony_ci	} else {
509662306a36Sopenharmony_ci		port->pkt_size =  MVPP2_RX_PKT_SIZE(mtu);
509762306a36Sopenharmony_ci	}
509862306a36Sopenharmony_ci
509962306a36Sopenharmony_ci	if (running) {
510062306a36Sopenharmony_ci		mvpp2_start_dev(port);
510162306a36Sopenharmony_ci		mvpp2_egress_enable(port);
510262306a36Sopenharmony_ci		mvpp2_ingress_enable(port);
510362306a36Sopenharmony_ci	}
510462306a36Sopenharmony_ci
510562306a36Sopenharmony_ci	return err;
510662306a36Sopenharmony_ci}
510762306a36Sopenharmony_ci
510862306a36Sopenharmony_cistatic int mvpp2_check_pagepool_dma(struct mvpp2_port *port)
510962306a36Sopenharmony_ci{
511062306a36Sopenharmony_ci	enum dma_data_direction dma_dir = DMA_FROM_DEVICE;
511162306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
511262306a36Sopenharmony_ci	int err = -1, i;
511362306a36Sopenharmony_ci
511462306a36Sopenharmony_ci	if (!priv->percpu_pools)
511562306a36Sopenharmony_ci		return err;
511662306a36Sopenharmony_ci
511762306a36Sopenharmony_ci	if (!priv->page_pool[0])
511862306a36Sopenharmony_ci		return -ENOMEM;
511962306a36Sopenharmony_ci
512062306a36Sopenharmony_ci	for (i = 0; i < priv->port_count; i++) {
512162306a36Sopenharmony_ci		port = priv->port_list[i];
512262306a36Sopenharmony_ci		if (port->xdp_prog) {
512362306a36Sopenharmony_ci			dma_dir = DMA_BIDIRECTIONAL;
512462306a36Sopenharmony_ci			break;
512562306a36Sopenharmony_ci		}
512662306a36Sopenharmony_ci	}
512762306a36Sopenharmony_ci
512862306a36Sopenharmony_ci	/* All pools are equal in terms of DMA direction */
512962306a36Sopenharmony_ci	if (priv->page_pool[0]->p.dma_dir != dma_dir)
513062306a36Sopenharmony_ci		err = mvpp2_bm_switch_buffers(priv, true);
513162306a36Sopenharmony_ci
513262306a36Sopenharmony_ci	return err;
513362306a36Sopenharmony_ci}
513462306a36Sopenharmony_ci
513562306a36Sopenharmony_cistatic void
513662306a36Sopenharmony_cimvpp2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
513762306a36Sopenharmony_ci{
513862306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
513962306a36Sopenharmony_ci	unsigned int start;
514062306a36Sopenharmony_ci	unsigned int cpu;
514162306a36Sopenharmony_ci
514262306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
514362306a36Sopenharmony_ci		struct mvpp2_pcpu_stats *cpu_stats;
514462306a36Sopenharmony_ci		u64 rx_packets;
514562306a36Sopenharmony_ci		u64 rx_bytes;
514662306a36Sopenharmony_ci		u64 tx_packets;
514762306a36Sopenharmony_ci		u64 tx_bytes;
514862306a36Sopenharmony_ci
514962306a36Sopenharmony_ci		cpu_stats = per_cpu_ptr(port->stats, cpu);
515062306a36Sopenharmony_ci		do {
515162306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&cpu_stats->syncp);
515262306a36Sopenharmony_ci			rx_packets = cpu_stats->rx_packets;
515362306a36Sopenharmony_ci			rx_bytes   = cpu_stats->rx_bytes;
515462306a36Sopenharmony_ci			tx_packets = cpu_stats->tx_packets;
515562306a36Sopenharmony_ci			tx_bytes   = cpu_stats->tx_bytes;
515662306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&cpu_stats->syncp, start));
515762306a36Sopenharmony_ci
515862306a36Sopenharmony_ci		stats->rx_packets += rx_packets;
515962306a36Sopenharmony_ci		stats->rx_bytes   += rx_bytes;
516062306a36Sopenharmony_ci		stats->tx_packets += tx_packets;
516162306a36Sopenharmony_ci		stats->tx_bytes   += tx_bytes;
516262306a36Sopenharmony_ci	}
516362306a36Sopenharmony_ci
516462306a36Sopenharmony_ci	stats->rx_errors	= dev->stats.rx_errors;
516562306a36Sopenharmony_ci	stats->rx_dropped	= dev->stats.rx_dropped;
516662306a36Sopenharmony_ci	stats->tx_dropped	= dev->stats.tx_dropped;
516762306a36Sopenharmony_ci}
516862306a36Sopenharmony_ci
516962306a36Sopenharmony_cistatic int mvpp2_set_ts_config(struct mvpp2_port *port, struct ifreq *ifr)
517062306a36Sopenharmony_ci{
517162306a36Sopenharmony_ci	struct hwtstamp_config config;
517262306a36Sopenharmony_ci	void __iomem *ptp;
517362306a36Sopenharmony_ci	u32 gcr, int_mask;
517462306a36Sopenharmony_ci
517562306a36Sopenharmony_ci	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
517662306a36Sopenharmony_ci		return -EFAULT;
517762306a36Sopenharmony_ci
517862306a36Sopenharmony_ci	if (config.tx_type != HWTSTAMP_TX_OFF &&
517962306a36Sopenharmony_ci	    config.tx_type != HWTSTAMP_TX_ON)
518062306a36Sopenharmony_ci		return -ERANGE;
518162306a36Sopenharmony_ci
518262306a36Sopenharmony_ci	ptp = port->priv->iface_base + MVPP22_PTP_BASE(port->gop_id);
518362306a36Sopenharmony_ci
518462306a36Sopenharmony_ci	int_mask = gcr = 0;
518562306a36Sopenharmony_ci	if (config.tx_type != HWTSTAMP_TX_OFF) {
518662306a36Sopenharmony_ci		gcr |= MVPP22_PTP_GCR_TSU_ENABLE | MVPP22_PTP_GCR_TX_RESET;
518762306a36Sopenharmony_ci		int_mask |= MVPP22_PTP_INT_MASK_QUEUE1 |
518862306a36Sopenharmony_ci			    MVPP22_PTP_INT_MASK_QUEUE0;
518962306a36Sopenharmony_ci	}
519062306a36Sopenharmony_ci
519162306a36Sopenharmony_ci	/* It seems we must also release the TX reset when enabling the TSU */
519262306a36Sopenharmony_ci	if (config.rx_filter != HWTSTAMP_FILTER_NONE)
519362306a36Sopenharmony_ci		gcr |= MVPP22_PTP_GCR_TSU_ENABLE | MVPP22_PTP_GCR_RX_RESET |
519462306a36Sopenharmony_ci		       MVPP22_PTP_GCR_TX_RESET;
519562306a36Sopenharmony_ci
519662306a36Sopenharmony_ci	if (gcr & MVPP22_PTP_GCR_TSU_ENABLE)
519762306a36Sopenharmony_ci		mvpp22_tai_start(port->priv->tai);
519862306a36Sopenharmony_ci
519962306a36Sopenharmony_ci	if (config.rx_filter != HWTSTAMP_FILTER_NONE) {
520062306a36Sopenharmony_ci		config.rx_filter = HWTSTAMP_FILTER_ALL;
520162306a36Sopenharmony_ci		mvpp2_modify(ptp + MVPP22_PTP_GCR,
520262306a36Sopenharmony_ci			     MVPP22_PTP_GCR_RX_RESET |
520362306a36Sopenharmony_ci			     MVPP22_PTP_GCR_TX_RESET |
520462306a36Sopenharmony_ci			     MVPP22_PTP_GCR_TSU_ENABLE, gcr);
520562306a36Sopenharmony_ci		port->rx_hwtstamp = true;
520662306a36Sopenharmony_ci	} else {
520762306a36Sopenharmony_ci		port->rx_hwtstamp = false;
520862306a36Sopenharmony_ci		mvpp2_modify(ptp + MVPP22_PTP_GCR,
520962306a36Sopenharmony_ci			     MVPP22_PTP_GCR_RX_RESET |
521062306a36Sopenharmony_ci			     MVPP22_PTP_GCR_TX_RESET |
521162306a36Sopenharmony_ci			     MVPP22_PTP_GCR_TSU_ENABLE, gcr);
521262306a36Sopenharmony_ci	}
521362306a36Sopenharmony_ci
521462306a36Sopenharmony_ci	mvpp2_modify(ptp + MVPP22_PTP_INT_MASK,
521562306a36Sopenharmony_ci		     MVPP22_PTP_INT_MASK_QUEUE1 |
521662306a36Sopenharmony_ci		     MVPP22_PTP_INT_MASK_QUEUE0, int_mask);
521762306a36Sopenharmony_ci
521862306a36Sopenharmony_ci	if (!(gcr & MVPP22_PTP_GCR_TSU_ENABLE))
521962306a36Sopenharmony_ci		mvpp22_tai_stop(port->priv->tai);
522062306a36Sopenharmony_ci
522162306a36Sopenharmony_ci	port->tx_hwtstamp_type = config.tx_type;
522262306a36Sopenharmony_ci
522362306a36Sopenharmony_ci	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
522462306a36Sopenharmony_ci		return -EFAULT;
522562306a36Sopenharmony_ci
522662306a36Sopenharmony_ci	return 0;
522762306a36Sopenharmony_ci}
522862306a36Sopenharmony_ci
522962306a36Sopenharmony_cistatic int mvpp2_get_ts_config(struct mvpp2_port *port, struct ifreq *ifr)
523062306a36Sopenharmony_ci{
523162306a36Sopenharmony_ci	struct hwtstamp_config config;
523262306a36Sopenharmony_ci
523362306a36Sopenharmony_ci	memset(&config, 0, sizeof(config));
523462306a36Sopenharmony_ci
523562306a36Sopenharmony_ci	config.tx_type = port->tx_hwtstamp_type;
523662306a36Sopenharmony_ci	config.rx_filter = port->rx_hwtstamp ?
523762306a36Sopenharmony_ci		HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE;
523862306a36Sopenharmony_ci
523962306a36Sopenharmony_ci	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
524062306a36Sopenharmony_ci		return -EFAULT;
524162306a36Sopenharmony_ci
524262306a36Sopenharmony_ci	return 0;
524362306a36Sopenharmony_ci}
524462306a36Sopenharmony_ci
524562306a36Sopenharmony_cistatic int mvpp2_ethtool_get_ts_info(struct net_device *dev,
524662306a36Sopenharmony_ci				     struct ethtool_ts_info *info)
524762306a36Sopenharmony_ci{
524862306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
524962306a36Sopenharmony_ci
525062306a36Sopenharmony_ci	if (!port->hwtstamp)
525162306a36Sopenharmony_ci		return -EOPNOTSUPP;
525262306a36Sopenharmony_ci
525362306a36Sopenharmony_ci	info->phc_index = mvpp22_tai_ptp_clock_index(port->priv->tai);
525462306a36Sopenharmony_ci	info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
525562306a36Sopenharmony_ci				SOF_TIMESTAMPING_RX_SOFTWARE |
525662306a36Sopenharmony_ci				SOF_TIMESTAMPING_SOFTWARE |
525762306a36Sopenharmony_ci				SOF_TIMESTAMPING_TX_HARDWARE |
525862306a36Sopenharmony_ci				SOF_TIMESTAMPING_RX_HARDWARE |
525962306a36Sopenharmony_ci				SOF_TIMESTAMPING_RAW_HARDWARE;
526062306a36Sopenharmony_ci	info->tx_types = BIT(HWTSTAMP_TX_OFF) |
526162306a36Sopenharmony_ci			 BIT(HWTSTAMP_TX_ON);
526262306a36Sopenharmony_ci	info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
526362306a36Sopenharmony_ci			   BIT(HWTSTAMP_FILTER_ALL);
526462306a36Sopenharmony_ci
526562306a36Sopenharmony_ci	return 0;
526662306a36Sopenharmony_ci}
526762306a36Sopenharmony_ci
526862306a36Sopenharmony_cistatic int mvpp2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
526962306a36Sopenharmony_ci{
527062306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
527162306a36Sopenharmony_ci
527262306a36Sopenharmony_ci	switch (cmd) {
527362306a36Sopenharmony_ci	case SIOCSHWTSTAMP:
527462306a36Sopenharmony_ci		if (port->hwtstamp)
527562306a36Sopenharmony_ci			return mvpp2_set_ts_config(port, ifr);
527662306a36Sopenharmony_ci		break;
527762306a36Sopenharmony_ci
527862306a36Sopenharmony_ci	case SIOCGHWTSTAMP:
527962306a36Sopenharmony_ci		if (port->hwtstamp)
528062306a36Sopenharmony_ci			return mvpp2_get_ts_config(port, ifr);
528162306a36Sopenharmony_ci		break;
528262306a36Sopenharmony_ci	}
528362306a36Sopenharmony_ci
528462306a36Sopenharmony_ci	if (!port->phylink)
528562306a36Sopenharmony_ci		return -ENOTSUPP;
528662306a36Sopenharmony_ci
528762306a36Sopenharmony_ci	return phylink_mii_ioctl(port->phylink, ifr, cmd);
528862306a36Sopenharmony_ci}
528962306a36Sopenharmony_ci
529062306a36Sopenharmony_cistatic int mvpp2_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
529162306a36Sopenharmony_ci{
529262306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
529362306a36Sopenharmony_ci	int ret;
529462306a36Sopenharmony_ci
529562306a36Sopenharmony_ci	ret = mvpp2_prs_vid_entry_add(port, vid);
529662306a36Sopenharmony_ci	if (ret)
529762306a36Sopenharmony_ci		netdev_err(dev, "rx-vlan-filter offloading cannot accept more than %d VIDs per port\n",
529862306a36Sopenharmony_ci			   MVPP2_PRS_VLAN_FILT_MAX - 1);
529962306a36Sopenharmony_ci	return ret;
530062306a36Sopenharmony_ci}
530162306a36Sopenharmony_ci
530262306a36Sopenharmony_cistatic int mvpp2_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
530362306a36Sopenharmony_ci{
530462306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
530562306a36Sopenharmony_ci
530662306a36Sopenharmony_ci	mvpp2_prs_vid_entry_remove(port, vid);
530762306a36Sopenharmony_ci	return 0;
530862306a36Sopenharmony_ci}
530962306a36Sopenharmony_ci
531062306a36Sopenharmony_cistatic int mvpp2_set_features(struct net_device *dev,
531162306a36Sopenharmony_ci			      netdev_features_t features)
531262306a36Sopenharmony_ci{
531362306a36Sopenharmony_ci	netdev_features_t changed = dev->features ^ features;
531462306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
531562306a36Sopenharmony_ci
531662306a36Sopenharmony_ci	if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) {
531762306a36Sopenharmony_ci		if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
531862306a36Sopenharmony_ci			mvpp2_prs_vid_enable_filtering(port);
531962306a36Sopenharmony_ci		} else {
532062306a36Sopenharmony_ci			/* Invalidate all registered VID filters for this
532162306a36Sopenharmony_ci			 * port
532262306a36Sopenharmony_ci			 */
532362306a36Sopenharmony_ci			mvpp2_prs_vid_remove_all(port);
532462306a36Sopenharmony_ci
532562306a36Sopenharmony_ci			mvpp2_prs_vid_disable_filtering(port);
532662306a36Sopenharmony_ci		}
532762306a36Sopenharmony_ci	}
532862306a36Sopenharmony_ci
532962306a36Sopenharmony_ci	if (changed & NETIF_F_RXHASH) {
533062306a36Sopenharmony_ci		if (features & NETIF_F_RXHASH)
533162306a36Sopenharmony_ci			mvpp22_port_rss_enable(port);
533262306a36Sopenharmony_ci		else
533362306a36Sopenharmony_ci			mvpp22_port_rss_disable(port);
533462306a36Sopenharmony_ci	}
533562306a36Sopenharmony_ci
533662306a36Sopenharmony_ci	return 0;
533762306a36Sopenharmony_ci}
533862306a36Sopenharmony_ci
533962306a36Sopenharmony_cistatic int mvpp2_xdp_setup(struct mvpp2_port *port, struct netdev_bpf *bpf)
534062306a36Sopenharmony_ci{
534162306a36Sopenharmony_ci	struct bpf_prog *prog = bpf->prog, *old_prog;
534262306a36Sopenharmony_ci	bool running = netif_running(port->dev);
534362306a36Sopenharmony_ci	bool reset = !prog != !port->xdp_prog;
534462306a36Sopenharmony_ci
534562306a36Sopenharmony_ci	if (port->dev->mtu > MVPP2_MAX_RX_BUF_SIZE) {
534662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(bpf->extack, "MTU too large for XDP");
534762306a36Sopenharmony_ci		return -EOPNOTSUPP;
534862306a36Sopenharmony_ci	}
534962306a36Sopenharmony_ci
535062306a36Sopenharmony_ci	if (!port->priv->percpu_pools) {
535162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(bpf->extack, "Per CPU Pools required for XDP");
535262306a36Sopenharmony_ci		return -EOPNOTSUPP;
535362306a36Sopenharmony_ci	}
535462306a36Sopenharmony_ci
535562306a36Sopenharmony_ci	if (port->ntxqs < num_possible_cpus() * 2) {
535662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(bpf->extack, "XDP_TX needs two TX queues per CPU");
535762306a36Sopenharmony_ci		return -EOPNOTSUPP;
535862306a36Sopenharmony_ci	}
535962306a36Sopenharmony_ci
536062306a36Sopenharmony_ci	/* device is up and bpf is added/removed, must setup the RX queues */
536162306a36Sopenharmony_ci	if (running && reset)
536262306a36Sopenharmony_ci		mvpp2_stop(port->dev);
536362306a36Sopenharmony_ci
536462306a36Sopenharmony_ci	old_prog = xchg(&port->xdp_prog, prog);
536562306a36Sopenharmony_ci	if (old_prog)
536662306a36Sopenharmony_ci		bpf_prog_put(old_prog);
536762306a36Sopenharmony_ci
536862306a36Sopenharmony_ci	/* bpf is just replaced, RXQ and MTU are already setup */
536962306a36Sopenharmony_ci	if (!reset)
537062306a36Sopenharmony_ci		return 0;
537162306a36Sopenharmony_ci
537262306a36Sopenharmony_ci	/* device was up, restore the link */
537362306a36Sopenharmony_ci	if (running)
537462306a36Sopenharmony_ci		mvpp2_open(port->dev);
537562306a36Sopenharmony_ci
537662306a36Sopenharmony_ci	/* Check Page Pool DMA Direction */
537762306a36Sopenharmony_ci	mvpp2_check_pagepool_dma(port);
537862306a36Sopenharmony_ci
537962306a36Sopenharmony_ci	return 0;
538062306a36Sopenharmony_ci}
538162306a36Sopenharmony_ci
538262306a36Sopenharmony_cistatic int mvpp2_xdp(struct net_device *dev, struct netdev_bpf *xdp)
538362306a36Sopenharmony_ci{
538462306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
538562306a36Sopenharmony_ci
538662306a36Sopenharmony_ci	switch (xdp->command) {
538762306a36Sopenharmony_ci	case XDP_SETUP_PROG:
538862306a36Sopenharmony_ci		return mvpp2_xdp_setup(port, xdp);
538962306a36Sopenharmony_ci	default:
539062306a36Sopenharmony_ci		return -EINVAL;
539162306a36Sopenharmony_ci	}
539262306a36Sopenharmony_ci}
539362306a36Sopenharmony_ci
539462306a36Sopenharmony_ci/* Ethtool methods */
539562306a36Sopenharmony_ci
539662306a36Sopenharmony_cistatic int mvpp2_ethtool_nway_reset(struct net_device *dev)
539762306a36Sopenharmony_ci{
539862306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
539962306a36Sopenharmony_ci
540062306a36Sopenharmony_ci	if (!port->phylink)
540162306a36Sopenharmony_ci		return -ENOTSUPP;
540262306a36Sopenharmony_ci
540362306a36Sopenharmony_ci	return phylink_ethtool_nway_reset(port->phylink);
540462306a36Sopenharmony_ci}
540562306a36Sopenharmony_ci
540662306a36Sopenharmony_ci/* Set interrupt coalescing for ethtools */
540762306a36Sopenharmony_cistatic int
540862306a36Sopenharmony_cimvpp2_ethtool_set_coalesce(struct net_device *dev,
540962306a36Sopenharmony_ci			   struct ethtool_coalesce *c,
541062306a36Sopenharmony_ci			   struct kernel_ethtool_coalesce *kernel_coal,
541162306a36Sopenharmony_ci			   struct netlink_ext_ack *extack)
541262306a36Sopenharmony_ci{
541362306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
541462306a36Sopenharmony_ci	int queue;
541562306a36Sopenharmony_ci
541662306a36Sopenharmony_ci	for (queue = 0; queue < port->nrxqs; queue++) {
541762306a36Sopenharmony_ci		struct mvpp2_rx_queue *rxq = port->rxqs[queue];
541862306a36Sopenharmony_ci
541962306a36Sopenharmony_ci		rxq->time_coal = c->rx_coalesce_usecs;
542062306a36Sopenharmony_ci		rxq->pkts_coal = c->rx_max_coalesced_frames;
542162306a36Sopenharmony_ci		mvpp2_rx_pkts_coal_set(port, rxq);
542262306a36Sopenharmony_ci		mvpp2_rx_time_coal_set(port, rxq);
542362306a36Sopenharmony_ci	}
542462306a36Sopenharmony_ci
542562306a36Sopenharmony_ci	if (port->has_tx_irqs) {
542662306a36Sopenharmony_ci		port->tx_time_coal = c->tx_coalesce_usecs;
542762306a36Sopenharmony_ci		mvpp2_tx_time_coal_set(port);
542862306a36Sopenharmony_ci	}
542962306a36Sopenharmony_ci
543062306a36Sopenharmony_ci	for (queue = 0; queue < port->ntxqs; queue++) {
543162306a36Sopenharmony_ci		struct mvpp2_tx_queue *txq = port->txqs[queue];
543262306a36Sopenharmony_ci
543362306a36Sopenharmony_ci		txq->done_pkts_coal = c->tx_max_coalesced_frames;
543462306a36Sopenharmony_ci
543562306a36Sopenharmony_ci		if (port->has_tx_irqs)
543662306a36Sopenharmony_ci			mvpp2_tx_pkts_coal_set(port, txq);
543762306a36Sopenharmony_ci	}
543862306a36Sopenharmony_ci
543962306a36Sopenharmony_ci	return 0;
544062306a36Sopenharmony_ci}
544162306a36Sopenharmony_ci
544262306a36Sopenharmony_ci/* get coalescing for ethtools */
544362306a36Sopenharmony_cistatic int
544462306a36Sopenharmony_cimvpp2_ethtool_get_coalesce(struct net_device *dev,
544562306a36Sopenharmony_ci			   struct ethtool_coalesce *c,
544662306a36Sopenharmony_ci			   struct kernel_ethtool_coalesce *kernel_coal,
544762306a36Sopenharmony_ci			   struct netlink_ext_ack *extack)
544862306a36Sopenharmony_ci{
544962306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
545062306a36Sopenharmony_ci
545162306a36Sopenharmony_ci	c->rx_coalesce_usecs       = port->rxqs[0]->time_coal;
545262306a36Sopenharmony_ci	c->rx_max_coalesced_frames = port->rxqs[0]->pkts_coal;
545362306a36Sopenharmony_ci	c->tx_max_coalesced_frames = port->txqs[0]->done_pkts_coal;
545462306a36Sopenharmony_ci	c->tx_coalesce_usecs       = port->tx_time_coal;
545562306a36Sopenharmony_ci	return 0;
545662306a36Sopenharmony_ci}
545762306a36Sopenharmony_ci
545862306a36Sopenharmony_cistatic void mvpp2_ethtool_get_drvinfo(struct net_device *dev,
545962306a36Sopenharmony_ci				      struct ethtool_drvinfo *drvinfo)
546062306a36Sopenharmony_ci{
546162306a36Sopenharmony_ci	strscpy(drvinfo->driver, MVPP2_DRIVER_NAME,
546262306a36Sopenharmony_ci		sizeof(drvinfo->driver));
546362306a36Sopenharmony_ci	strscpy(drvinfo->version, MVPP2_DRIVER_VERSION,
546462306a36Sopenharmony_ci		sizeof(drvinfo->version));
546562306a36Sopenharmony_ci	strscpy(drvinfo->bus_info, dev_name(&dev->dev),
546662306a36Sopenharmony_ci		sizeof(drvinfo->bus_info));
546762306a36Sopenharmony_ci}
546862306a36Sopenharmony_ci
546962306a36Sopenharmony_cistatic void
547062306a36Sopenharmony_cimvpp2_ethtool_get_ringparam(struct net_device *dev,
547162306a36Sopenharmony_ci			    struct ethtool_ringparam *ring,
547262306a36Sopenharmony_ci			    struct kernel_ethtool_ringparam *kernel_ring,
547362306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
547462306a36Sopenharmony_ci{
547562306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
547662306a36Sopenharmony_ci
547762306a36Sopenharmony_ci	ring->rx_max_pending = MVPP2_MAX_RXD_MAX;
547862306a36Sopenharmony_ci	ring->tx_max_pending = MVPP2_MAX_TXD_MAX;
547962306a36Sopenharmony_ci	ring->rx_pending = port->rx_ring_size;
548062306a36Sopenharmony_ci	ring->tx_pending = port->tx_ring_size;
548162306a36Sopenharmony_ci}
548262306a36Sopenharmony_ci
548362306a36Sopenharmony_cistatic int
548462306a36Sopenharmony_cimvpp2_ethtool_set_ringparam(struct net_device *dev,
548562306a36Sopenharmony_ci			    struct ethtool_ringparam *ring,
548662306a36Sopenharmony_ci			    struct kernel_ethtool_ringparam *kernel_ring,
548762306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
548862306a36Sopenharmony_ci{
548962306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
549062306a36Sopenharmony_ci	u16 prev_rx_ring_size = port->rx_ring_size;
549162306a36Sopenharmony_ci	u16 prev_tx_ring_size = port->tx_ring_size;
549262306a36Sopenharmony_ci	int err;
549362306a36Sopenharmony_ci
549462306a36Sopenharmony_ci	err = mvpp2_check_ringparam_valid(dev, ring);
549562306a36Sopenharmony_ci	if (err)
549662306a36Sopenharmony_ci		return err;
549762306a36Sopenharmony_ci
549862306a36Sopenharmony_ci	if (!netif_running(dev)) {
549962306a36Sopenharmony_ci		port->rx_ring_size = ring->rx_pending;
550062306a36Sopenharmony_ci		port->tx_ring_size = ring->tx_pending;
550162306a36Sopenharmony_ci		return 0;
550262306a36Sopenharmony_ci	}
550362306a36Sopenharmony_ci
550462306a36Sopenharmony_ci	/* The interface is running, so we have to force a
550562306a36Sopenharmony_ci	 * reallocation of the queues
550662306a36Sopenharmony_ci	 */
550762306a36Sopenharmony_ci	mvpp2_stop_dev(port);
550862306a36Sopenharmony_ci	mvpp2_cleanup_rxqs(port);
550962306a36Sopenharmony_ci	mvpp2_cleanup_txqs(port);
551062306a36Sopenharmony_ci
551162306a36Sopenharmony_ci	port->rx_ring_size = ring->rx_pending;
551262306a36Sopenharmony_ci	port->tx_ring_size = ring->tx_pending;
551362306a36Sopenharmony_ci
551462306a36Sopenharmony_ci	err = mvpp2_setup_rxqs(port);
551562306a36Sopenharmony_ci	if (err) {
551662306a36Sopenharmony_ci		/* Reallocate Rx queues with the original ring size */
551762306a36Sopenharmony_ci		port->rx_ring_size = prev_rx_ring_size;
551862306a36Sopenharmony_ci		ring->rx_pending = prev_rx_ring_size;
551962306a36Sopenharmony_ci		err = mvpp2_setup_rxqs(port);
552062306a36Sopenharmony_ci		if (err)
552162306a36Sopenharmony_ci			goto err_out;
552262306a36Sopenharmony_ci	}
552362306a36Sopenharmony_ci	err = mvpp2_setup_txqs(port);
552462306a36Sopenharmony_ci	if (err) {
552562306a36Sopenharmony_ci		/* Reallocate Tx queues with the original ring size */
552662306a36Sopenharmony_ci		port->tx_ring_size = prev_tx_ring_size;
552762306a36Sopenharmony_ci		ring->tx_pending = prev_tx_ring_size;
552862306a36Sopenharmony_ci		err = mvpp2_setup_txqs(port);
552962306a36Sopenharmony_ci		if (err)
553062306a36Sopenharmony_ci			goto err_clean_rxqs;
553162306a36Sopenharmony_ci	}
553262306a36Sopenharmony_ci
553362306a36Sopenharmony_ci	mvpp2_start_dev(port);
553462306a36Sopenharmony_ci	mvpp2_egress_enable(port);
553562306a36Sopenharmony_ci	mvpp2_ingress_enable(port);
553662306a36Sopenharmony_ci
553762306a36Sopenharmony_ci	return 0;
553862306a36Sopenharmony_ci
553962306a36Sopenharmony_cierr_clean_rxqs:
554062306a36Sopenharmony_ci	mvpp2_cleanup_rxqs(port);
554162306a36Sopenharmony_cierr_out:
554262306a36Sopenharmony_ci	netdev_err(dev, "failed to change ring parameters");
554362306a36Sopenharmony_ci	return err;
554462306a36Sopenharmony_ci}
554562306a36Sopenharmony_ci
554662306a36Sopenharmony_cistatic void mvpp2_ethtool_get_pause_param(struct net_device *dev,
554762306a36Sopenharmony_ci					  struct ethtool_pauseparam *pause)
554862306a36Sopenharmony_ci{
554962306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
555062306a36Sopenharmony_ci
555162306a36Sopenharmony_ci	if (!port->phylink)
555262306a36Sopenharmony_ci		return;
555362306a36Sopenharmony_ci
555462306a36Sopenharmony_ci	phylink_ethtool_get_pauseparam(port->phylink, pause);
555562306a36Sopenharmony_ci}
555662306a36Sopenharmony_ci
555762306a36Sopenharmony_cistatic int mvpp2_ethtool_set_pause_param(struct net_device *dev,
555862306a36Sopenharmony_ci					 struct ethtool_pauseparam *pause)
555962306a36Sopenharmony_ci{
556062306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
556162306a36Sopenharmony_ci
556262306a36Sopenharmony_ci	if (!port->phylink)
556362306a36Sopenharmony_ci		return -ENOTSUPP;
556462306a36Sopenharmony_ci
556562306a36Sopenharmony_ci	return phylink_ethtool_set_pauseparam(port->phylink, pause);
556662306a36Sopenharmony_ci}
556762306a36Sopenharmony_ci
556862306a36Sopenharmony_cistatic int mvpp2_ethtool_get_link_ksettings(struct net_device *dev,
556962306a36Sopenharmony_ci					    struct ethtool_link_ksettings *cmd)
557062306a36Sopenharmony_ci{
557162306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
557262306a36Sopenharmony_ci
557362306a36Sopenharmony_ci	if (!port->phylink)
557462306a36Sopenharmony_ci		return -ENOTSUPP;
557562306a36Sopenharmony_ci
557662306a36Sopenharmony_ci	return phylink_ethtool_ksettings_get(port->phylink, cmd);
557762306a36Sopenharmony_ci}
557862306a36Sopenharmony_ci
557962306a36Sopenharmony_cistatic int mvpp2_ethtool_set_link_ksettings(struct net_device *dev,
558062306a36Sopenharmony_ci					    const struct ethtool_link_ksettings *cmd)
558162306a36Sopenharmony_ci{
558262306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
558362306a36Sopenharmony_ci
558462306a36Sopenharmony_ci	if (!port->phylink)
558562306a36Sopenharmony_ci		return -ENOTSUPP;
558662306a36Sopenharmony_ci
558762306a36Sopenharmony_ci	return phylink_ethtool_ksettings_set(port->phylink, cmd);
558862306a36Sopenharmony_ci}
558962306a36Sopenharmony_ci
559062306a36Sopenharmony_cistatic int mvpp2_ethtool_get_rxnfc(struct net_device *dev,
559162306a36Sopenharmony_ci				   struct ethtool_rxnfc *info, u32 *rules)
559262306a36Sopenharmony_ci{
559362306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
559462306a36Sopenharmony_ci	int ret = 0, i, loc = 0;
559562306a36Sopenharmony_ci
559662306a36Sopenharmony_ci	if (!mvpp22_rss_is_supported(port))
559762306a36Sopenharmony_ci		return -EOPNOTSUPP;
559862306a36Sopenharmony_ci
559962306a36Sopenharmony_ci	switch (info->cmd) {
560062306a36Sopenharmony_ci	case ETHTOOL_GRXFH:
560162306a36Sopenharmony_ci		ret = mvpp2_ethtool_rxfh_get(port, info);
560262306a36Sopenharmony_ci		break;
560362306a36Sopenharmony_ci	case ETHTOOL_GRXRINGS:
560462306a36Sopenharmony_ci		info->data = port->nrxqs;
560562306a36Sopenharmony_ci		break;
560662306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRLCNT:
560762306a36Sopenharmony_ci		info->rule_cnt = port->n_rfs_rules;
560862306a36Sopenharmony_ci		break;
560962306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRULE:
561062306a36Sopenharmony_ci		ret = mvpp2_ethtool_cls_rule_get(port, info);
561162306a36Sopenharmony_ci		break;
561262306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRLALL:
561362306a36Sopenharmony_ci		for (i = 0; i < MVPP2_N_RFS_ENTRIES_PER_FLOW; i++) {
561462306a36Sopenharmony_ci			if (loc == info->rule_cnt) {
561562306a36Sopenharmony_ci				ret = -EMSGSIZE;
561662306a36Sopenharmony_ci				break;
561762306a36Sopenharmony_ci			}
561862306a36Sopenharmony_ci
561962306a36Sopenharmony_ci			if (port->rfs_rules[i])
562062306a36Sopenharmony_ci				rules[loc++] = i;
562162306a36Sopenharmony_ci		}
562262306a36Sopenharmony_ci		break;
562362306a36Sopenharmony_ci	default:
562462306a36Sopenharmony_ci		return -ENOTSUPP;
562562306a36Sopenharmony_ci	}
562662306a36Sopenharmony_ci
562762306a36Sopenharmony_ci	return ret;
562862306a36Sopenharmony_ci}
562962306a36Sopenharmony_ci
563062306a36Sopenharmony_cistatic int mvpp2_ethtool_set_rxnfc(struct net_device *dev,
563162306a36Sopenharmony_ci				   struct ethtool_rxnfc *info)
563262306a36Sopenharmony_ci{
563362306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
563462306a36Sopenharmony_ci	int ret = 0;
563562306a36Sopenharmony_ci
563662306a36Sopenharmony_ci	if (!mvpp22_rss_is_supported(port))
563762306a36Sopenharmony_ci		return -EOPNOTSUPP;
563862306a36Sopenharmony_ci
563962306a36Sopenharmony_ci	switch (info->cmd) {
564062306a36Sopenharmony_ci	case ETHTOOL_SRXFH:
564162306a36Sopenharmony_ci		ret = mvpp2_ethtool_rxfh_set(port, info);
564262306a36Sopenharmony_ci		break;
564362306a36Sopenharmony_ci	case ETHTOOL_SRXCLSRLINS:
564462306a36Sopenharmony_ci		ret = mvpp2_ethtool_cls_rule_ins(port, info);
564562306a36Sopenharmony_ci		break;
564662306a36Sopenharmony_ci	case ETHTOOL_SRXCLSRLDEL:
564762306a36Sopenharmony_ci		ret = mvpp2_ethtool_cls_rule_del(port, info);
564862306a36Sopenharmony_ci		break;
564962306a36Sopenharmony_ci	default:
565062306a36Sopenharmony_ci		return -EOPNOTSUPP;
565162306a36Sopenharmony_ci	}
565262306a36Sopenharmony_ci	return ret;
565362306a36Sopenharmony_ci}
565462306a36Sopenharmony_ci
565562306a36Sopenharmony_cistatic u32 mvpp2_ethtool_get_rxfh_indir_size(struct net_device *dev)
565662306a36Sopenharmony_ci{
565762306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
565862306a36Sopenharmony_ci
565962306a36Sopenharmony_ci	return mvpp22_rss_is_supported(port) ? MVPP22_RSS_TABLE_ENTRIES : 0;
566062306a36Sopenharmony_ci}
566162306a36Sopenharmony_ci
566262306a36Sopenharmony_cistatic int mvpp2_ethtool_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
566362306a36Sopenharmony_ci				  u8 *hfunc)
566462306a36Sopenharmony_ci{
566562306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
566662306a36Sopenharmony_ci	int ret = 0;
566762306a36Sopenharmony_ci
566862306a36Sopenharmony_ci	if (!mvpp22_rss_is_supported(port))
566962306a36Sopenharmony_ci		return -EOPNOTSUPP;
567062306a36Sopenharmony_ci
567162306a36Sopenharmony_ci	if (indir)
567262306a36Sopenharmony_ci		ret = mvpp22_port_rss_ctx_indir_get(port, 0, indir);
567362306a36Sopenharmony_ci
567462306a36Sopenharmony_ci	if (hfunc)
567562306a36Sopenharmony_ci		*hfunc = ETH_RSS_HASH_CRC32;
567662306a36Sopenharmony_ci
567762306a36Sopenharmony_ci	return ret;
567862306a36Sopenharmony_ci}
567962306a36Sopenharmony_ci
568062306a36Sopenharmony_cistatic int mvpp2_ethtool_set_rxfh(struct net_device *dev, const u32 *indir,
568162306a36Sopenharmony_ci				  const u8 *key, const u8 hfunc)
568262306a36Sopenharmony_ci{
568362306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
568462306a36Sopenharmony_ci	int ret = 0;
568562306a36Sopenharmony_ci
568662306a36Sopenharmony_ci	if (!mvpp22_rss_is_supported(port))
568762306a36Sopenharmony_ci		return -EOPNOTSUPP;
568862306a36Sopenharmony_ci
568962306a36Sopenharmony_ci	if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_CRC32)
569062306a36Sopenharmony_ci		return -EOPNOTSUPP;
569162306a36Sopenharmony_ci
569262306a36Sopenharmony_ci	if (key)
569362306a36Sopenharmony_ci		return -EOPNOTSUPP;
569462306a36Sopenharmony_ci
569562306a36Sopenharmony_ci	if (indir)
569662306a36Sopenharmony_ci		ret = mvpp22_port_rss_ctx_indir_set(port, 0, indir);
569762306a36Sopenharmony_ci
569862306a36Sopenharmony_ci	return ret;
569962306a36Sopenharmony_ci}
570062306a36Sopenharmony_ci
570162306a36Sopenharmony_cistatic int mvpp2_ethtool_get_rxfh_context(struct net_device *dev, u32 *indir,
570262306a36Sopenharmony_ci					  u8 *key, u8 *hfunc, u32 rss_context)
570362306a36Sopenharmony_ci{
570462306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
570562306a36Sopenharmony_ci	int ret = 0;
570662306a36Sopenharmony_ci
570762306a36Sopenharmony_ci	if (!mvpp22_rss_is_supported(port))
570862306a36Sopenharmony_ci		return -EOPNOTSUPP;
570962306a36Sopenharmony_ci	if (rss_context >= MVPP22_N_RSS_TABLES)
571062306a36Sopenharmony_ci		return -EINVAL;
571162306a36Sopenharmony_ci
571262306a36Sopenharmony_ci	if (hfunc)
571362306a36Sopenharmony_ci		*hfunc = ETH_RSS_HASH_CRC32;
571462306a36Sopenharmony_ci
571562306a36Sopenharmony_ci	if (indir)
571662306a36Sopenharmony_ci		ret = mvpp22_port_rss_ctx_indir_get(port, rss_context, indir);
571762306a36Sopenharmony_ci
571862306a36Sopenharmony_ci	return ret;
571962306a36Sopenharmony_ci}
572062306a36Sopenharmony_ci
572162306a36Sopenharmony_cistatic int mvpp2_ethtool_set_rxfh_context(struct net_device *dev,
572262306a36Sopenharmony_ci					  const u32 *indir, const u8 *key,
572362306a36Sopenharmony_ci					  const u8 hfunc, u32 *rss_context,
572462306a36Sopenharmony_ci					  bool delete)
572562306a36Sopenharmony_ci{
572662306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
572762306a36Sopenharmony_ci	int ret;
572862306a36Sopenharmony_ci
572962306a36Sopenharmony_ci	if (!mvpp22_rss_is_supported(port))
573062306a36Sopenharmony_ci		return -EOPNOTSUPP;
573162306a36Sopenharmony_ci
573262306a36Sopenharmony_ci	if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_CRC32)
573362306a36Sopenharmony_ci		return -EOPNOTSUPP;
573462306a36Sopenharmony_ci
573562306a36Sopenharmony_ci	if (key)
573662306a36Sopenharmony_ci		return -EOPNOTSUPP;
573762306a36Sopenharmony_ci
573862306a36Sopenharmony_ci	if (delete)
573962306a36Sopenharmony_ci		return mvpp22_port_rss_ctx_delete(port, *rss_context);
574062306a36Sopenharmony_ci
574162306a36Sopenharmony_ci	if (*rss_context == ETH_RXFH_CONTEXT_ALLOC) {
574262306a36Sopenharmony_ci		ret = mvpp22_port_rss_ctx_create(port, rss_context);
574362306a36Sopenharmony_ci		if (ret)
574462306a36Sopenharmony_ci			return ret;
574562306a36Sopenharmony_ci	}
574662306a36Sopenharmony_ci
574762306a36Sopenharmony_ci	return mvpp22_port_rss_ctx_indir_set(port, *rss_context, indir);
574862306a36Sopenharmony_ci}
574962306a36Sopenharmony_ci/* Device ops */
575062306a36Sopenharmony_ci
575162306a36Sopenharmony_cistatic const struct net_device_ops mvpp2_netdev_ops = {
575262306a36Sopenharmony_ci	.ndo_open		= mvpp2_open,
575362306a36Sopenharmony_ci	.ndo_stop		= mvpp2_stop,
575462306a36Sopenharmony_ci	.ndo_start_xmit		= mvpp2_tx,
575562306a36Sopenharmony_ci	.ndo_set_rx_mode	= mvpp2_set_rx_mode,
575662306a36Sopenharmony_ci	.ndo_set_mac_address	= mvpp2_set_mac_address,
575762306a36Sopenharmony_ci	.ndo_change_mtu		= mvpp2_change_mtu,
575862306a36Sopenharmony_ci	.ndo_get_stats64	= mvpp2_get_stats64,
575962306a36Sopenharmony_ci	.ndo_eth_ioctl		= mvpp2_ioctl,
576062306a36Sopenharmony_ci	.ndo_vlan_rx_add_vid	= mvpp2_vlan_rx_add_vid,
576162306a36Sopenharmony_ci	.ndo_vlan_rx_kill_vid	= mvpp2_vlan_rx_kill_vid,
576262306a36Sopenharmony_ci	.ndo_set_features	= mvpp2_set_features,
576362306a36Sopenharmony_ci	.ndo_bpf		= mvpp2_xdp,
576462306a36Sopenharmony_ci	.ndo_xdp_xmit		= mvpp2_xdp_xmit,
576562306a36Sopenharmony_ci};
576662306a36Sopenharmony_ci
576762306a36Sopenharmony_cistatic const struct ethtool_ops mvpp2_eth_tool_ops = {
576862306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
576962306a36Sopenharmony_ci				     ETHTOOL_COALESCE_MAX_FRAMES,
577062306a36Sopenharmony_ci	.nway_reset		= mvpp2_ethtool_nway_reset,
577162306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
577262306a36Sopenharmony_ci	.get_ts_info		= mvpp2_ethtool_get_ts_info,
577362306a36Sopenharmony_ci	.set_coalesce		= mvpp2_ethtool_set_coalesce,
577462306a36Sopenharmony_ci	.get_coalesce		= mvpp2_ethtool_get_coalesce,
577562306a36Sopenharmony_ci	.get_drvinfo		= mvpp2_ethtool_get_drvinfo,
577662306a36Sopenharmony_ci	.get_ringparam		= mvpp2_ethtool_get_ringparam,
577762306a36Sopenharmony_ci	.set_ringparam		= mvpp2_ethtool_set_ringparam,
577862306a36Sopenharmony_ci	.get_strings		= mvpp2_ethtool_get_strings,
577962306a36Sopenharmony_ci	.get_ethtool_stats	= mvpp2_ethtool_get_stats,
578062306a36Sopenharmony_ci	.get_sset_count		= mvpp2_ethtool_get_sset_count,
578162306a36Sopenharmony_ci	.get_pauseparam		= mvpp2_ethtool_get_pause_param,
578262306a36Sopenharmony_ci	.set_pauseparam		= mvpp2_ethtool_set_pause_param,
578362306a36Sopenharmony_ci	.get_link_ksettings	= mvpp2_ethtool_get_link_ksettings,
578462306a36Sopenharmony_ci	.set_link_ksettings	= mvpp2_ethtool_set_link_ksettings,
578562306a36Sopenharmony_ci	.get_rxnfc		= mvpp2_ethtool_get_rxnfc,
578662306a36Sopenharmony_ci	.set_rxnfc		= mvpp2_ethtool_set_rxnfc,
578762306a36Sopenharmony_ci	.get_rxfh_indir_size	= mvpp2_ethtool_get_rxfh_indir_size,
578862306a36Sopenharmony_ci	.get_rxfh		= mvpp2_ethtool_get_rxfh,
578962306a36Sopenharmony_ci	.set_rxfh		= mvpp2_ethtool_set_rxfh,
579062306a36Sopenharmony_ci	.get_rxfh_context	= mvpp2_ethtool_get_rxfh_context,
579162306a36Sopenharmony_ci	.set_rxfh_context	= mvpp2_ethtool_set_rxfh_context,
579262306a36Sopenharmony_ci};
579362306a36Sopenharmony_ci
579462306a36Sopenharmony_ci/* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that
579562306a36Sopenharmony_ci * had a single IRQ defined per-port.
579662306a36Sopenharmony_ci */
579762306a36Sopenharmony_cistatic int mvpp2_simple_queue_vectors_init(struct mvpp2_port *port,
579862306a36Sopenharmony_ci					   struct device_node *port_node)
579962306a36Sopenharmony_ci{
580062306a36Sopenharmony_ci	struct mvpp2_queue_vector *v = &port->qvecs[0];
580162306a36Sopenharmony_ci
580262306a36Sopenharmony_ci	v->first_rxq = 0;
580362306a36Sopenharmony_ci	v->nrxqs = port->nrxqs;
580462306a36Sopenharmony_ci	v->type = MVPP2_QUEUE_VECTOR_SHARED;
580562306a36Sopenharmony_ci	v->sw_thread_id = 0;
580662306a36Sopenharmony_ci	v->sw_thread_mask = *cpumask_bits(cpu_online_mask);
580762306a36Sopenharmony_ci	v->port = port;
580862306a36Sopenharmony_ci	v->irq = irq_of_parse_and_map(port_node, 0);
580962306a36Sopenharmony_ci	if (v->irq <= 0)
581062306a36Sopenharmony_ci		return -EINVAL;
581162306a36Sopenharmony_ci	netif_napi_add(port->dev, &v->napi, mvpp2_poll);
581262306a36Sopenharmony_ci
581362306a36Sopenharmony_ci	port->nqvecs = 1;
581462306a36Sopenharmony_ci
581562306a36Sopenharmony_ci	return 0;
581662306a36Sopenharmony_ci}
581762306a36Sopenharmony_ci
581862306a36Sopenharmony_cistatic int mvpp2_multi_queue_vectors_init(struct mvpp2_port *port,
581962306a36Sopenharmony_ci					  struct device_node *port_node)
582062306a36Sopenharmony_ci{
582162306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
582262306a36Sopenharmony_ci	struct mvpp2_queue_vector *v;
582362306a36Sopenharmony_ci	int i, ret;
582462306a36Sopenharmony_ci
582562306a36Sopenharmony_ci	switch (queue_mode) {
582662306a36Sopenharmony_ci	case MVPP2_QDIST_SINGLE_MODE:
582762306a36Sopenharmony_ci		port->nqvecs = priv->nthreads + 1;
582862306a36Sopenharmony_ci		break;
582962306a36Sopenharmony_ci	case MVPP2_QDIST_MULTI_MODE:
583062306a36Sopenharmony_ci		port->nqvecs = priv->nthreads;
583162306a36Sopenharmony_ci		break;
583262306a36Sopenharmony_ci	}
583362306a36Sopenharmony_ci
583462306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++) {
583562306a36Sopenharmony_ci		char irqname[16];
583662306a36Sopenharmony_ci
583762306a36Sopenharmony_ci		v = port->qvecs + i;
583862306a36Sopenharmony_ci
583962306a36Sopenharmony_ci		v->port = port;
584062306a36Sopenharmony_ci		v->type = MVPP2_QUEUE_VECTOR_PRIVATE;
584162306a36Sopenharmony_ci		v->sw_thread_id = i;
584262306a36Sopenharmony_ci		v->sw_thread_mask = BIT(i);
584362306a36Sopenharmony_ci
584462306a36Sopenharmony_ci		if (port->flags & MVPP2_F_DT_COMPAT)
584562306a36Sopenharmony_ci			snprintf(irqname, sizeof(irqname), "tx-cpu%d", i);
584662306a36Sopenharmony_ci		else
584762306a36Sopenharmony_ci			snprintf(irqname, sizeof(irqname), "hif%d", i);
584862306a36Sopenharmony_ci
584962306a36Sopenharmony_ci		if (queue_mode == MVPP2_QDIST_MULTI_MODE) {
585062306a36Sopenharmony_ci			v->first_rxq = i;
585162306a36Sopenharmony_ci			v->nrxqs = 1;
585262306a36Sopenharmony_ci		} else if (queue_mode == MVPP2_QDIST_SINGLE_MODE &&
585362306a36Sopenharmony_ci			   i == (port->nqvecs - 1)) {
585462306a36Sopenharmony_ci			v->first_rxq = 0;
585562306a36Sopenharmony_ci			v->nrxqs = port->nrxqs;
585662306a36Sopenharmony_ci			v->type = MVPP2_QUEUE_VECTOR_SHARED;
585762306a36Sopenharmony_ci
585862306a36Sopenharmony_ci			if (port->flags & MVPP2_F_DT_COMPAT)
585962306a36Sopenharmony_ci				strncpy(irqname, "rx-shared", sizeof(irqname));
586062306a36Sopenharmony_ci		}
586162306a36Sopenharmony_ci
586262306a36Sopenharmony_ci		if (port_node)
586362306a36Sopenharmony_ci			v->irq = of_irq_get_byname(port_node, irqname);
586462306a36Sopenharmony_ci		else
586562306a36Sopenharmony_ci			v->irq = fwnode_irq_get(port->fwnode, i);
586662306a36Sopenharmony_ci		if (v->irq <= 0) {
586762306a36Sopenharmony_ci			ret = -EINVAL;
586862306a36Sopenharmony_ci			goto err;
586962306a36Sopenharmony_ci		}
587062306a36Sopenharmony_ci
587162306a36Sopenharmony_ci		netif_napi_add(port->dev, &v->napi, mvpp2_poll);
587262306a36Sopenharmony_ci	}
587362306a36Sopenharmony_ci
587462306a36Sopenharmony_ci	return 0;
587562306a36Sopenharmony_ci
587662306a36Sopenharmony_cierr:
587762306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++)
587862306a36Sopenharmony_ci		irq_dispose_mapping(port->qvecs[i].irq);
587962306a36Sopenharmony_ci	return ret;
588062306a36Sopenharmony_ci}
588162306a36Sopenharmony_ci
588262306a36Sopenharmony_cistatic int mvpp2_queue_vectors_init(struct mvpp2_port *port,
588362306a36Sopenharmony_ci				    struct device_node *port_node)
588462306a36Sopenharmony_ci{
588562306a36Sopenharmony_ci	if (port->has_tx_irqs)
588662306a36Sopenharmony_ci		return mvpp2_multi_queue_vectors_init(port, port_node);
588762306a36Sopenharmony_ci	else
588862306a36Sopenharmony_ci		return mvpp2_simple_queue_vectors_init(port, port_node);
588962306a36Sopenharmony_ci}
589062306a36Sopenharmony_ci
589162306a36Sopenharmony_cistatic void mvpp2_queue_vectors_deinit(struct mvpp2_port *port)
589262306a36Sopenharmony_ci{
589362306a36Sopenharmony_ci	int i;
589462306a36Sopenharmony_ci
589562306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++)
589662306a36Sopenharmony_ci		irq_dispose_mapping(port->qvecs[i].irq);
589762306a36Sopenharmony_ci}
589862306a36Sopenharmony_ci
589962306a36Sopenharmony_ci/* Configure Rx queue group interrupt for this port */
590062306a36Sopenharmony_cistatic void mvpp2_rx_irqs_setup(struct mvpp2_port *port)
590162306a36Sopenharmony_ci{
590262306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
590362306a36Sopenharmony_ci	u32 val;
590462306a36Sopenharmony_ci	int i;
590562306a36Sopenharmony_ci
590662306a36Sopenharmony_ci	if (priv->hw_version == MVPP21) {
590762306a36Sopenharmony_ci		mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(port->id),
590862306a36Sopenharmony_ci			    port->nrxqs);
590962306a36Sopenharmony_ci		return;
591062306a36Sopenharmony_ci	}
591162306a36Sopenharmony_ci
591262306a36Sopenharmony_ci	/* Handle the more complicated PPv2.2 and PPv2.3 case */
591362306a36Sopenharmony_ci	for (i = 0; i < port->nqvecs; i++) {
591462306a36Sopenharmony_ci		struct mvpp2_queue_vector *qv = port->qvecs + i;
591562306a36Sopenharmony_ci
591662306a36Sopenharmony_ci		if (!qv->nrxqs)
591762306a36Sopenharmony_ci			continue;
591862306a36Sopenharmony_ci
591962306a36Sopenharmony_ci		val = qv->sw_thread_id;
592062306a36Sopenharmony_ci		val |= port->id << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET;
592162306a36Sopenharmony_ci		mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val);
592262306a36Sopenharmony_ci
592362306a36Sopenharmony_ci		val = qv->first_rxq;
592462306a36Sopenharmony_ci		val |= qv->nrxqs << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET;
592562306a36Sopenharmony_ci		mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val);
592662306a36Sopenharmony_ci	}
592762306a36Sopenharmony_ci}
592862306a36Sopenharmony_ci
592962306a36Sopenharmony_ci/* Initialize port HW */
593062306a36Sopenharmony_cistatic int mvpp2_port_init(struct mvpp2_port *port)
593162306a36Sopenharmony_ci{
593262306a36Sopenharmony_ci	struct device *dev = port->dev->dev.parent;
593362306a36Sopenharmony_ci	struct mvpp2 *priv = port->priv;
593462306a36Sopenharmony_ci	struct mvpp2_txq_pcpu *txq_pcpu;
593562306a36Sopenharmony_ci	unsigned int thread;
593662306a36Sopenharmony_ci	int queue, err, val;
593762306a36Sopenharmony_ci
593862306a36Sopenharmony_ci	/* Checks for hardware constraints */
593962306a36Sopenharmony_ci	if (port->first_rxq + port->nrxqs >
594062306a36Sopenharmony_ci	    MVPP2_MAX_PORTS * priv->max_port_rxqs)
594162306a36Sopenharmony_ci		return -EINVAL;
594262306a36Sopenharmony_ci
594362306a36Sopenharmony_ci	if (port->nrxqs > priv->max_port_rxqs || port->ntxqs > MVPP2_MAX_TXQ)
594462306a36Sopenharmony_ci		return -EINVAL;
594562306a36Sopenharmony_ci
594662306a36Sopenharmony_ci	/* Disable port */
594762306a36Sopenharmony_ci	mvpp2_egress_disable(port);
594862306a36Sopenharmony_ci	mvpp2_port_disable(port);
594962306a36Sopenharmony_ci
595062306a36Sopenharmony_ci	if (mvpp2_is_xlg(port->phy_interface)) {
595162306a36Sopenharmony_ci		val = readl(port->base + MVPP22_XLG_CTRL0_REG);
595262306a36Sopenharmony_ci		val &= ~MVPP22_XLG_CTRL0_FORCE_LINK_PASS;
595362306a36Sopenharmony_ci		val |= MVPP22_XLG_CTRL0_FORCE_LINK_DOWN;
595462306a36Sopenharmony_ci		writel(val, port->base + MVPP22_XLG_CTRL0_REG);
595562306a36Sopenharmony_ci	} else {
595662306a36Sopenharmony_ci		val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
595762306a36Sopenharmony_ci		val &= ~MVPP2_GMAC_FORCE_LINK_PASS;
595862306a36Sopenharmony_ci		val |= MVPP2_GMAC_FORCE_LINK_DOWN;
595962306a36Sopenharmony_ci		writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
596062306a36Sopenharmony_ci	}
596162306a36Sopenharmony_ci
596262306a36Sopenharmony_ci	port->tx_time_coal = MVPP2_TXDONE_COAL_USEC;
596362306a36Sopenharmony_ci
596462306a36Sopenharmony_ci	port->txqs = devm_kcalloc(dev, port->ntxqs, sizeof(*port->txqs),
596562306a36Sopenharmony_ci				  GFP_KERNEL);
596662306a36Sopenharmony_ci	if (!port->txqs)
596762306a36Sopenharmony_ci		return -ENOMEM;
596862306a36Sopenharmony_ci
596962306a36Sopenharmony_ci	/* Associate physical Tx queues to this port and initialize.
597062306a36Sopenharmony_ci	 * The mapping is predefined.
597162306a36Sopenharmony_ci	 */
597262306a36Sopenharmony_ci	for (queue = 0; queue < port->ntxqs; queue++) {
597362306a36Sopenharmony_ci		int queue_phy_id = mvpp2_txq_phys(port->id, queue);
597462306a36Sopenharmony_ci		struct mvpp2_tx_queue *txq;
597562306a36Sopenharmony_ci
597662306a36Sopenharmony_ci		txq = devm_kzalloc(dev, sizeof(*txq), GFP_KERNEL);
597762306a36Sopenharmony_ci		if (!txq) {
597862306a36Sopenharmony_ci			err = -ENOMEM;
597962306a36Sopenharmony_ci			goto err_free_percpu;
598062306a36Sopenharmony_ci		}
598162306a36Sopenharmony_ci
598262306a36Sopenharmony_ci		txq->pcpu = alloc_percpu(struct mvpp2_txq_pcpu);
598362306a36Sopenharmony_ci		if (!txq->pcpu) {
598462306a36Sopenharmony_ci			err = -ENOMEM;
598562306a36Sopenharmony_ci			goto err_free_percpu;
598662306a36Sopenharmony_ci		}
598762306a36Sopenharmony_ci
598862306a36Sopenharmony_ci		txq->id = queue_phy_id;
598962306a36Sopenharmony_ci		txq->log_id = queue;
599062306a36Sopenharmony_ci		txq->done_pkts_coal = MVPP2_TXDONE_COAL_PKTS_THRESH;
599162306a36Sopenharmony_ci		for (thread = 0; thread < priv->nthreads; thread++) {
599262306a36Sopenharmony_ci			txq_pcpu = per_cpu_ptr(txq->pcpu, thread);
599362306a36Sopenharmony_ci			txq_pcpu->thread = thread;
599462306a36Sopenharmony_ci		}
599562306a36Sopenharmony_ci
599662306a36Sopenharmony_ci		port->txqs[queue] = txq;
599762306a36Sopenharmony_ci	}
599862306a36Sopenharmony_ci
599962306a36Sopenharmony_ci	port->rxqs = devm_kcalloc(dev, port->nrxqs, sizeof(*port->rxqs),
600062306a36Sopenharmony_ci				  GFP_KERNEL);
600162306a36Sopenharmony_ci	if (!port->rxqs) {
600262306a36Sopenharmony_ci		err = -ENOMEM;
600362306a36Sopenharmony_ci		goto err_free_percpu;
600462306a36Sopenharmony_ci	}
600562306a36Sopenharmony_ci
600662306a36Sopenharmony_ci	/* Allocate and initialize Rx queue for this port */
600762306a36Sopenharmony_ci	for (queue = 0; queue < port->nrxqs; queue++) {
600862306a36Sopenharmony_ci		struct mvpp2_rx_queue *rxq;
600962306a36Sopenharmony_ci
601062306a36Sopenharmony_ci		/* Map physical Rx queue to port's logical Rx queue */
601162306a36Sopenharmony_ci		rxq = devm_kzalloc(dev, sizeof(*rxq), GFP_KERNEL);
601262306a36Sopenharmony_ci		if (!rxq) {
601362306a36Sopenharmony_ci			err = -ENOMEM;
601462306a36Sopenharmony_ci			goto err_free_percpu;
601562306a36Sopenharmony_ci		}
601662306a36Sopenharmony_ci		/* Map this Rx queue to a physical queue */
601762306a36Sopenharmony_ci		rxq->id = port->first_rxq + queue;
601862306a36Sopenharmony_ci		rxq->port = port->id;
601962306a36Sopenharmony_ci		rxq->logic_rxq = queue;
602062306a36Sopenharmony_ci
602162306a36Sopenharmony_ci		port->rxqs[queue] = rxq;
602262306a36Sopenharmony_ci	}
602362306a36Sopenharmony_ci
602462306a36Sopenharmony_ci	mvpp2_rx_irqs_setup(port);
602562306a36Sopenharmony_ci
602662306a36Sopenharmony_ci	/* Create Rx descriptor rings */
602762306a36Sopenharmony_ci	for (queue = 0; queue < port->nrxqs; queue++) {
602862306a36Sopenharmony_ci		struct mvpp2_rx_queue *rxq = port->rxqs[queue];
602962306a36Sopenharmony_ci
603062306a36Sopenharmony_ci		rxq->size = port->rx_ring_size;
603162306a36Sopenharmony_ci		rxq->pkts_coal = MVPP2_RX_COAL_PKTS;
603262306a36Sopenharmony_ci		rxq->time_coal = MVPP2_RX_COAL_USEC;
603362306a36Sopenharmony_ci	}
603462306a36Sopenharmony_ci
603562306a36Sopenharmony_ci	mvpp2_ingress_disable(port);
603662306a36Sopenharmony_ci
603762306a36Sopenharmony_ci	/* Port default configuration */
603862306a36Sopenharmony_ci	mvpp2_defaults_set(port);
603962306a36Sopenharmony_ci
604062306a36Sopenharmony_ci	/* Port's classifier configuration */
604162306a36Sopenharmony_ci	mvpp2_cls_oversize_rxq_set(port);
604262306a36Sopenharmony_ci	mvpp2_cls_port_config(port);
604362306a36Sopenharmony_ci
604462306a36Sopenharmony_ci	if (mvpp22_rss_is_supported(port))
604562306a36Sopenharmony_ci		mvpp22_port_rss_init(port);
604662306a36Sopenharmony_ci
604762306a36Sopenharmony_ci	/* Provide an initial Rx packet size */
604862306a36Sopenharmony_ci	port->pkt_size = MVPP2_RX_PKT_SIZE(port->dev->mtu);
604962306a36Sopenharmony_ci
605062306a36Sopenharmony_ci	/* Initialize pools for swf */
605162306a36Sopenharmony_ci	err = mvpp2_swf_bm_pool_init(port);
605262306a36Sopenharmony_ci	if (err)
605362306a36Sopenharmony_ci		goto err_free_percpu;
605462306a36Sopenharmony_ci
605562306a36Sopenharmony_ci	/* Clear all port stats */
605662306a36Sopenharmony_ci	mvpp2_read_stats(port);
605762306a36Sopenharmony_ci	memset(port->ethtool_stats, 0,
605862306a36Sopenharmony_ci	       MVPP2_N_ETHTOOL_STATS(port->ntxqs, port->nrxqs) * sizeof(u64));
605962306a36Sopenharmony_ci
606062306a36Sopenharmony_ci	return 0;
606162306a36Sopenharmony_ci
606262306a36Sopenharmony_cierr_free_percpu:
606362306a36Sopenharmony_ci	for (queue = 0; queue < port->ntxqs; queue++) {
606462306a36Sopenharmony_ci		if (!port->txqs[queue])
606562306a36Sopenharmony_ci			continue;
606662306a36Sopenharmony_ci		free_percpu(port->txqs[queue]->pcpu);
606762306a36Sopenharmony_ci	}
606862306a36Sopenharmony_ci	return err;
606962306a36Sopenharmony_ci}
607062306a36Sopenharmony_ci
607162306a36Sopenharmony_cistatic bool mvpp22_port_has_legacy_tx_irqs(struct device_node *port_node,
607262306a36Sopenharmony_ci					   unsigned long *flags)
607362306a36Sopenharmony_ci{
607462306a36Sopenharmony_ci	char *irqs[5] = { "rx-shared", "tx-cpu0", "tx-cpu1", "tx-cpu2",
607562306a36Sopenharmony_ci			  "tx-cpu3" };
607662306a36Sopenharmony_ci	int i;
607762306a36Sopenharmony_ci
607862306a36Sopenharmony_ci	for (i = 0; i < 5; i++)
607962306a36Sopenharmony_ci		if (of_property_match_string(port_node, "interrupt-names",
608062306a36Sopenharmony_ci					     irqs[i]) < 0)
608162306a36Sopenharmony_ci			return false;
608262306a36Sopenharmony_ci
608362306a36Sopenharmony_ci	*flags |= MVPP2_F_DT_COMPAT;
608462306a36Sopenharmony_ci	return true;
608562306a36Sopenharmony_ci}
608662306a36Sopenharmony_ci
608762306a36Sopenharmony_ci/* Checks if the port dt description has the required Tx interrupts:
608862306a36Sopenharmony_ci * - PPv2.1: there are no such interrupts.
608962306a36Sopenharmony_ci * - PPv2.2 and PPv2.3:
609062306a36Sopenharmony_ci *   - The old DTs have: "rx-shared", "tx-cpuX" with X in [0...3]
609162306a36Sopenharmony_ci *   - The new ones have: "hifX" with X in [0..8]
609262306a36Sopenharmony_ci *
609362306a36Sopenharmony_ci * All those variants are supported to keep the backward compatibility.
609462306a36Sopenharmony_ci */
609562306a36Sopenharmony_cistatic bool mvpp2_port_has_irqs(struct mvpp2 *priv,
609662306a36Sopenharmony_ci				struct device_node *port_node,
609762306a36Sopenharmony_ci				unsigned long *flags)
609862306a36Sopenharmony_ci{
609962306a36Sopenharmony_ci	char name[5];
610062306a36Sopenharmony_ci	int i;
610162306a36Sopenharmony_ci
610262306a36Sopenharmony_ci	/* ACPI */
610362306a36Sopenharmony_ci	if (!port_node)
610462306a36Sopenharmony_ci		return true;
610562306a36Sopenharmony_ci
610662306a36Sopenharmony_ci	if (priv->hw_version == MVPP21)
610762306a36Sopenharmony_ci		return false;
610862306a36Sopenharmony_ci
610962306a36Sopenharmony_ci	if (mvpp22_port_has_legacy_tx_irqs(port_node, flags))
611062306a36Sopenharmony_ci		return true;
611162306a36Sopenharmony_ci
611262306a36Sopenharmony_ci	for (i = 0; i < MVPP2_MAX_THREADS; i++) {
611362306a36Sopenharmony_ci		snprintf(name, 5, "hif%d", i);
611462306a36Sopenharmony_ci		if (of_property_match_string(port_node, "interrupt-names",
611562306a36Sopenharmony_ci					     name) < 0)
611662306a36Sopenharmony_ci			return false;
611762306a36Sopenharmony_ci	}
611862306a36Sopenharmony_ci
611962306a36Sopenharmony_ci	return true;
612062306a36Sopenharmony_ci}
612162306a36Sopenharmony_ci
612262306a36Sopenharmony_cistatic int mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv,
612362306a36Sopenharmony_ci				    struct fwnode_handle *fwnode,
612462306a36Sopenharmony_ci				    char **mac_from)
612562306a36Sopenharmony_ci{
612662306a36Sopenharmony_ci	struct mvpp2_port *port = netdev_priv(dev);
612762306a36Sopenharmony_ci	char hw_mac_addr[ETH_ALEN] = {0};
612862306a36Sopenharmony_ci	char fw_mac_addr[ETH_ALEN];
612962306a36Sopenharmony_ci	int ret;
613062306a36Sopenharmony_ci
613162306a36Sopenharmony_ci	if (!fwnode_get_mac_address(fwnode, fw_mac_addr)) {
613262306a36Sopenharmony_ci		*mac_from = "firmware node";
613362306a36Sopenharmony_ci		eth_hw_addr_set(dev, fw_mac_addr);
613462306a36Sopenharmony_ci		return 0;
613562306a36Sopenharmony_ci	}
613662306a36Sopenharmony_ci
613762306a36Sopenharmony_ci	if (priv->hw_version == MVPP21) {
613862306a36Sopenharmony_ci		mvpp21_get_mac_address(port, hw_mac_addr);
613962306a36Sopenharmony_ci		if (is_valid_ether_addr(hw_mac_addr)) {
614062306a36Sopenharmony_ci			*mac_from = "hardware";
614162306a36Sopenharmony_ci			eth_hw_addr_set(dev, hw_mac_addr);
614262306a36Sopenharmony_ci			return 0;
614362306a36Sopenharmony_ci		}
614462306a36Sopenharmony_ci	}
614562306a36Sopenharmony_ci
614662306a36Sopenharmony_ci	/* Only valid on OF enabled platforms */
614762306a36Sopenharmony_ci	ret = of_get_mac_address_nvmem(to_of_node(fwnode), fw_mac_addr);
614862306a36Sopenharmony_ci	if (ret == -EPROBE_DEFER)
614962306a36Sopenharmony_ci		return ret;
615062306a36Sopenharmony_ci	if (!ret) {
615162306a36Sopenharmony_ci		*mac_from = "nvmem cell";
615262306a36Sopenharmony_ci		eth_hw_addr_set(dev, fw_mac_addr);
615362306a36Sopenharmony_ci		return 0;
615462306a36Sopenharmony_ci	}
615562306a36Sopenharmony_ci
615662306a36Sopenharmony_ci	*mac_from = "random";
615762306a36Sopenharmony_ci	eth_hw_addr_random(dev);
615862306a36Sopenharmony_ci
615962306a36Sopenharmony_ci	return 0;
616062306a36Sopenharmony_ci}
616162306a36Sopenharmony_ci
616262306a36Sopenharmony_cistatic struct mvpp2_port *mvpp2_phylink_to_port(struct phylink_config *config)
616362306a36Sopenharmony_ci{
616462306a36Sopenharmony_ci	return container_of(config, struct mvpp2_port, phylink_config);
616562306a36Sopenharmony_ci}
616662306a36Sopenharmony_ci
616762306a36Sopenharmony_cistatic struct mvpp2_port *mvpp2_pcs_xlg_to_port(struct phylink_pcs *pcs)
616862306a36Sopenharmony_ci{
616962306a36Sopenharmony_ci	return container_of(pcs, struct mvpp2_port, pcs_xlg);
617062306a36Sopenharmony_ci}
617162306a36Sopenharmony_ci
617262306a36Sopenharmony_cistatic struct mvpp2_port *mvpp2_pcs_gmac_to_port(struct phylink_pcs *pcs)
617362306a36Sopenharmony_ci{
617462306a36Sopenharmony_ci	return container_of(pcs, struct mvpp2_port, pcs_gmac);
617562306a36Sopenharmony_ci}
617662306a36Sopenharmony_ci
617762306a36Sopenharmony_cistatic void mvpp2_xlg_pcs_get_state(struct phylink_pcs *pcs,
617862306a36Sopenharmony_ci				    struct phylink_link_state *state)
617962306a36Sopenharmony_ci{
618062306a36Sopenharmony_ci	struct mvpp2_port *port = mvpp2_pcs_xlg_to_port(pcs);
618162306a36Sopenharmony_ci	u32 val;
618262306a36Sopenharmony_ci
618362306a36Sopenharmony_ci	if (port->phy_interface == PHY_INTERFACE_MODE_5GBASER)
618462306a36Sopenharmony_ci		state->speed = SPEED_5000;
618562306a36Sopenharmony_ci	else
618662306a36Sopenharmony_ci		state->speed = SPEED_10000;
618762306a36Sopenharmony_ci	state->duplex = 1;
618862306a36Sopenharmony_ci	state->an_complete = 1;
618962306a36Sopenharmony_ci
619062306a36Sopenharmony_ci	val = readl(port->base + MVPP22_XLG_STATUS);
619162306a36Sopenharmony_ci	state->link = !!(val & MVPP22_XLG_STATUS_LINK_UP);
619262306a36Sopenharmony_ci
619362306a36Sopenharmony_ci	state->pause = 0;
619462306a36Sopenharmony_ci	val = readl(port->base + MVPP22_XLG_CTRL0_REG);
619562306a36Sopenharmony_ci	if (val & MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN)
619662306a36Sopenharmony_ci		state->pause |= MLO_PAUSE_TX;
619762306a36Sopenharmony_ci	if (val & MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN)
619862306a36Sopenharmony_ci		state->pause |= MLO_PAUSE_RX;
619962306a36Sopenharmony_ci}
620062306a36Sopenharmony_ci
620162306a36Sopenharmony_cistatic int mvpp2_xlg_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
620262306a36Sopenharmony_ci				phy_interface_t interface,
620362306a36Sopenharmony_ci				const unsigned long *advertising,
620462306a36Sopenharmony_ci				bool permit_pause_to_mac)
620562306a36Sopenharmony_ci{
620662306a36Sopenharmony_ci	return 0;
620762306a36Sopenharmony_ci}
620862306a36Sopenharmony_ci
620962306a36Sopenharmony_cistatic const struct phylink_pcs_ops mvpp2_phylink_xlg_pcs_ops = {
621062306a36Sopenharmony_ci	.pcs_get_state = mvpp2_xlg_pcs_get_state,
621162306a36Sopenharmony_ci	.pcs_config = mvpp2_xlg_pcs_config,
621262306a36Sopenharmony_ci};
621362306a36Sopenharmony_ci
621462306a36Sopenharmony_cistatic int mvpp2_gmac_pcs_validate(struct phylink_pcs *pcs,
621562306a36Sopenharmony_ci				   unsigned long *supported,
621662306a36Sopenharmony_ci				   const struct phylink_link_state *state)
621762306a36Sopenharmony_ci{
621862306a36Sopenharmony_ci	/* When in 802.3z mode, we must have AN enabled:
621962306a36Sopenharmony_ci	 * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
622062306a36Sopenharmony_ci	 * When <PortType> = 1 (1000BASE-X) this field must be set to 1.
622162306a36Sopenharmony_ci	 */
622262306a36Sopenharmony_ci	if (phy_interface_mode_is_8023z(state->interface) &&
622362306a36Sopenharmony_ci	    !phylink_test(state->advertising, Autoneg))
622462306a36Sopenharmony_ci		return -EINVAL;
622562306a36Sopenharmony_ci
622662306a36Sopenharmony_ci	return 0;
622762306a36Sopenharmony_ci}
622862306a36Sopenharmony_ci
622962306a36Sopenharmony_cistatic void mvpp2_gmac_pcs_get_state(struct phylink_pcs *pcs,
623062306a36Sopenharmony_ci				     struct phylink_link_state *state)
623162306a36Sopenharmony_ci{
623262306a36Sopenharmony_ci	struct mvpp2_port *port = mvpp2_pcs_gmac_to_port(pcs);
623362306a36Sopenharmony_ci	u32 val;
623462306a36Sopenharmony_ci
623562306a36Sopenharmony_ci	val = readl(port->base + MVPP2_GMAC_STATUS0);
623662306a36Sopenharmony_ci
623762306a36Sopenharmony_ci	state->an_complete = !!(val & MVPP2_GMAC_STATUS0_AN_COMPLETE);
623862306a36Sopenharmony_ci	state->link = !!(val & MVPP2_GMAC_STATUS0_LINK_UP);
623962306a36Sopenharmony_ci	state->duplex = !!(val & MVPP2_GMAC_STATUS0_FULL_DUPLEX);
624062306a36Sopenharmony_ci
624162306a36Sopenharmony_ci	switch (port->phy_interface) {
624262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_1000BASEX:
624362306a36Sopenharmony_ci		state->speed = SPEED_1000;
624462306a36Sopenharmony_ci		break;
624562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_2500BASEX:
624662306a36Sopenharmony_ci		state->speed = SPEED_2500;
624762306a36Sopenharmony_ci		break;
624862306a36Sopenharmony_ci	default:
624962306a36Sopenharmony_ci		if (val & MVPP2_GMAC_STATUS0_GMII_SPEED)
625062306a36Sopenharmony_ci			state->speed = SPEED_1000;
625162306a36Sopenharmony_ci		else if (val & MVPP2_GMAC_STATUS0_MII_SPEED)
625262306a36Sopenharmony_ci			state->speed = SPEED_100;
625362306a36Sopenharmony_ci		else
625462306a36Sopenharmony_ci			state->speed = SPEED_10;
625562306a36Sopenharmony_ci	}
625662306a36Sopenharmony_ci
625762306a36Sopenharmony_ci	state->pause = 0;
625862306a36Sopenharmony_ci	if (val & MVPP2_GMAC_STATUS0_RX_PAUSE)
625962306a36Sopenharmony_ci		state->pause |= MLO_PAUSE_RX;
626062306a36Sopenharmony_ci	if (val & MVPP2_GMAC_STATUS0_TX_PAUSE)
626162306a36Sopenharmony_ci		state->pause |= MLO_PAUSE_TX;
626262306a36Sopenharmony_ci}
626362306a36Sopenharmony_ci
626462306a36Sopenharmony_cistatic int mvpp2_gmac_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
626562306a36Sopenharmony_ci				 phy_interface_t interface,
626662306a36Sopenharmony_ci				 const unsigned long *advertising,
626762306a36Sopenharmony_ci				 bool permit_pause_to_mac)
626862306a36Sopenharmony_ci{
626962306a36Sopenharmony_ci	struct mvpp2_port *port = mvpp2_pcs_gmac_to_port(pcs);
627062306a36Sopenharmony_ci	u32 mask, val, an, old_an, changed;
627162306a36Sopenharmony_ci
627262306a36Sopenharmony_ci	mask = MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS |
627362306a36Sopenharmony_ci	       MVPP2_GMAC_IN_BAND_AUTONEG |
627462306a36Sopenharmony_ci	       MVPP2_GMAC_AN_SPEED_EN |
627562306a36Sopenharmony_ci	       MVPP2_GMAC_FLOW_CTRL_AUTONEG |
627662306a36Sopenharmony_ci	       MVPP2_GMAC_AN_DUPLEX_EN;
627762306a36Sopenharmony_ci
627862306a36Sopenharmony_ci	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
627962306a36Sopenharmony_ci		mask |= MVPP2_GMAC_CONFIG_MII_SPEED |
628062306a36Sopenharmony_ci			MVPP2_GMAC_CONFIG_GMII_SPEED |
628162306a36Sopenharmony_ci			MVPP2_GMAC_CONFIG_FULL_DUPLEX;
628262306a36Sopenharmony_ci		val = MVPP2_GMAC_IN_BAND_AUTONEG;
628362306a36Sopenharmony_ci
628462306a36Sopenharmony_ci		if (interface == PHY_INTERFACE_MODE_SGMII) {
628562306a36Sopenharmony_ci			/* SGMII mode receives the speed and duplex from PHY */
628662306a36Sopenharmony_ci			val |= MVPP2_GMAC_AN_SPEED_EN |
628762306a36Sopenharmony_ci			       MVPP2_GMAC_AN_DUPLEX_EN;
628862306a36Sopenharmony_ci		} else {
628962306a36Sopenharmony_ci			/* 802.3z mode has fixed speed and duplex */
629062306a36Sopenharmony_ci			val |= MVPP2_GMAC_CONFIG_GMII_SPEED |
629162306a36Sopenharmony_ci			       MVPP2_GMAC_CONFIG_FULL_DUPLEX;
629262306a36Sopenharmony_ci
629362306a36Sopenharmony_ci			/* The FLOW_CTRL_AUTONEG bit selects either the hardware
629462306a36Sopenharmony_ci			 * automatically or the bits in MVPP22_GMAC_CTRL_4_REG
629562306a36Sopenharmony_ci			 * manually controls the GMAC pause modes.
629662306a36Sopenharmony_ci			 */
629762306a36Sopenharmony_ci			if (permit_pause_to_mac)
629862306a36Sopenharmony_ci				val |= MVPP2_GMAC_FLOW_CTRL_AUTONEG;
629962306a36Sopenharmony_ci
630062306a36Sopenharmony_ci			/* Configure advertisement bits */
630162306a36Sopenharmony_ci			mask |= MVPP2_GMAC_FC_ADV_EN | MVPP2_GMAC_FC_ADV_ASM_EN;
630262306a36Sopenharmony_ci			if (phylink_test(advertising, Pause))
630362306a36Sopenharmony_ci				val |= MVPP2_GMAC_FC_ADV_EN;
630462306a36Sopenharmony_ci			if (phylink_test(advertising, Asym_Pause))
630562306a36Sopenharmony_ci				val |= MVPP2_GMAC_FC_ADV_ASM_EN;
630662306a36Sopenharmony_ci		}
630762306a36Sopenharmony_ci	} else {
630862306a36Sopenharmony_ci		val = 0;
630962306a36Sopenharmony_ci	}
631062306a36Sopenharmony_ci
631162306a36Sopenharmony_ci	old_an = an = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
631262306a36Sopenharmony_ci	an = (an & ~mask) | val;
631362306a36Sopenharmony_ci	changed = an ^ old_an;
631462306a36Sopenharmony_ci	if (changed)
631562306a36Sopenharmony_ci		writel(an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
631662306a36Sopenharmony_ci
631762306a36Sopenharmony_ci	/* We are only interested in the advertisement bits changing */
631862306a36Sopenharmony_ci	return changed & (MVPP2_GMAC_FC_ADV_EN | MVPP2_GMAC_FC_ADV_ASM_EN);
631962306a36Sopenharmony_ci}
632062306a36Sopenharmony_ci
632162306a36Sopenharmony_cistatic void mvpp2_gmac_pcs_an_restart(struct phylink_pcs *pcs)
632262306a36Sopenharmony_ci{
632362306a36Sopenharmony_ci	struct mvpp2_port *port = mvpp2_pcs_gmac_to_port(pcs);
632462306a36Sopenharmony_ci	u32 val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
632562306a36Sopenharmony_ci
632662306a36Sopenharmony_ci	writel(val | MVPP2_GMAC_IN_BAND_RESTART_AN,
632762306a36Sopenharmony_ci	       port->base + MVPP2_GMAC_AUTONEG_CONFIG);
632862306a36Sopenharmony_ci	writel(val & ~MVPP2_GMAC_IN_BAND_RESTART_AN,
632962306a36Sopenharmony_ci	       port->base + MVPP2_GMAC_AUTONEG_CONFIG);
633062306a36Sopenharmony_ci}
633162306a36Sopenharmony_ci
633262306a36Sopenharmony_cistatic const struct phylink_pcs_ops mvpp2_phylink_gmac_pcs_ops = {
633362306a36Sopenharmony_ci	.pcs_validate = mvpp2_gmac_pcs_validate,
633462306a36Sopenharmony_ci	.pcs_get_state = mvpp2_gmac_pcs_get_state,
633562306a36Sopenharmony_ci	.pcs_config = mvpp2_gmac_pcs_config,
633662306a36Sopenharmony_ci	.pcs_an_restart = mvpp2_gmac_pcs_an_restart,
633762306a36Sopenharmony_ci};
633862306a36Sopenharmony_ci
633962306a36Sopenharmony_cistatic void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode,
634062306a36Sopenharmony_ci			     const struct phylink_link_state *state)
634162306a36Sopenharmony_ci{
634262306a36Sopenharmony_ci	u32 val;
634362306a36Sopenharmony_ci
634462306a36Sopenharmony_ci	mvpp2_modify(port->base + MVPP22_XLG_CTRL0_REG,
634562306a36Sopenharmony_ci		     MVPP22_XLG_CTRL0_MAC_RESET_DIS,
634662306a36Sopenharmony_ci		     MVPP22_XLG_CTRL0_MAC_RESET_DIS);
634762306a36Sopenharmony_ci	mvpp2_modify(port->base + MVPP22_XLG_CTRL4_REG,
634862306a36Sopenharmony_ci		     MVPP22_XLG_CTRL4_MACMODSELECT_GMAC |
634962306a36Sopenharmony_ci		     MVPP22_XLG_CTRL4_EN_IDLE_CHECK |
635062306a36Sopenharmony_ci		     MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC,
635162306a36Sopenharmony_ci		     MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC);
635262306a36Sopenharmony_ci
635362306a36Sopenharmony_ci	/* Wait for reset to deassert */
635462306a36Sopenharmony_ci	do {
635562306a36Sopenharmony_ci		val = readl(port->base + MVPP22_XLG_CTRL0_REG);
635662306a36Sopenharmony_ci	} while (!(val & MVPP22_XLG_CTRL0_MAC_RESET_DIS));
635762306a36Sopenharmony_ci}
635862306a36Sopenharmony_ci
635962306a36Sopenharmony_cistatic void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode,
636062306a36Sopenharmony_ci			      const struct phylink_link_state *state)
636162306a36Sopenharmony_ci{
636262306a36Sopenharmony_ci	u32 old_ctrl0, ctrl0;
636362306a36Sopenharmony_ci	u32 old_ctrl2, ctrl2;
636462306a36Sopenharmony_ci	u32 old_ctrl4, ctrl4;
636562306a36Sopenharmony_ci
636662306a36Sopenharmony_ci	old_ctrl0 = ctrl0 = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
636762306a36Sopenharmony_ci	old_ctrl2 = ctrl2 = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
636862306a36Sopenharmony_ci	old_ctrl4 = ctrl4 = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
636962306a36Sopenharmony_ci
637062306a36Sopenharmony_ci	ctrl0 &= ~MVPP2_GMAC_PORT_TYPE_MASK;
637162306a36Sopenharmony_ci	ctrl2 &= ~(MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK | MVPP2_GMAC_FLOW_CTRL_MASK);
637262306a36Sopenharmony_ci
637362306a36Sopenharmony_ci	/* Configure port type */
637462306a36Sopenharmony_ci	if (phy_interface_mode_is_8023z(state->interface)) {
637562306a36Sopenharmony_ci		ctrl2 |= MVPP2_GMAC_PCS_ENABLE_MASK;
637662306a36Sopenharmony_ci		ctrl4 &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL;
637762306a36Sopenharmony_ci		ctrl4 |= MVPP22_CTRL4_SYNC_BYPASS_DIS |
637862306a36Sopenharmony_ci			 MVPP22_CTRL4_DP_CLK_SEL |
637962306a36Sopenharmony_ci			 MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
638062306a36Sopenharmony_ci	} else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
638162306a36Sopenharmony_ci		ctrl2 |= MVPP2_GMAC_PCS_ENABLE_MASK | MVPP2_GMAC_INBAND_AN_MASK;
638262306a36Sopenharmony_ci		ctrl4 &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL;
638362306a36Sopenharmony_ci		ctrl4 |= MVPP22_CTRL4_SYNC_BYPASS_DIS |
638462306a36Sopenharmony_ci			 MVPP22_CTRL4_DP_CLK_SEL |
638562306a36Sopenharmony_ci			 MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
638662306a36Sopenharmony_ci	} else if (phy_interface_mode_is_rgmii(state->interface)) {
638762306a36Sopenharmony_ci		ctrl4 &= ~MVPP22_CTRL4_DP_CLK_SEL;
638862306a36Sopenharmony_ci		ctrl4 |= MVPP22_CTRL4_EXT_PIN_GMII_SEL |
638962306a36Sopenharmony_ci			 MVPP22_CTRL4_SYNC_BYPASS_DIS |
639062306a36Sopenharmony_ci			 MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
639162306a36Sopenharmony_ci	}
639262306a36Sopenharmony_ci
639362306a36Sopenharmony_ci	/* Configure negotiation style */
639462306a36Sopenharmony_ci	if (!phylink_autoneg_inband(mode)) {
639562306a36Sopenharmony_ci		/* Phy or fixed speed - no in-band AN, nothing to do, leave the
639662306a36Sopenharmony_ci		 * configured speed, duplex and flow control as-is.
639762306a36Sopenharmony_ci		 */
639862306a36Sopenharmony_ci	} else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
639962306a36Sopenharmony_ci		/* SGMII in-band mode receives the speed and duplex from
640062306a36Sopenharmony_ci		 * the PHY. Flow control information is not received. */
640162306a36Sopenharmony_ci	} else if (phy_interface_mode_is_8023z(state->interface)) {
640262306a36Sopenharmony_ci		/* 1000BaseX and 2500BaseX ports cannot negotiate speed nor can
640362306a36Sopenharmony_ci		 * they negotiate duplex: they are always operating with a fixed
640462306a36Sopenharmony_ci		 * speed of 1000/2500Mbps in full duplex, so force 1000/2500
640562306a36Sopenharmony_ci		 * speed and full duplex here.
640662306a36Sopenharmony_ci		 */
640762306a36Sopenharmony_ci		ctrl0 |= MVPP2_GMAC_PORT_TYPE_MASK;
640862306a36Sopenharmony_ci	}
640962306a36Sopenharmony_ci
641062306a36Sopenharmony_ci	if (old_ctrl0 != ctrl0)
641162306a36Sopenharmony_ci		writel(ctrl0, port->base + MVPP2_GMAC_CTRL_0_REG);
641262306a36Sopenharmony_ci	if (old_ctrl2 != ctrl2)
641362306a36Sopenharmony_ci		writel(ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG);
641462306a36Sopenharmony_ci	if (old_ctrl4 != ctrl4)
641562306a36Sopenharmony_ci		writel(ctrl4, port->base + MVPP22_GMAC_CTRL_4_REG);
641662306a36Sopenharmony_ci}
641762306a36Sopenharmony_ci
641862306a36Sopenharmony_cistatic struct phylink_pcs *mvpp2_select_pcs(struct phylink_config *config,
641962306a36Sopenharmony_ci					    phy_interface_t interface)
642062306a36Sopenharmony_ci{
642162306a36Sopenharmony_ci	struct mvpp2_port *port = mvpp2_phylink_to_port(config);
642262306a36Sopenharmony_ci
642362306a36Sopenharmony_ci	/* Select the appropriate PCS operations depending on the
642462306a36Sopenharmony_ci	 * configured interface mode. We will only switch to a mode
642562306a36Sopenharmony_ci	 * that the validate() checks have already passed.
642662306a36Sopenharmony_ci	 */
642762306a36Sopenharmony_ci	if (mvpp2_is_xlg(interface))
642862306a36Sopenharmony_ci		return &port->pcs_xlg;
642962306a36Sopenharmony_ci	else
643062306a36Sopenharmony_ci		return &port->pcs_gmac;
643162306a36Sopenharmony_ci}
643262306a36Sopenharmony_ci
643362306a36Sopenharmony_cistatic int mvpp2_mac_prepare(struct phylink_config *config, unsigned int mode,
643462306a36Sopenharmony_ci			     phy_interface_t interface)
643562306a36Sopenharmony_ci{
643662306a36Sopenharmony_ci	struct mvpp2_port *port = mvpp2_phylink_to_port(config);
643762306a36Sopenharmony_ci
643862306a36Sopenharmony_ci	/* Check for invalid configuration */
643962306a36Sopenharmony_ci	if (mvpp2_is_xlg(interface) && port->gop_id != 0) {
644062306a36Sopenharmony_ci		netdev_err(port->dev, "Invalid mode on %s\n", port->dev->name);
644162306a36Sopenharmony_ci		return -EINVAL;
644262306a36Sopenharmony_ci	}
644362306a36Sopenharmony_ci
644462306a36Sopenharmony_ci	if (port->phy_interface != interface ||
644562306a36Sopenharmony_ci	    phylink_autoneg_inband(mode)) {
644662306a36Sopenharmony_ci		/* Force the link down when changing the interface or if in
644762306a36Sopenharmony_ci		 * in-band mode to ensure we do not change the configuration
644862306a36Sopenharmony_ci		 * while the hardware is indicating link is up. We force both
644962306a36Sopenharmony_ci		 * XLG and GMAC down to ensure that they're both in a known
645062306a36Sopenharmony_ci		 * state.
645162306a36Sopenharmony_ci		 */
645262306a36Sopenharmony_ci		mvpp2_modify(port->base + MVPP2_GMAC_AUTONEG_CONFIG,
645362306a36Sopenharmony_ci			     MVPP2_GMAC_FORCE_LINK_PASS |
645462306a36Sopenharmony_ci			     MVPP2_GMAC_FORCE_LINK_DOWN,
645562306a36Sopenharmony_ci			     MVPP2_GMAC_FORCE_LINK_DOWN);
645662306a36Sopenharmony_ci
645762306a36Sopenharmony_ci		if (mvpp2_port_supports_xlg(port))
645862306a36Sopenharmony_ci			mvpp2_modify(port->base + MVPP22_XLG_CTRL0_REG,
645962306a36Sopenharmony_ci				     MVPP22_XLG_CTRL0_FORCE_LINK_PASS |
646062306a36Sopenharmony_ci				     MVPP22_XLG_CTRL0_FORCE_LINK_DOWN,
646162306a36Sopenharmony_ci				     MVPP22_XLG_CTRL0_FORCE_LINK_DOWN);
646262306a36Sopenharmony_ci	}
646362306a36Sopenharmony_ci
646462306a36Sopenharmony_ci	/* Make sure the port is disabled when reconfiguring the mode */
646562306a36Sopenharmony_ci	mvpp2_port_disable(port);
646662306a36Sopenharmony_ci
646762306a36Sopenharmony_ci	if (port->phy_interface != interface) {
646862306a36Sopenharmony_ci		/* Place GMAC into reset */
646962306a36Sopenharmony_ci		mvpp2_modify(port->base + MVPP2_GMAC_CTRL_2_REG,
647062306a36Sopenharmony_ci			     MVPP2_GMAC_PORT_RESET_MASK,
647162306a36Sopenharmony_ci			     MVPP2_GMAC_PORT_RESET_MASK);
647262306a36Sopenharmony_ci
647362306a36Sopenharmony_ci		if (port->priv->hw_version >= MVPP22) {
647462306a36Sopenharmony_ci			mvpp22_gop_mask_irq(port);
647562306a36Sopenharmony_ci
647662306a36Sopenharmony_ci			phy_power_off(port->comphy);
647762306a36Sopenharmony_ci
647862306a36Sopenharmony_ci			/* Reconfigure the serdes lanes */
647962306a36Sopenharmony_ci			mvpp22_mode_reconfigure(port, interface);
648062306a36Sopenharmony_ci		}
648162306a36Sopenharmony_ci	}
648262306a36Sopenharmony_ci
648362306a36Sopenharmony_ci	return 0;
648462306a36Sopenharmony_ci}
648562306a36Sopenharmony_ci
648662306a36Sopenharmony_cistatic void mvpp2_mac_config(struct phylink_config *config, unsigned int mode,
648762306a36Sopenharmony_ci			     const struct phylink_link_state *state)
648862306a36Sopenharmony_ci{
648962306a36Sopenharmony_ci	struct mvpp2_port *port = mvpp2_phylink_to_port(config);
649062306a36Sopenharmony_ci
649162306a36Sopenharmony_ci	/* mac (re)configuration */
649262306a36Sopenharmony_ci	if (mvpp2_is_xlg(state->interface))
649362306a36Sopenharmony_ci		mvpp2_xlg_config(port, mode, state);
649462306a36Sopenharmony_ci	else if (phy_interface_mode_is_rgmii(state->interface) ||
649562306a36Sopenharmony_ci		 phy_interface_mode_is_8023z(state->interface) ||
649662306a36Sopenharmony_ci		 state->interface == PHY_INTERFACE_MODE_SGMII)
649762306a36Sopenharmony_ci		mvpp2_gmac_config(port, mode, state);
649862306a36Sopenharmony_ci
649962306a36Sopenharmony_ci	if (port->priv->hw_version == MVPP21 && port->flags & MVPP2_F_LOOPBACK)
650062306a36Sopenharmony_ci		mvpp2_port_loopback_set(port, state);
650162306a36Sopenharmony_ci}
650262306a36Sopenharmony_ci
650362306a36Sopenharmony_cistatic int mvpp2_mac_finish(struct phylink_config *config, unsigned int mode,
650462306a36Sopenharmony_ci			    phy_interface_t interface)
650562306a36Sopenharmony_ci{
650662306a36Sopenharmony_ci	struct mvpp2_port *port = mvpp2_phylink_to_port(config);
650762306a36Sopenharmony_ci
650862306a36Sopenharmony_ci	if (port->priv->hw_version >= MVPP22 &&
650962306a36Sopenharmony_ci	    port->phy_interface != interface) {
651062306a36Sopenharmony_ci		port->phy_interface = interface;
651162306a36Sopenharmony_ci
651262306a36Sopenharmony_ci		/* Unmask interrupts */
651362306a36Sopenharmony_ci		mvpp22_gop_unmask_irq(port);
651462306a36Sopenharmony_ci	}
651562306a36Sopenharmony_ci
651662306a36Sopenharmony_ci	if (!mvpp2_is_xlg(interface)) {
651762306a36Sopenharmony_ci		/* Release GMAC reset and wait */
651862306a36Sopenharmony_ci		mvpp2_modify(port->base + MVPP2_GMAC_CTRL_2_REG,
651962306a36Sopenharmony_ci			     MVPP2_GMAC_PORT_RESET_MASK, 0);
652062306a36Sopenharmony_ci
652162306a36Sopenharmony_ci		while (readl(port->base + MVPP2_GMAC_CTRL_2_REG) &
652262306a36Sopenharmony_ci		       MVPP2_GMAC_PORT_RESET_MASK)
652362306a36Sopenharmony_ci			continue;
652462306a36Sopenharmony_ci	}
652562306a36Sopenharmony_ci
652662306a36Sopenharmony_ci	mvpp2_port_enable(port);
652762306a36Sopenharmony_ci
652862306a36Sopenharmony_ci	/* Allow the link to come up if in in-band mode, otherwise the
652962306a36Sopenharmony_ci	 * link is forced via mac_link_down()/mac_link_up()
653062306a36Sopenharmony_ci	 */
653162306a36Sopenharmony_ci	if (phylink_autoneg_inband(mode)) {
653262306a36Sopenharmony_ci		if (mvpp2_is_xlg(interface))
653362306a36Sopenharmony_ci			mvpp2_modify(port->base + MVPP22_XLG_CTRL0_REG,
653462306a36Sopenharmony_ci				     MVPP22_XLG_CTRL0_FORCE_LINK_PASS |
653562306a36Sopenharmony_ci				     MVPP22_XLG_CTRL0_FORCE_LINK_DOWN, 0);
653662306a36Sopenharmony_ci		else
653762306a36Sopenharmony_ci			mvpp2_modify(port->base + MVPP2_GMAC_AUTONEG_CONFIG,
653862306a36Sopenharmony_ci				     MVPP2_GMAC_FORCE_LINK_PASS |
653962306a36Sopenharmony_ci				     MVPP2_GMAC_FORCE_LINK_DOWN, 0);
654062306a36Sopenharmony_ci	}
654162306a36Sopenharmony_ci
654262306a36Sopenharmony_ci	return 0;
654362306a36Sopenharmony_ci}
654462306a36Sopenharmony_ci
654562306a36Sopenharmony_cistatic void mvpp2_mac_link_up(struct phylink_config *config,
654662306a36Sopenharmony_ci			      struct phy_device *phy,
654762306a36Sopenharmony_ci			      unsigned int mode, phy_interface_t interface,
654862306a36Sopenharmony_ci			      int speed, int duplex,
654962306a36Sopenharmony_ci			      bool tx_pause, bool rx_pause)
655062306a36Sopenharmony_ci{
655162306a36Sopenharmony_ci	struct mvpp2_port *port = mvpp2_phylink_to_port(config);
655262306a36Sopenharmony_ci	u32 val;
655362306a36Sopenharmony_ci	int i;
655462306a36Sopenharmony_ci
655562306a36Sopenharmony_ci	if (mvpp2_is_xlg(interface)) {
655662306a36Sopenharmony_ci		if (!phylink_autoneg_inband(mode)) {
655762306a36Sopenharmony_ci			val = MVPP22_XLG_CTRL0_FORCE_LINK_PASS;
655862306a36Sopenharmony_ci			if (tx_pause)
655962306a36Sopenharmony_ci				val |= MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN;
656062306a36Sopenharmony_ci			if (rx_pause)
656162306a36Sopenharmony_ci				val |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN;
656262306a36Sopenharmony_ci
656362306a36Sopenharmony_ci			mvpp2_modify(port->base + MVPP22_XLG_CTRL0_REG,
656462306a36Sopenharmony_ci				     MVPP22_XLG_CTRL0_FORCE_LINK_DOWN |
656562306a36Sopenharmony_ci				     MVPP22_XLG_CTRL0_FORCE_LINK_PASS |
656662306a36Sopenharmony_ci				     MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN |
656762306a36Sopenharmony_ci				     MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN, val);
656862306a36Sopenharmony_ci		}
656962306a36Sopenharmony_ci	} else {
657062306a36Sopenharmony_ci		if (!phylink_autoneg_inband(mode)) {
657162306a36Sopenharmony_ci			val = MVPP2_GMAC_FORCE_LINK_PASS;
657262306a36Sopenharmony_ci
657362306a36Sopenharmony_ci			if (speed == SPEED_1000 || speed == SPEED_2500)
657462306a36Sopenharmony_ci				val |= MVPP2_GMAC_CONFIG_GMII_SPEED;
657562306a36Sopenharmony_ci			else if (speed == SPEED_100)
657662306a36Sopenharmony_ci				val |= MVPP2_GMAC_CONFIG_MII_SPEED;
657762306a36Sopenharmony_ci
657862306a36Sopenharmony_ci			if (duplex == DUPLEX_FULL)
657962306a36Sopenharmony_ci				val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
658062306a36Sopenharmony_ci
658162306a36Sopenharmony_ci			mvpp2_modify(port->base + MVPP2_GMAC_AUTONEG_CONFIG,
658262306a36Sopenharmony_ci				     MVPP2_GMAC_FORCE_LINK_DOWN |
658362306a36Sopenharmony_ci				     MVPP2_GMAC_FORCE_LINK_PASS |
658462306a36Sopenharmony_ci				     MVPP2_GMAC_CONFIG_MII_SPEED |
658562306a36Sopenharmony_ci				     MVPP2_GMAC_CONFIG_GMII_SPEED |
658662306a36Sopenharmony_ci				     MVPP2_GMAC_CONFIG_FULL_DUPLEX, val);
658762306a36Sopenharmony_ci		}
658862306a36Sopenharmony_ci
658962306a36Sopenharmony_ci		/* We can always update the flow control enable bits;
659062306a36Sopenharmony_ci		 * these will only be effective if flow control AN
659162306a36Sopenharmony_ci		 * (MVPP2_GMAC_FLOW_CTRL_AUTONEG) is disabled.
659262306a36Sopenharmony_ci		 */
659362306a36Sopenharmony_ci		val = 0;
659462306a36Sopenharmony_ci		if (tx_pause)
659562306a36Sopenharmony_ci			val |= MVPP22_CTRL4_TX_FC_EN;
659662306a36Sopenharmony_ci		if (rx_pause)
659762306a36Sopenharmony_ci			val |= MVPP22_CTRL4_RX_FC_EN;
659862306a36Sopenharmony_ci
659962306a36Sopenharmony_ci		mvpp2_modify(port->base + MVPP22_GMAC_CTRL_4_REG,
660062306a36Sopenharmony_ci			     MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN,
660162306a36Sopenharmony_ci			     val);
660262306a36Sopenharmony_ci	}
660362306a36Sopenharmony_ci
660462306a36Sopenharmony_ci	if (port->priv->global_tx_fc) {
660562306a36Sopenharmony_ci		port->tx_fc = tx_pause;
660662306a36Sopenharmony_ci		if (tx_pause)
660762306a36Sopenharmony_ci			mvpp2_rxq_enable_fc(port);
660862306a36Sopenharmony_ci		else
660962306a36Sopenharmony_ci			mvpp2_rxq_disable_fc(port);
661062306a36Sopenharmony_ci		if (port->priv->percpu_pools) {
661162306a36Sopenharmony_ci			for (i = 0; i < port->nrxqs; i++)
661262306a36Sopenharmony_ci				mvpp2_bm_pool_update_fc(port, &port->priv->bm_pools[i], tx_pause);
661362306a36Sopenharmony_ci		} else {
661462306a36Sopenharmony_ci			mvpp2_bm_pool_update_fc(port, port->pool_long, tx_pause);
661562306a36Sopenharmony_ci			mvpp2_bm_pool_update_fc(port, port->pool_short, tx_pause);
661662306a36Sopenharmony_ci		}
661762306a36Sopenharmony_ci		if (port->priv->hw_version == MVPP23)
661862306a36Sopenharmony_ci			mvpp23_rx_fifo_fc_en(port->priv, port->id, tx_pause);
661962306a36Sopenharmony_ci	}
662062306a36Sopenharmony_ci
662162306a36Sopenharmony_ci	mvpp2_port_enable(port);
662262306a36Sopenharmony_ci
662362306a36Sopenharmony_ci	mvpp2_egress_enable(port);
662462306a36Sopenharmony_ci	mvpp2_ingress_enable(port);
662562306a36Sopenharmony_ci	netif_tx_wake_all_queues(port->dev);
662662306a36Sopenharmony_ci}
662762306a36Sopenharmony_ci
662862306a36Sopenharmony_cistatic void mvpp2_mac_link_down(struct phylink_config *config,
662962306a36Sopenharmony_ci				unsigned int mode, phy_interface_t interface)
663062306a36Sopenharmony_ci{
663162306a36Sopenharmony_ci	struct mvpp2_port *port = mvpp2_phylink_to_port(config);
663262306a36Sopenharmony_ci	u32 val;
663362306a36Sopenharmony_ci
663462306a36Sopenharmony_ci	if (!phylink_autoneg_inband(mode)) {
663562306a36Sopenharmony_ci		if (mvpp2_is_xlg(interface)) {
663662306a36Sopenharmony_ci			val = readl(port->base + MVPP22_XLG_CTRL0_REG);
663762306a36Sopenharmony_ci			val &= ~MVPP22_XLG_CTRL0_FORCE_LINK_PASS;
663862306a36Sopenharmony_ci			val |= MVPP22_XLG_CTRL0_FORCE_LINK_DOWN;
663962306a36Sopenharmony_ci			writel(val, port->base + MVPP22_XLG_CTRL0_REG);
664062306a36Sopenharmony_ci		} else {
664162306a36Sopenharmony_ci			val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
664262306a36Sopenharmony_ci			val &= ~MVPP2_GMAC_FORCE_LINK_PASS;
664362306a36Sopenharmony_ci			val |= MVPP2_GMAC_FORCE_LINK_DOWN;
664462306a36Sopenharmony_ci			writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
664562306a36Sopenharmony_ci		}
664662306a36Sopenharmony_ci	}
664762306a36Sopenharmony_ci
664862306a36Sopenharmony_ci	netif_tx_stop_all_queues(port->dev);
664962306a36Sopenharmony_ci	mvpp2_egress_disable(port);
665062306a36Sopenharmony_ci	mvpp2_ingress_disable(port);
665162306a36Sopenharmony_ci
665262306a36Sopenharmony_ci	mvpp2_port_disable(port);
665362306a36Sopenharmony_ci}
665462306a36Sopenharmony_ci
665562306a36Sopenharmony_cistatic const struct phylink_mac_ops mvpp2_phylink_ops = {
665662306a36Sopenharmony_ci	.mac_select_pcs = mvpp2_select_pcs,
665762306a36Sopenharmony_ci	.mac_prepare = mvpp2_mac_prepare,
665862306a36Sopenharmony_ci	.mac_config = mvpp2_mac_config,
665962306a36Sopenharmony_ci	.mac_finish = mvpp2_mac_finish,
666062306a36Sopenharmony_ci	.mac_link_up = mvpp2_mac_link_up,
666162306a36Sopenharmony_ci	.mac_link_down = mvpp2_mac_link_down,
666262306a36Sopenharmony_ci};
666362306a36Sopenharmony_ci
666462306a36Sopenharmony_ci/* Work-around for ACPI */
666562306a36Sopenharmony_cistatic void mvpp2_acpi_start(struct mvpp2_port *port)
666662306a36Sopenharmony_ci{
666762306a36Sopenharmony_ci	/* Phylink isn't used as of now for ACPI, so the MAC has to be
666862306a36Sopenharmony_ci	 * configured manually when the interface is started. This will
666962306a36Sopenharmony_ci	 * be removed as soon as the phylink ACPI support lands in.
667062306a36Sopenharmony_ci	 */
667162306a36Sopenharmony_ci	struct phylink_link_state state = {
667262306a36Sopenharmony_ci		.interface = port->phy_interface,
667362306a36Sopenharmony_ci	};
667462306a36Sopenharmony_ci	struct phylink_pcs *pcs;
667562306a36Sopenharmony_ci
667662306a36Sopenharmony_ci	pcs = mvpp2_select_pcs(&port->phylink_config, port->phy_interface);
667762306a36Sopenharmony_ci
667862306a36Sopenharmony_ci	mvpp2_mac_prepare(&port->phylink_config, MLO_AN_INBAND,
667962306a36Sopenharmony_ci			  port->phy_interface);
668062306a36Sopenharmony_ci	mvpp2_mac_config(&port->phylink_config, MLO_AN_INBAND, &state);
668162306a36Sopenharmony_ci	pcs->ops->pcs_config(pcs, PHYLINK_PCS_NEG_INBAND_ENABLED,
668262306a36Sopenharmony_ci			     port->phy_interface, state.advertising,
668362306a36Sopenharmony_ci			     false);
668462306a36Sopenharmony_ci	mvpp2_mac_finish(&port->phylink_config, MLO_AN_INBAND,
668562306a36Sopenharmony_ci			 port->phy_interface);
668662306a36Sopenharmony_ci	mvpp2_mac_link_up(&port->phylink_config, NULL,
668762306a36Sopenharmony_ci			  MLO_AN_INBAND, port->phy_interface,
668862306a36Sopenharmony_ci			  SPEED_UNKNOWN, DUPLEX_UNKNOWN, false, false);
668962306a36Sopenharmony_ci}
669062306a36Sopenharmony_ci
669162306a36Sopenharmony_ci/* In order to ensure backward compatibility for ACPI, check if the port
669262306a36Sopenharmony_ci * firmware node comprises the necessary description allowing to use phylink.
669362306a36Sopenharmony_ci */
669462306a36Sopenharmony_cistatic bool mvpp2_use_acpi_compat_mode(struct fwnode_handle *port_fwnode)
669562306a36Sopenharmony_ci{
669662306a36Sopenharmony_ci	if (!is_acpi_node(port_fwnode))
669762306a36Sopenharmony_ci		return false;
669862306a36Sopenharmony_ci
669962306a36Sopenharmony_ci	return (!fwnode_property_present(port_fwnode, "phy-handle") &&
670062306a36Sopenharmony_ci		!fwnode_property_present(port_fwnode, "managed") &&
670162306a36Sopenharmony_ci		!fwnode_get_named_child_node(port_fwnode, "fixed-link"));
670262306a36Sopenharmony_ci}
670362306a36Sopenharmony_ci
670462306a36Sopenharmony_ci/* Ports initialization */
670562306a36Sopenharmony_cistatic int mvpp2_port_probe(struct platform_device *pdev,
670662306a36Sopenharmony_ci			    struct fwnode_handle *port_fwnode,
670762306a36Sopenharmony_ci			    struct mvpp2 *priv)
670862306a36Sopenharmony_ci{
670962306a36Sopenharmony_ci	struct phy *comphy = NULL;
671062306a36Sopenharmony_ci	struct mvpp2_port *port;
671162306a36Sopenharmony_ci	struct mvpp2_port_pcpu *port_pcpu;
671262306a36Sopenharmony_ci	struct device_node *port_node = to_of_node(port_fwnode);
671362306a36Sopenharmony_ci	netdev_features_t features;
671462306a36Sopenharmony_ci	struct net_device *dev;
671562306a36Sopenharmony_ci	struct phylink *phylink;
671662306a36Sopenharmony_ci	char *mac_from = "";
671762306a36Sopenharmony_ci	unsigned int ntxqs, nrxqs, thread;
671862306a36Sopenharmony_ci	unsigned long flags = 0;
671962306a36Sopenharmony_ci	bool has_tx_irqs;
672062306a36Sopenharmony_ci	u32 id;
672162306a36Sopenharmony_ci	int phy_mode;
672262306a36Sopenharmony_ci	int err, i;
672362306a36Sopenharmony_ci
672462306a36Sopenharmony_ci	has_tx_irqs = mvpp2_port_has_irqs(priv, port_node, &flags);
672562306a36Sopenharmony_ci	if (!has_tx_irqs && queue_mode == MVPP2_QDIST_MULTI_MODE) {
672662306a36Sopenharmony_ci		dev_err(&pdev->dev,
672762306a36Sopenharmony_ci			"not enough IRQs to support multi queue mode\n");
672862306a36Sopenharmony_ci		return -EINVAL;
672962306a36Sopenharmony_ci	}
673062306a36Sopenharmony_ci
673162306a36Sopenharmony_ci	ntxqs = MVPP2_MAX_TXQ;
673262306a36Sopenharmony_ci	nrxqs = mvpp2_get_nrxqs(priv);
673362306a36Sopenharmony_ci
673462306a36Sopenharmony_ci	dev = alloc_etherdev_mqs(sizeof(*port), ntxqs, nrxqs);
673562306a36Sopenharmony_ci	if (!dev)
673662306a36Sopenharmony_ci		return -ENOMEM;
673762306a36Sopenharmony_ci
673862306a36Sopenharmony_ci	phy_mode = fwnode_get_phy_mode(port_fwnode);
673962306a36Sopenharmony_ci	if (phy_mode < 0) {
674062306a36Sopenharmony_ci		dev_err(&pdev->dev, "incorrect phy mode\n");
674162306a36Sopenharmony_ci		err = phy_mode;
674262306a36Sopenharmony_ci		goto err_free_netdev;
674362306a36Sopenharmony_ci	}
674462306a36Sopenharmony_ci
674562306a36Sopenharmony_ci	/*
674662306a36Sopenharmony_ci	 * Rewrite 10GBASE-KR to 10GBASE-R for compatibility with existing DT.
674762306a36Sopenharmony_ci	 * Existing usage of 10GBASE-KR is not correct; no backplane
674862306a36Sopenharmony_ci	 * negotiation is done, and this driver does not actually support
674962306a36Sopenharmony_ci	 * 10GBASE-KR.
675062306a36Sopenharmony_ci	 */
675162306a36Sopenharmony_ci	if (phy_mode == PHY_INTERFACE_MODE_10GKR)
675262306a36Sopenharmony_ci		phy_mode = PHY_INTERFACE_MODE_10GBASER;
675362306a36Sopenharmony_ci
675462306a36Sopenharmony_ci	if (port_node) {
675562306a36Sopenharmony_ci		comphy = devm_of_phy_get(&pdev->dev, port_node, NULL);
675662306a36Sopenharmony_ci		if (IS_ERR(comphy)) {
675762306a36Sopenharmony_ci			if (PTR_ERR(comphy) == -EPROBE_DEFER) {
675862306a36Sopenharmony_ci				err = -EPROBE_DEFER;
675962306a36Sopenharmony_ci				goto err_free_netdev;
676062306a36Sopenharmony_ci			}
676162306a36Sopenharmony_ci			comphy = NULL;
676262306a36Sopenharmony_ci		}
676362306a36Sopenharmony_ci	}
676462306a36Sopenharmony_ci
676562306a36Sopenharmony_ci	if (fwnode_property_read_u32(port_fwnode, "port-id", &id)) {
676662306a36Sopenharmony_ci		err = -EINVAL;
676762306a36Sopenharmony_ci		dev_err(&pdev->dev, "missing port-id value\n");
676862306a36Sopenharmony_ci		goto err_free_netdev;
676962306a36Sopenharmony_ci	}
677062306a36Sopenharmony_ci
677162306a36Sopenharmony_ci	dev->tx_queue_len = MVPP2_MAX_TXD_MAX;
677262306a36Sopenharmony_ci	dev->watchdog_timeo = 5 * HZ;
677362306a36Sopenharmony_ci	dev->netdev_ops = &mvpp2_netdev_ops;
677462306a36Sopenharmony_ci	dev->ethtool_ops = &mvpp2_eth_tool_ops;
677562306a36Sopenharmony_ci
677662306a36Sopenharmony_ci	port = netdev_priv(dev);
677762306a36Sopenharmony_ci	port->dev = dev;
677862306a36Sopenharmony_ci	port->fwnode = port_fwnode;
677962306a36Sopenharmony_ci	port->ntxqs = ntxqs;
678062306a36Sopenharmony_ci	port->nrxqs = nrxqs;
678162306a36Sopenharmony_ci	port->priv = priv;
678262306a36Sopenharmony_ci	port->has_tx_irqs = has_tx_irqs;
678362306a36Sopenharmony_ci	port->flags = flags;
678462306a36Sopenharmony_ci
678562306a36Sopenharmony_ci	err = mvpp2_queue_vectors_init(port, port_node);
678662306a36Sopenharmony_ci	if (err)
678762306a36Sopenharmony_ci		goto err_free_netdev;
678862306a36Sopenharmony_ci
678962306a36Sopenharmony_ci	if (port_node)
679062306a36Sopenharmony_ci		port->port_irq = of_irq_get_byname(port_node, "link");
679162306a36Sopenharmony_ci	else
679262306a36Sopenharmony_ci		port->port_irq = fwnode_irq_get(port_fwnode, port->nqvecs + 1);
679362306a36Sopenharmony_ci	if (port->port_irq == -EPROBE_DEFER) {
679462306a36Sopenharmony_ci		err = -EPROBE_DEFER;
679562306a36Sopenharmony_ci		goto err_deinit_qvecs;
679662306a36Sopenharmony_ci	}
679762306a36Sopenharmony_ci	if (port->port_irq <= 0)
679862306a36Sopenharmony_ci		/* the link irq is optional */
679962306a36Sopenharmony_ci		port->port_irq = 0;
680062306a36Sopenharmony_ci
680162306a36Sopenharmony_ci	if (fwnode_property_read_bool(port_fwnode, "marvell,loopback"))
680262306a36Sopenharmony_ci		port->flags |= MVPP2_F_LOOPBACK;
680362306a36Sopenharmony_ci
680462306a36Sopenharmony_ci	port->id = id;
680562306a36Sopenharmony_ci	if (priv->hw_version == MVPP21)
680662306a36Sopenharmony_ci		port->first_rxq = port->id * port->nrxqs;
680762306a36Sopenharmony_ci	else
680862306a36Sopenharmony_ci		port->first_rxq = port->id * priv->max_port_rxqs;
680962306a36Sopenharmony_ci
681062306a36Sopenharmony_ci	port->of_node = port_node;
681162306a36Sopenharmony_ci	port->phy_interface = phy_mode;
681262306a36Sopenharmony_ci	port->comphy = comphy;
681362306a36Sopenharmony_ci
681462306a36Sopenharmony_ci	if (priv->hw_version == MVPP21) {
681562306a36Sopenharmony_ci		port->base = devm_platform_ioremap_resource(pdev, 2 + id);
681662306a36Sopenharmony_ci		if (IS_ERR(port->base)) {
681762306a36Sopenharmony_ci			err = PTR_ERR(port->base);
681862306a36Sopenharmony_ci			goto err_free_irq;
681962306a36Sopenharmony_ci		}
682062306a36Sopenharmony_ci
682162306a36Sopenharmony_ci		port->stats_base = port->priv->lms_base +
682262306a36Sopenharmony_ci				   MVPP21_MIB_COUNTERS_OFFSET +
682362306a36Sopenharmony_ci				   port->gop_id * MVPP21_MIB_COUNTERS_PORT_SZ;
682462306a36Sopenharmony_ci	} else {
682562306a36Sopenharmony_ci		if (fwnode_property_read_u32(port_fwnode, "gop-port-id",
682662306a36Sopenharmony_ci					     &port->gop_id)) {
682762306a36Sopenharmony_ci			err = -EINVAL;
682862306a36Sopenharmony_ci			dev_err(&pdev->dev, "missing gop-port-id value\n");
682962306a36Sopenharmony_ci			goto err_deinit_qvecs;
683062306a36Sopenharmony_ci		}
683162306a36Sopenharmony_ci
683262306a36Sopenharmony_ci		port->base = priv->iface_base + MVPP22_GMAC_BASE(port->gop_id);
683362306a36Sopenharmony_ci		port->stats_base = port->priv->iface_base +
683462306a36Sopenharmony_ci				   MVPP22_MIB_COUNTERS_OFFSET +
683562306a36Sopenharmony_ci				   port->gop_id * MVPP22_MIB_COUNTERS_PORT_SZ;
683662306a36Sopenharmony_ci
683762306a36Sopenharmony_ci		/* We may want a property to describe whether we should use
683862306a36Sopenharmony_ci		 * MAC hardware timestamping.
683962306a36Sopenharmony_ci		 */
684062306a36Sopenharmony_ci		if (priv->tai)
684162306a36Sopenharmony_ci			port->hwtstamp = true;
684262306a36Sopenharmony_ci	}
684362306a36Sopenharmony_ci
684462306a36Sopenharmony_ci	/* Alloc per-cpu and ethtool stats */
684562306a36Sopenharmony_ci	port->stats = netdev_alloc_pcpu_stats(struct mvpp2_pcpu_stats);
684662306a36Sopenharmony_ci	if (!port->stats) {
684762306a36Sopenharmony_ci		err = -ENOMEM;
684862306a36Sopenharmony_ci		goto err_free_irq;
684962306a36Sopenharmony_ci	}
685062306a36Sopenharmony_ci
685162306a36Sopenharmony_ci	port->ethtool_stats = devm_kcalloc(&pdev->dev,
685262306a36Sopenharmony_ci					   MVPP2_N_ETHTOOL_STATS(ntxqs, nrxqs),
685362306a36Sopenharmony_ci					   sizeof(u64), GFP_KERNEL);
685462306a36Sopenharmony_ci	if (!port->ethtool_stats) {
685562306a36Sopenharmony_ci		err = -ENOMEM;
685662306a36Sopenharmony_ci		goto err_free_stats;
685762306a36Sopenharmony_ci	}
685862306a36Sopenharmony_ci
685962306a36Sopenharmony_ci	mutex_init(&port->gather_stats_lock);
686062306a36Sopenharmony_ci	INIT_DELAYED_WORK(&port->stats_work, mvpp2_gather_hw_statistics);
686162306a36Sopenharmony_ci
686262306a36Sopenharmony_ci	err = mvpp2_port_copy_mac_addr(dev, priv, port_fwnode, &mac_from);
686362306a36Sopenharmony_ci	if (err < 0)
686462306a36Sopenharmony_ci		goto err_free_stats;
686562306a36Sopenharmony_ci
686662306a36Sopenharmony_ci	port->tx_ring_size = MVPP2_MAX_TXD_DFLT;
686762306a36Sopenharmony_ci	port->rx_ring_size = MVPP2_MAX_RXD_DFLT;
686862306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
686962306a36Sopenharmony_ci
687062306a36Sopenharmony_ci	err = mvpp2_port_init(port);
687162306a36Sopenharmony_ci	if (err < 0) {
687262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to init port %d\n", id);
687362306a36Sopenharmony_ci		goto err_free_stats;
687462306a36Sopenharmony_ci	}
687562306a36Sopenharmony_ci
687662306a36Sopenharmony_ci	mvpp2_port_periodic_xon_disable(port);
687762306a36Sopenharmony_ci
687862306a36Sopenharmony_ci	mvpp2_mac_reset_assert(port);
687962306a36Sopenharmony_ci	mvpp22_pcs_reset_assert(port);
688062306a36Sopenharmony_ci
688162306a36Sopenharmony_ci	port->pcpu = alloc_percpu(struct mvpp2_port_pcpu);
688262306a36Sopenharmony_ci	if (!port->pcpu) {
688362306a36Sopenharmony_ci		err = -ENOMEM;
688462306a36Sopenharmony_ci		goto err_free_txq_pcpu;
688562306a36Sopenharmony_ci	}
688662306a36Sopenharmony_ci
688762306a36Sopenharmony_ci	if (!port->has_tx_irqs) {
688862306a36Sopenharmony_ci		for (thread = 0; thread < priv->nthreads; thread++) {
688962306a36Sopenharmony_ci			port_pcpu = per_cpu_ptr(port->pcpu, thread);
689062306a36Sopenharmony_ci
689162306a36Sopenharmony_ci			hrtimer_init(&port_pcpu->tx_done_timer, CLOCK_MONOTONIC,
689262306a36Sopenharmony_ci				     HRTIMER_MODE_REL_PINNED_SOFT);
689362306a36Sopenharmony_ci			port_pcpu->tx_done_timer.function = mvpp2_hr_timer_cb;
689462306a36Sopenharmony_ci			port_pcpu->timer_scheduled = false;
689562306a36Sopenharmony_ci			port_pcpu->dev = dev;
689662306a36Sopenharmony_ci		}
689762306a36Sopenharmony_ci	}
689862306a36Sopenharmony_ci
689962306a36Sopenharmony_ci	features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
690062306a36Sopenharmony_ci		   NETIF_F_TSO;
690162306a36Sopenharmony_ci	dev->features = features | NETIF_F_RXCSUM;
690262306a36Sopenharmony_ci	dev->hw_features |= features | NETIF_F_RXCSUM | NETIF_F_GRO |
690362306a36Sopenharmony_ci			    NETIF_F_HW_VLAN_CTAG_FILTER;
690462306a36Sopenharmony_ci
690562306a36Sopenharmony_ci	if (mvpp22_rss_is_supported(port)) {
690662306a36Sopenharmony_ci		dev->hw_features |= NETIF_F_RXHASH;
690762306a36Sopenharmony_ci		dev->features |= NETIF_F_NTUPLE;
690862306a36Sopenharmony_ci	}
690962306a36Sopenharmony_ci
691062306a36Sopenharmony_ci	if (!port->priv->percpu_pools)
691162306a36Sopenharmony_ci		mvpp2_set_hw_csum(port, port->pool_long->id);
691262306a36Sopenharmony_ci	else if (port->ntxqs >= num_possible_cpus() * 2)
691362306a36Sopenharmony_ci		dev->xdp_features = NETDEV_XDP_ACT_BASIC |
691462306a36Sopenharmony_ci				    NETDEV_XDP_ACT_REDIRECT |
691562306a36Sopenharmony_ci				    NETDEV_XDP_ACT_NDO_XMIT;
691662306a36Sopenharmony_ci
691762306a36Sopenharmony_ci	dev->vlan_features |= features;
691862306a36Sopenharmony_ci	netif_set_tso_max_segs(dev, MVPP2_MAX_TSO_SEGS);
691962306a36Sopenharmony_ci
692062306a36Sopenharmony_ci	dev->priv_flags |= IFF_UNICAST_FLT;
692162306a36Sopenharmony_ci
692262306a36Sopenharmony_ci	/* MTU range: 68 - 9704 */
692362306a36Sopenharmony_ci	dev->min_mtu = ETH_MIN_MTU;
692462306a36Sopenharmony_ci	/* 9704 == 9728 - 20 and rounding to 8 */
692562306a36Sopenharmony_ci	dev->max_mtu = MVPP2_BM_JUMBO_PKT_SIZE;
692662306a36Sopenharmony_ci	dev->dev.of_node = port_node;
692762306a36Sopenharmony_ci
692862306a36Sopenharmony_ci	port->pcs_gmac.ops = &mvpp2_phylink_gmac_pcs_ops;
692962306a36Sopenharmony_ci	port->pcs_gmac.neg_mode = true;
693062306a36Sopenharmony_ci	port->pcs_xlg.ops = &mvpp2_phylink_xlg_pcs_ops;
693162306a36Sopenharmony_ci	port->pcs_xlg.neg_mode = true;
693262306a36Sopenharmony_ci
693362306a36Sopenharmony_ci	if (!mvpp2_use_acpi_compat_mode(port_fwnode)) {
693462306a36Sopenharmony_ci		port->phylink_config.dev = &dev->dev;
693562306a36Sopenharmony_ci		port->phylink_config.type = PHYLINK_NETDEV;
693662306a36Sopenharmony_ci		port->phylink_config.mac_capabilities =
693762306a36Sopenharmony_ci			MAC_2500FD | MAC_1000FD | MAC_100 | MAC_10;
693862306a36Sopenharmony_ci
693962306a36Sopenharmony_ci		if (port->priv->global_tx_fc)
694062306a36Sopenharmony_ci			port->phylink_config.mac_capabilities |=
694162306a36Sopenharmony_ci				MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
694262306a36Sopenharmony_ci
694362306a36Sopenharmony_ci		if (mvpp2_port_supports_xlg(port)) {
694462306a36Sopenharmony_ci			/* If a COMPHY is present, we can support any of
694562306a36Sopenharmony_ci			 * the serdes modes and switch between them.
694662306a36Sopenharmony_ci			 */
694762306a36Sopenharmony_ci			if (comphy) {
694862306a36Sopenharmony_ci				__set_bit(PHY_INTERFACE_MODE_5GBASER,
694962306a36Sopenharmony_ci					  port->phylink_config.supported_interfaces);
695062306a36Sopenharmony_ci				__set_bit(PHY_INTERFACE_MODE_10GBASER,
695162306a36Sopenharmony_ci					  port->phylink_config.supported_interfaces);
695262306a36Sopenharmony_ci				__set_bit(PHY_INTERFACE_MODE_XAUI,
695362306a36Sopenharmony_ci					  port->phylink_config.supported_interfaces);
695462306a36Sopenharmony_ci			} else if (phy_mode == PHY_INTERFACE_MODE_5GBASER) {
695562306a36Sopenharmony_ci				__set_bit(PHY_INTERFACE_MODE_5GBASER,
695662306a36Sopenharmony_ci					  port->phylink_config.supported_interfaces);
695762306a36Sopenharmony_ci			} else if (phy_mode == PHY_INTERFACE_MODE_10GBASER) {
695862306a36Sopenharmony_ci				__set_bit(PHY_INTERFACE_MODE_10GBASER,
695962306a36Sopenharmony_ci					  port->phylink_config.supported_interfaces);
696062306a36Sopenharmony_ci			} else if (phy_mode == PHY_INTERFACE_MODE_XAUI) {
696162306a36Sopenharmony_ci				__set_bit(PHY_INTERFACE_MODE_XAUI,
696262306a36Sopenharmony_ci					  port->phylink_config.supported_interfaces);
696362306a36Sopenharmony_ci			}
696462306a36Sopenharmony_ci
696562306a36Sopenharmony_ci			if (comphy)
696662306a36Sopenharmony_ci				port->phylink_config.mac_capabilities |=
696762306a36Sopenharmony_ci					MAC_10000FD | MAC_5000FD;
696862306a36Sopenharmony_ci			else if (phy_mode == PHY_INTERFACE_MODE_5GBASER)
696962306a36Sopenharmony_ci				port->phylink_config.mac_capabilities |=
697062306a36Sopenharmony_ci					MAC_5000FD;
697162306a36Sopenharmony_ci			else
697262306a36Sopenharmony_ci				port->phylink_config.mac_capabilities |=
697362306a36Sopenharmony_ci					MAC_10000FD;
697462306a36Sopenharmony_ci		}
697562306a36Sopenharmony_ci
697662306a36Sopenharmony_ci		if (mvpp2_port_supports_rgmii(port))
697762306a36Sopenharmony_ci			phy_interface_set_rgmii(port->phylink_config.supported_interfaces);
697862306a36Sopenharmony_ci
697962306a36Sopenharmony_ci		if (comphy) {
698062306a36Sopenharmony_ci			/* If a COMPHY is present, we can support any of the
698162306a36Sopenharmony_ci			 * serdes modes and switch between them.
698262306a36Sopenharmony_ci			 */
698362306a36Sopenharmony_ci			__set_bit(PHY_INTERFACE_MODE_SGMII,
698462306a36Sopenharmony_ci				  port->phylink_config.supported_interfaces);
698562306a36Sopenharmony_ci			__set_bit(PHY_INTERFACE_MODE_1000BASEX,
698662306a36Sopenharmony_ci				  port->phylink_config.supported_interfaces);
698762306a36Sopenharmony_ci			__set_bit(PHY_INTERFACE_MODE_2500BASEX,
698862306a36Sopenharmony_ci				  port->phylink_config.supported_interfaces);
698962306a36Sopenharmony_ci		} else if (phy_mode == PHY_INTERFACE_MODE_2500BASEX) {
699062306a36Sopenharmony_ci			/* No COMPHY, with only 2500BASE-X mode supported */
699162306a36Sopenharmony_ci			__set_bit(PHY_INTERFACE_MODE_2500BASEX,
699262306a36Sopenharmony_ci				  port->phylink_config.supported_interfaces);
699362306a36Sopenharmony_ci		} else if (phy_mode == PHY_INTERFACE_MODE_1000BASEX ||
699462306a36Sopenharmony_ci			   phy_mode == PHY_INTERFACE_MODE_SGMII) {
699562306a36Sopenharmony_ci			/* No COMPHY, we can switch between 1000BASE-X and SGMII
699662306a36Sopenharmony_ci			 */
699762306a36Sopenharmony_ci			__set_bit(PHY_INTERFACE_MODE_1000BASEX,
699862306a36Sopenharmony_ci				  port->phylink_config.supported_interfaces);
699962306a36Sopenharmony_ci			__set_bit(PHY_INTERFACE_MODE_SGMII,
700062306a36Sopenharmony_ci				  port->phylink_config.supported_interfaces);
700162306a36Sopenharmony_ci		}
700262306a36Sopenharmony_ci
700362306a36Sopenharmony_ci		phylink = phylink_create(&port->phylink_config, port_fwnode,
700462306a36Sopenharmony_ci					 phy_mode, &mvpp2_phylink_ops);
700562306a36Sopenharmony_ci		if (IS_ERR(phylink)) {
700662306a36Sopenharmony_ci			err = PTR_ERR(phylink);
700762306a36Sopenharmony_ci			goto err_free_port_pcpu;
700862306a36Sopenharmony_ci		}
700962306a36Sopenharmony_ci		port->phylink = phylink;
701062306a36Sopenharmony_ci	} else {
701162306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Use link irqs for port#%d. FW update required\n", port->id);
701262306a36Sopenharmony_ci		port->phylink = NULL;
701362306a36Sopenharmony_ci	}
701462306a36Sopenharmony_ci
701562306a36Sopenharmony_ci	/* Cycle the comphy to power it down, saving 270mW per port -
701662306a36Sopenharmony_ci	 * don't worry about an error powering it up. When the comphy
701762306a36Sopenharmony_ci	 * driver does this, we can remove this code.
701862306a36Sopenharmony_ci	 */
701962306a36Sopenharmony_ci	if (port->comphy) {
702062306a36Sopenharmony_ci		err = mvpp22_comphy_init(port, port->phy_interface);
702162306a36Sopenharmony_ci		if (err == 0)
702262306a36Sopenharmony_ci			phy_power_off(port->comphy);
702362306a36Sopenharmony_ci	}
702462306a36Sopenharmony_ci
702562306a36Sopenharmony_ci	err = register_netdev(dev);
702662306a36Sopenharmony_ci	if (err < 0) {
702762306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register netdev\n");
702862306a36Sopenharmony_ci		goto err_phylink;
702962306a36Sopenharmony_ci	}
703062306a36Sopenharmony_ci	netdev_info(dev, "Using %s mac address %pM\n", mac_from, dev->dev_addr);
703162306a36Sopenharmony_ci
703262306a36Sopenharmony_ci	priv->port_list[priv->port_count++] = port;
703362306a36Sopenharmony_ci
703462306a36Sopenharmony_ci	return 0;
703562306a36Sopenharmony_ci
703662306a36Sopenharmony_cierr_phylink:
703762306a36Sopenharmony_ci	if (port->phylink)
703862306a36Sopenharmony_ci		phylink_destroy(port->phylink);
703962306a36Sopenharmony_cierr_free_port_pcpu:
704062306a36Sopenharmony_ci	free_percpu(port->pcpu);
704162306a36Sopenharmony_cierr_free_txq_pcpu:
704262306a36Sopenharmony_ci	for (i = 0; i < port->ntxqs; i++)
704362306a36Sopenharmony_ci		free_percpu(port->txqs[i]->pcpu);
704462306a36Sopenharmony_cierr_free_stats:
704562306a36Sopenharmony_ci	free_percpu(port->stats);
704662306a36Sopenharmony_cierr_free_irq:
704762306a36Sopenharmony_ci	if (port->port_irq)
704862306a36Sopenharmony_ci		irq_dispose_mapping(port->port_irq);
704962306a36Sopenharmony_cierr_deinit_qvecs:
705062306a36Sopenharmony_ci	mvpp2_queue_vectors_deinit(port);
705162306a36Sopenharmony_cierr_free_netdev:
705262306a36Sopenharmony_ci	free_netdev(dev);
705362306a36Sopenharmony_ci	return err;
705462306a36Sopenharmony_ci}
705562306a36Sopenharmony_ci
705662306a36Sopenharmony_ci/* Ports removal routine */
705762306a36Sopenharmony_cistatic void mvpp2_port_remove(struct mvpp2_port *port)
705862306a36Sopenharmony_ci{
705962306a36Sopenharmony_ci	int i;
706062306a36Sopenharmony_ci
706162306a36Sopenharmony_ci	unregister_netdev(port->dev);
706262306a36Sopenharmony_ci	if (port->phylink)
706362306a36Sopenharmony_ci		phylink_destroy(port->phylink);
706462306a36Sopenharmony_ci	free_percpu(port->pcpu);
706562306a36Sopenharmony_ci	free_percpu(port->stats);
706662306a36Sopenharmony_ci	for (i = 0; i < port->ntxqs; i++)
706762306a36Sopenharmony_ci		free_percpu(port->txqs[i]->pcpu);
706862306a36Sopenharmony_ci	mvpp2_queue_vectors_deinit(port);
706962306a36Sopenharmony_ci	if (port->port_irq)
707062306a36Sopenharmony_ci		irq_dispose_mapping(port->port_irq);
707162306a36Sopenharmony_ci	free_netdev(port->dev);
707262306a36Sopenharmony_ci}
707362306a36Sopenharmony_ci
707462306a36Sopenharmony_ci/* Initialize decoding windows */
707562306a36Sopenharmony_cistatic void mvpp2_conf_mbus_windows(const struct mbus_dram_target_info *dram,
707662306a36Sopenharmony_ci				    struct mvpp2 *priv)
707762306a36Sopenharmony_ci{
707862306a36Sopenharmony_ci	u32 win_enable;
707962306a36Sopenharmony_ci	int i;
708062306a36Sopenharmony_ci
708162306a36Sopenharmony_ci	for (i = 0; i < 6; i++) {
708262306a36Sopenharmony_ci		mvpp2_write(priv, MVPP2_WIN_BASE(i), 0);
708362306a36Sopenharmony_ci		mvpp2_write(priv, MVPP2_WIN_SIZE(i), 0);
708462306a36Sopenharmony_ci
708562306a36Sopenharmony_ci		if (i < 4)
708662306a36Sopenharmony_ci			mvpp2_write(priv, MVPP2_WIN_REMAP(i), 0);
708762306a36Sopenharmony_ci	}
708862306a36Sopenharmony_ci
708962306a36Sopenharmony_ci	win_enable = 0;
709062306a36Sopenharmony_ci
709162306a36Sopenharmony_ci	for (i = 0; i < dram->num_cs; i++) {
709262306a36Sopenharmony_ci		const struct mbus_dram_window *cs = dram->cs + i;
709362306a36Sopenharmony_ci
709462306a36Sopenharmony_ci		mvpp2_write(priv, MVPP2_WIN_BASE(i),
709562306a36Sopenharmony_ci			    (cs->base & 0xffff0000) | (cs->mbus_attr << 8) |
709662306a36Sopenharmony_ci			    dram->mbus_dram_target_id);
709762306a36Sopenharmony_ci
709862306a36Sopenharmony_ci		mvpp2_write(priv, MVPP2_WIN_SIZE(i),
709962306a36Sopenharmony_ci			    (cs->size - 1) & 0xffff0000);
710062306a36Sopenharmony_ci
710162306a36Sopenharmony_ci		win_enable |= (1 << i);
710262306a36Sopenharmony_ci	}
710362306a36Sopenharmony_ci
710462306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_BASE_ADDR_ENABLE, win_enable);
710562306a36Sopenharmony_ci}
710662306a36Sopenharmony_ci
710762306a36Sopenharmony_ci/* Initialize Rx FIFO's */
710862306a36Sopenharmony_cistatic void mvpp2_rx_fifo_init(struct mvpp2 *priv)
710962306a36Sopenharmony_ci{
711062306a36Sopenharmony_ci	int port;
711162306a36Sopenharmony_ci
711262306a36Sopenharmony_ci	for (port = 0; port < MVPP2_MAX_PORTS; port++) {
711362306a36Sopenharmony_ci		mvpp2_write(priv, MVPP2_RX_DATA_FIFO_SIZE_REG(port),
711462306a36Sopenharmony_ci			    MVPP2_RX_FIFO_PORT_DATA_SIZE_4KB);
711562306a36Sopenharmony_ci		mvpp2_write(priv, MVPP2_RX_ATTR_FIFO_SIZE_REG(port),
711662306a36Sopenharmony_ci			    MVPP2_RX_FIFO_PORT_ATTR_SIZE_4KB);
711762306a36Sopenharmony_ci	}
711862306a36Sopenharmony_ci
711962306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_RX_MIN_PKT_SIZE_REG,
712062306a36Sopenharmony_ci		    MVPP2_RX_FIFO_PORT_MIN_PKT);
712162306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_RX_FIFO_INIT_REG, 0x1);
712262306a36Sopenharmony_ci}
712362306a36Sopenharmony_ci
712462306a36Sopenharmony_cistatic void mvpp22_rx_fifo_set_hw(struct mvpp2 *priv, int port, int data_size)
712562306a36Sopenharmony_ci{
712662306a36Sopenharmony_ci	int attr_size = MVPP2_RX_FIFO_PORT_ATTR_SIZE(data_size);
712762306a36Sopenharmony_ci
712862306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_RX_DATA_FIFO_SIZE_REG(port), data_size);
712962306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_RX_ATTR_FIFO_SIZE_REG(port), attr_size);
713062306a36Sopenharmony_ci}
713162306a36Sopenharmony_ci
713262306a36Sopenharmony_ci/* Initialize TX FIFO's: the total FIFO size is 48kB on PPv2.2 and PPv2.3.
713362306a36Sopenharmony_ci * 4kB fixed space must be assigned for the loopback port.
713462306a36Sopenharmony_ci * Redistribute remaining avialable 44kB space among all active ports.
713562306a36Sopenharmony_ci * Guarantee minimum 32kB for 10G port and 8kB for port 1, capable of 2.5G
713662306a36Sopenharmony_ci * SGMII link.
713762306a36Sopenharmony_ci */
713862306a36Sopenharmony_cistatic void mvpp22_rx_fifo_init(struct mvpp2 *priv)
713962306a36Sopenharmony_ci{
714062306a36Sopenharmony_ci	int remaining_ports_count;
714162306a36Sopenharmony_ci	unsigned long port_map;
714262306a36Sopenharmony_ci	int size_remainder;
714362306a36Sopenharmony_ci	int port, size;
714462306a36Sopenharmony_ci
714562306a36Sopenharmony_ci	/* The loopback requires fixed 4kB of the FIFO space assignment. */
714662306a36Sopenharmony_ci	mvpp22_rx_fifo_set_hw(priv, MVPP2_LOOPBACK_PORT_INDEX,
714762306a36Sopenharmony_ci			      MVPP2_RX_FIFO_PORT_DATA_SIZE_4KB);
714862306a36Sopenharmony_ci	port_map = priv->port_map & ~BIT(MVPP2_LOOPBACK_PORT_INDEX);
714962306a36Sopenharmony_ci
715062306a36Sopenharmony_ci	/* Set RX FIFO size to 0 for inactive ports. */
715162306a36Sopenharmony_ci	for_each_clear_bit(port, &port_map, MVPP2_LOOPBACK_PORT_INDEX)
715262306a36Sopenharmony_ci		mvpp22_rx_fifo_set_hw(priv, port, 0);
715362306a36Sopenharmony_ci
715462306a36Sopenharmony_ci	/* Assign remaining RX FIFO space among all active ports. */
715562306a36Sopenharmony_ci	size_remainder = MVPP2_RX_FIFO_PORT_DATA_SIZE_44KB;
715662306a36Sopenharmony_ci	remaining_ports_count = hweight_long(port_map);
715762306a36Sopenharmony_ci
715862306a36Sopenharmony_ci	for_each_set_bit(port, &port_map, MVPP2_LOOPBACK_PORT_INDEX) {
715962306a36Sopenharmony_ci		if (remaining_ports_count == 1)
716062306a36Sopenharmony_ci			size = size_remainder;
716162306a36Sopenharmony_ci		else if (port == 0)
716262306a36Sopenharmony_ci			size = max(size_remainder / remaining_ports_count,
716362306a36Sopenharmony_ci				   MVPP2_RX_FIFO_PORT_DATA_SIZE_32KB);
716462306a36Sopenharmony_ci		else if (port == 1)
716562306a36Sopenharmony_ci			size = max(size_remainder / remaining_ports_count,
716662306a36Sopenharmony_ci				   MVPP2_RX_FIFO_PORT_DATA_SIZE_8KB);
716762306a36Sopenharmony_ci		else
716862306a36Sopenharmony_ci			size = size_remainder / remaining_ports_count;
716962306a36Sopenharmony_ci
717062306a36Sopenharmony_ci		size_remainder -= size;
717162306a36Sopenharmony_ci		remaining_ports_count--;
717262306a36Sopenharmony_ci
717362306a36Sopenharmony_ci		mvpp22_rx_fifo_set_hw(priv, port, size);
717462306a36Sopenharmony_ci	}
717562306a36Sopenharmony_ci
717662306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_RX_MIN_PKT_SIZE_REG,
717762306a36Sopenharmony_ci		    MVPP2_RX_FIFO_PORT_MIN_PKT);
717862306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_RX_FIFO_INIT_REG, 0x1);
717962306a36Sopenharmony_ci}
718062306a36Sopenharmony_ci
718162306a36Sopenharmony_ci/* Configure Rx FIFO Flow control thresholds */
718262306a36Sopenharmony_cistatic void mvpp23_rx_fifo_fc_set_tresh(struct mvpp2 *priv)
718362306a36Sopenharmony_ci{
718462306a36Sopenharmony_ci	int port, val;
718562306a36Sopenharmony_ci
718662306a36Sopenharmony_ci	/* Port 0: maximum speed -10Gb/s port
718762306a36Sopenharmony_ci	 *	   required by spec RX FIFO threshold 9KB
718862306a36Sopenharmony_ci	 * Port 1: maximum speed -5Gb/s port
718962306a36Sopenharmony_ci	 *	   required by spec RX FIFO threshold 4KB
719062306a36Sopenharmony_ci	 * Port 2: maximum speed -1Gb/s port
719162306a36Sopenharmony_ci	 *	   required by spec RX FIFO threshold 2KB
719262306a36Sopenharmony_ci	 */
719362306a36Sopenharmony_ci
719462306a36Sopenharmony_ci	/* Without loopback port */
719562306a36Sopenharmony_ci	for (port = 0; port < (MVPP2_MAX_PORTS - 1); port++) {
719662306a36Sopenharmony_ci		if (port == 0) {
719762306a36Sopenharmony_ci			val = (MVPP23_PORT0_FIFO_TRSH / MVPP2_RX_FC_TRSH_UNIT)
719862306a36Sopenharmony_ci				<< MVPP2_RX_FC_TRSH_OFFS;
719962306a36Sopenharmony_ci			val &= MVPP2_RX_FC_TRSH_MASK;
720062306a36Sopenharmony_ci			mvpp2_write(priv, MVPP2_RX_FC_REG(port), val);
720162306a36Sopenharmony_ci		} else if (port == 1) {
720262306a36Sopenharmony_ci			val = (MVPP23_PORT1_FIFO_TRSH / MVPP2_RX_FC_TRSH_UNIT)
720362306a36Sopenharmony_ci				<< MVPP2_RX_FC_TRSH_OFFS;
720462306a36Sopenharmony_ci			val &= MVPP2_RX_FC_TRSH_MASK;
720562306a36Sopenharmony_ci			mvpp2_write(priv, MVPP2_RX_FC_REG(port), val);
720662306a36Sopenharmony_ci		} else {
720762306a36Sopenharmony_ci			val = (MVPP23_PORT2_FIFO_TRSH / MVPP2_RX_FC_TRSH_UNIT)
720862306a36Sopenharmony_ci				<< MVPP2_RX_FC_TRSH_OFFS;
720962306a36Sopenharmony_ci			val &= MVPP2_RX_FC_TRSH_MASK;
721062306a36Sopenharmony_ci			mvpp2_write(priv, MVPP2_RX_FC_REG(port), val);
721162306a36Sopenharmony_ci		}
721262306a36Sopenharmony_ci	}
721362306a36Sopenharmony_ci}
721462306a36Sopenharmony_ci
721562306a36Sopenharmony_ci/* Configure Rx FIFO Flow control thresholds */
721662306a36Sopenharmony_civoid mvpp23_rx_fifo_fc_en(struct mvpp2 *priv, int port, bool en)
721762306a36Sopenharmony_ci{
721862306a36Sopenharmony_ci	int val;
721962306a36Sopenharmony_ci
722062306a36Sopenharmony_ci	val = mvpp2_read(priv, MVPP2_RX_FC_REG(port));
722162306a36Sopenharmony_ci
722262306a36Sopenharmony_ci	if (en)
722362306a36Sopenharmony_ci		val |= MVPP2_RX_FC_EN;
722462306a36Sopenharmony_ci	else
722562306a36Sopenharmony_ci		val &= ~MVPP2_RX_FC_EN;
722662306a36Sopenharmony_ci
722762306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_RX_FC_REG(port), val);
722862306a36Sopenharmony_ci}
722962306a36Sopenharmony_ci
723062306a36Sopenharmony_cistatic void mvpp22_tx_fifo_set_hw(struct mvpp2 *priv, int port, int size)
723162306a36Sopenharmony_ci{
723262306a36Sopenharmony_ci	int threshold = MVPP2_TX_FIFO_THRESHOLD(size);
723362306a36Sopenharmony_ci
723462306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_TX_FIFO_SIZE_REG(port), size);
723562306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_TX_FIFO_THRESH_REG(port), threshold);
723662306a36Sopenharmony_ci}
723762306a36Sopenharmony_ci
723862306a36Sopenharmony_ci/* Initialize TX FIFO's: the total FIFO size is 19kB on PPv2.2 and PPv2.3.
723962306a36Sopenharmony_ci * 1kB fixed space must be assigned for the loopback port.
724062306a36Sopenharmony_ci * Redistribute remaining avialable 18kB space among all active ports.
724162306a36Sopenharmony_ci * The 10G interface should use 10kB (which is maximum possible size
724262306a36Sopenharmony_ci * per single port).
724362306a36Sopenharmony_ci */
724462306a36Sopenharmony_cistatic void mvpp22_tx_fifo_init(struct mvpp2 *priv)
724562306a36Sopenharmony_ci{
724662306a36Sopenharmony_ci	int remaining_ports_count;
724762306a36Sopenharmony_ci	unsigned long port_map;
724862306a36Sopenharmony_ci	int size_remainder;
724962306a36Sopenharmony_ci	int port, size;
725062306a36Sopenharmony_ci
725162306a36Sopenharmony_ci	/* The loopback requires fixed 1kB of the FIFO space assignment. */
725262306a36Sopenharmony_ci	mvpp22_tx_fifo_set_hw(priv, MVPP2_LOOPBACK_PORT_INDEX,
725362306a36Sopenharmony_ci			      MVPP22_TX_FIFO_DATA_SIZE_1KB);
725462306a36Sopenharmony_ci	port_map = priv->port_map & ~BIT(MVPP2_LOOPBACK_PORT_INDEX);
725562306a36Sopenharmony_ci
725662306a36Sopenharmony_ci	/* Set TX FIFO size to 0 for inactive ports. */
725762306a36Sopenharmony_ci	for_each_clear_bit(port, &port_map, MVPP2_LOOPBACK_PORT_INDEX)
725862306a36Sopenharmony_ci		mvpp22_tx_fifo_set_hw(priv, port, 0);
725962306a36Sopenharmony_ci
726062306a36Sopenharmony_ci	/* Assign remaining TX FIFO space among all active ports. */
726162306a36Sopenharmony_ci	size_remainder = MVPP22_TX_FIFO_DATA_SIZE_18KB;
726262306a36Sopenharmony_ci	remaining_ports_count = hweight_long(port_map);
726362306a36Sopenharmony_ci
726462306a36Sopenharmony_ci	for_each_set_bit(port, &port_map, MVPP2_LOOPBACK_PORT_INDEX) {
726562306a36Sopenharmony_ci		if (remaining_ports_count == 1)
726662306a36Sopenharmony_ci			size = min(size_remainder,
726762306a36Sopenharmony_ci				   MVPP22_TX_FIFO_DATA_SIZE_10KB);
726862306a36Sopenharmony_ci		else if (port == 0)
726962306a36Sopenharmony_ci			size = MVPP22_TX_FIFO_DATA_SIZE_10KB;
727062306a36Sopenharmony_ci		else
727162306a36Sopenharmony_ci			size = size_remainder / remaining_ports_count;
727262306a36Sopenharmony_ci
727362306a36Sopenharmony_ci		size_remainder -= size;
727462306a36Sopenharmony_ci		remaining_ports_count--;
727562306a36Sopenharmony_ci
727662306a36Sopenharmony_ci		mvpp22_tx_fifo_set_hw(priv, port, size);
727762306a36Sopenharmony_ci	}
727862306a36Sopenharmony_ci}
727962306a36Sopenharmony_ci
728062306a36Sopenharmony_cistatic void mvpp2_axi_init(struct mvpp2 *priv)
728162306a36Sopenharmony_ci{
728262306a36Sopenharmony_ci	u32 val, rdval, wrval;
728362306a36Sopenharmony_ci
728462306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_BM_ADDR_HIGH_RLS_REG, 0x0);
728562306a36Sopenharmony_ci
728662306a36Sopenharmony_ci	/* AXI Bridge Configuration */
728762306a36Sopenharmony_ci
728862306a36Sopenharmony_ci	rdval = MVPP22_AXI_CODE_CACHE_RD_CACHE
728962306a36Sopenharmony_ci		<< MVPP22_AXI_ATTR_CACHE_OFFS;
729062306a36Sopenharmony_ci	rdval |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM
729162306a36Sopenharmony_ci		<< MVPP22_AXI_ATTR_DOMAIN_OFFS;
729262306a36Sopenharmony_ci
729362306a36Sopenharmony_ci	wrval = MVPP22_AXI_CODE_CACHE_WR_CACHE
729462306a36Sopenharmony_ci		<< MVPP22_AXI_ATTR_CACHE_OFFS;
729562306a36Sopenharmony_ci	wrval |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM
729662306a36Sopenharmony_ci		<< MVPP22_AXI_ATTR_DOMAIN_OFFS;
729762306a36Sopenharmony_ci
729862306a36Sopenharmony_ci	/* BM */
729962306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_BM_WR_ATTR_REG, wrval);
730062306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_BM_RD_ATTR_REG, rdval);
730162306a36Sopenharmony_ci
730262306a36Sopenharmony_ci	/* Descriptors */
730362306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_AGGRQ_DESCR_RD_ATTR_REG, rdval);
730462306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_TXQ_DESCR_WR_ATTR_REG, wrval);
730562306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_TXQ_DESCR_RD_ATTR_REG, rdval);
730662306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_RXQ_DESCR_WR_ATTR_REG, wrval);
730762306a36Sopenharmony_ci
730862306a36Sopenharmony_ci	/* Buffer Data */
730962306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_TX_DATA_RD_ATTR_REG, rdval);
731062306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_RX_DATA_WR_ATTR_REG, wrval);
731162306a36Sopenharmony_ci
731262306a36Sopenharmony_ci	val = MVPP22_AXI_CODE_CACHE_NON_CACHE
731362306a36Sopenharmony_ci		<< MVPP22_AXI_CODE_CACHE_OFFS;
731462306a36Sopenharmony_ci	val |= MVPP22_AXI_CODE_DOMAIN_SYSTEM
731562306a36Sopenharmony_ci		<< MVPP22_AXI_CODE_DOMAIN_OFFS;
731662306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_RD_NORMAL_CODE_REG, val);
731762306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_WR_NORMAL_CODE_REG, val);
731862306a36Sopenharmony_ci
731962306a36Sopenharmony_ci	val = MVPP22_AXI_CODE_CACHE_RD_CACHE
732062306a36Sopenharmony_ci		<< MVPP22_AXI_CODE_CACHE_OFFS;
732162306a36Sopenharmony_ci	val |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM
732262306a36Sopenharmony_ci		<< MVPP22_AXI_CODE_DOMAIN_OFFS;
732362306a36Sopenharmony_ci
732462306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_RD_SNOOP_CODE_REG, val);
732562306a36Sopenharmony_ci
732662306a36Sopenharmony_ci	val = MVPP22_AXI_CODE_CACHE_WR_CACHE
732762306a36Sopenharmony_ci		<< MVPP22_AXI_CODE_CACHE_OFFS;
732862306a36Sopenharmony_ci	val |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM
732962306a36Sopenharmony_ci		<< MVPP22_AXI_CODE_DOMAIN_OFFS;
733062306a36Sopenharmony_ci
733162306a36Sopenharmony_ci	mvpp2_write(priv, MVPP22_AXI_WR_SNOOP_CODE_REG, val);
733262306a36Sopenharmony_ci}
733362306a36Sopenharmony_ci
733462306a36Sopenharmony_ci/* Initialize network controller common part HW */
733562306a36Sopenharmony_cistatic int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv)
733662306a36Sopenharmony_ci{
733762306a36Sopenharmony_ci	const struct mbus_dram_target_info *dram_target_info;
733862306a36Sopenharmony_ci	int err, i;
733962306a36Sopenharmony_ci	u32 val;
734062306a36Sopenharmony_ci
734162306a36Sopenharmony_ci	/* MBUS windows configuration */
734262306a36Sopenharmony_ci	dram_target_info = mv_mbus_dram_info();
734362306a36Sopenharmony_ci	if (dram_target_info)
734462306a36Sopenharmony_ci		mvpp2_conf_mbus_windows(dram_target_info, priv);
734562306a36Sopenharmony_ci
734662306a36Sopenharmony_ci	if (priv->hw_version >= MVPP22)
734762306a36Sopenharmony_ci		mvpp2_axi_init(priv);
734862306a36Sopenharmony_ci
734962306a36Sopenharmony_ci	/* Disable HW PHY polling */
735062306a36Sopenharmony_ci	if (priv->hw_version == MVPP21) {
735162306a36Sopenharmony_ci		val = readl(priv->lms_base + MVPP2_PHY_AN_CFG0_REG);
735262306a36Sopenharmony_ci		val |= MVPP2_PHY_AN_STOP_SMI0_MASK;
735362306a36Sopenharmony_ci		writel(val, priv->lms_base + MVPP2_PHY_AN_CFG0_REG);
735462306a36Sopenharmony_ci	} else {
735562306a36Sopenharmony_ci		val = readl(priv->iface_base + MVPP22_SMI_MISC_CFG_REG);
735662306a36Sopenharmony_ci		val &= ~MVPP22_SMI_POLLING_EN;
735762306a36Sopenharmony_ci		writel(val, priv->iface_base + MVPP22_SMI_MISC_CFG_REG);
735862306a36Sopenharmony_ci	}
735962306a36Sopenharmony_ci
736062306a36Sopenharmony_ci	/* Allocate and initialize aggregated TXQs */
736162306a36Sopenharmony_ci	priv->aggr_txqs = devm_kcalloc(&pdev->dev, MVPP2_MAX_THREADS,
736262306a36Sopenharmony_ci				       sizeof(*priv->aggr_txqs),
736362306a36Sopenharmony_ci				       GFP_KERNEL);
736462306a36Sopenharmony_ci	if (!priv->aggr_txqs)
736562306a36Sopenharmony_ci		return -ENOMEM;
736662306a36Sopenharmony_ci
736762306a36Sopenharmony_ci	for (i = 0; i < MVPP2_MAX_THREADS; i++) {
736862306a36Sopenharmony_ci		priv->aggr_txqs[i].id = i;
736962306a36Sopenharmony_ci		priv->aggr_txqs[i].size = MVPP2_AGGR_TXQ_SIZE;
737062306a36Sopenharmony_ci		err = mvpp2_aggr_txq_init(pdev, &priv->aggr_txqs[i], i, priv);
737162306a36Sopenharmony_ci		if (err < 0)
737262306a36Sopenharmony_ci			return err;
737362306a36Sopenharmony_ci	}
737462306a36Sopenharmony_ci
737562306a36Sopenharmony_ci	/* Fifo Init */
737662306a36Sopenharmony_ci	if (priv->hw_version == MVPP21) {
737762306a36Sopenharmony_ci		mvpp2_rx_fifo_init(priv);
737862306a36Sopenharmony_ci	} else {
737962306a36Sopenharmony_ci		mvpp22_rx_fifo_init(priv);
738062306a36Sopenharmony_ci		mvpp22_tx_fifo_init(priv);
738162306a36Sopenharmony_ci		if (priv->hw_version == MVPP23)
738262306a36Sopenharmony_ci			mvpp23_rx_fifo_fc_set_tresh(priv);
738362306a36Sopenharmony_ci	}
738462306a36Sopenharmony_ci
738562306a36Sopenharmony_ci	if (priv->hw_version == MVPP21)
738662306a36Sopenharmony_ci		writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT,
738762306a36Sopenharmony_ci		       priv->lms_base + MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG);
738862306a36Sopenharmony_ci
738962306a36Sopenharmony_ci	/* Allow cache snoop when transmiting packets */
739062306a36Sopenharmony_ci	mvpp2_write(priv, MVPP2_TX_SNOOP_REG, 0x1);
739162306a36Sopenharmony_ci
739262306a36Sopenharmony_ci	/* Buffer Manager initialization */
739362306a36Sopenharmony_ci	err = mvpp2_bm_init(&pdev->dev, priv);
739462306a36Sopenharmony_ci	if (err < 0)
739562306a36Sopenharmony_ci		return err;
739662306a36Sopenharmony_ci
739762306a36Sopenharmony_ci	/* Parser default initialization */
739862306a36Sopenharmony_ci	err = mvpp2_prs_default_init(pdev, priv);
739962306a36Sopenharmony_ci	if (err < 0)
740062306a36Sopenharmony_ci		return err;
740162306a36Sopenharmony_ci
740262306a36Sopenharmony_ci	/* Classifier default initialization */
740362306a36Sopenharmony_ci	mvpp2_cls_init(priv);
740462306a36Sopenharmony_ci
740562306a36Sopenharmony_ci	return 0;
740662306a36Sopenharmony_ci}
740762306a36Sopenharmony_ci
740862306a36Sopenharmony_cistatic int mvpp2_get_sram(struct platform_device *pdev,
740962306a36Sopenharmony_ci			  struct mvpp2 *priv)
741062306a36Sopenharmony_ci{
741162306a36Sopenharmony_ci	struct resource *res;
741262306a36Sopenharmony_ci	void __iomem *base;
741362306a36Sopenharmony_ci
741462306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
741562306a36Sopenharmony_ci	if (!res) {
741662306a36Sopenharmony_ci		if (has_acpi_companion(&pdev->dev))
741762306a36Sopenharmony_ci			dev_warn(&pdev->dev, "ACPI is too old, Flow control not supported\n");
741862306a36Sopenharmony_ci		else
741962306a36Sopenharmony_ci			dev_warn(&pdev->dev, "DT is too old, Flow control not supported\n");
742062306a36Sopenharmony_ci		return 0;
742162306a36Sopenharmony_ci	}
742262306a36Sopenharmony_ci
742362306a36Sopenharmony_ci	base = devm_ioremap_resource(&pdev->dev, res);
742462306a36Sopenharmony_ci	if (IS_ERR(base))
742562306a36Sopenharmony_ci		return PTR_ERR(base);
742662306a36Sopenharmony_ci
742762306a36Sopenharmony_ci	priv->cm3_base = base;
742862306a36Sopenharmony_ci	return 0;
742962306a36Sopenharmony_ci}
743062306a36Sopenharmony_ci
743162306a36Sopenharmony_cistatic int mvpp2_probe(struct platform_device *pdev)
743262306a36Sopenharmony_ci{
743362306a36Sopenharmony_ci	struct fwnode_handle *fwnode = pdev->dev.fwnode;
743462306a36Sopenharmony_ci	struct fwnode_handle *port_fwnode;
743562306a36Sopenharmony_ci	struct mvpp2 *priv;
743662306a36Sopenharmony_ci	struct resource *res;
743762306a36Sopenharmony_ci	void __iomem *base;
743862306a36Sopenharmony_ci	int i, shared;
743962306a36Sopenharmony_ci	int err;
744062306a36Sopenharmony_ci
744162306a36Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
744262306a36Sopenharmony_ci	if (!priv)
744362306a36Sopenharmony_ci		return -ENOMEM;
744462306a36Sopenharmony_ci
744562306a36Sopenharmony_ci	priv->hw_version = (unsigned long)device_get_match_data(&pdev->dev);
744662306a36Sopenharmony_ci
744762306a36Sopenharmony_ci	/* multi queue mode isn't supported on PPV2.1, fallback to single
744862306a36Sopenharmony_ci	 * mode
744962306a36Sopenharmony_ci	 */
745062306a36Sopenharmony_ci	if (priv->hw_version == MVPP21)
745162306a36Sopenharmony_ci		queue_mode = MVPP2_QDIST_SINGLE_MODE;
745262306a36Sopenharmony_ci
745362306a36Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
745462306a36Sopenharmony_ci	if (IS_ERR(base))
745562306a36Sopenharmony_ci		return PTR_ERR(base);
745662306a36Sopenharmony_ci
745762306a36Sopenharmony_ci	if (priv->hw_version == MVPP21) {
745862306a36Sopenharmony_ci		priv->lms_base = devm_platform_ioremap_resource(pdev, 1);
745962306a36Sopenharmony_ci		if (IS_ERR(priv->lms_base))
746062306a36Sopenharmony_ci			return PTR_ERR(priv->lms_base);
746162306a36Sopenharmony_ci	} else {
746262306a36Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
746362306a36Sopenharmony_ci		if (!res) {
746462306a36Sopenharmony_ci			dev_err(&pdev->dev, "Invalid resource\n");
746562306a36Sopenharmony_ci			return -EINVAL;
746662306a36Sopenharmony_ci		}
746762306a36Sopenharmony_ci		if (has_acpi_companion(&pdev->dev)) {
746862306a36Sopenharmony_ci			/* In case the MDIO memory region is declared in
746962306a36Sopenharmony_ci			 * the ACPI, it can already appear as 'in-use'
747062306a36Sopenharmony_ci			 * in the OS. Because it is overlapped by second
747162306a36Sopenharmony_ci			 * region of the network controller, make
747262306a36Sopenharmony_ci			 * sure it is released, before requesting it again.
747362306a36Sopenharmony_ci			 * The care is taken by mvpp2 driver to avoid
747462306a36Sopenharmony_ci			 * concurrent access to this memory region.
747562306a36Sopenharmony_ci			 */
747662306a36Sopenharmony_ci			release_resource(res);
747762306a36Sopenharmony_ci		}
747862306a36Sopenharmony_ci		priv->iface_base = devm_ioremap_resource(&pdev->dev, res);
747962306a36Sopenharmony_ci		if (IS_ERR(priv->iface_base))
748062306a36Sopenharmony_ci			return PTR_ERR(priv->iface_base);
748162306a36Sopenharmony_ci
748262306a36Sopenharmony_ci		/* Map CM3 SRAM */
748362306a36Sopenharmony_ci		err = mvpp2_get_sram(pdev, priv);
748462306a36Sopenharmony_ci		if (err)
748562306a36Sopenharmony_ci			dev_warn(&pdev->dev, "Fail to alloc CM3 SRAM\n");
748662306a36Sopenharmony_ci
748762306a36Sopenharmony_ci		/* Enable global Flow Control only if handler to SRAM not NULL */
748862306a36Sopenharmony_ci		if (priv->cm3_base)
748962306a36Sopenharmony_ci			priv->global_tx_fc = true;
749062306a36Sopenharmony_ci	}
749162306a36Sopenharmony_ci
749262306a36Sopenharmony_ci	if (priv->hw_version >= MVPP22 && dev_of_node(&pdev->dev)) {
749362306a36Sopenharmony_ci		priv->sysctrl_base =
749462306a36Sopenharmony_ci			syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
749562306a36Sopenharmony_ci							"marvell,system-controller");
749662306a36Sopenharmony_ci		if (IS_ERR(priv->sysctrl_base))
749762306a36Sopenharmony_ci			/* The system controller regmap is optional for dt
749862306a36Sopenharmony_ci			 * compatibility reasons. When not provided, the
749962306a36Sopenharmony_ci			 * configuration of the GoP relies on the
750062306a36Sopenharmony_ci			 * firmware/bootloader.
750162306a36Sopenharmony_ci			 */
750262306a36Sopenharmony_ci			priv->sysctrl_base = NULL;
750362306a36Sopenharmony_ci	}
750462306a36Sopenharmony_ci
750562306a36Sopenharmony_ci	if (priv->hw_version >= MVPP22 &&
750662306a36Sopenharmony_ci	    mvpp2_get_nrxqs(priv) * 2 <= MVPP2_BM_MAX_POOLS)
750762306a36Sopenharmony_ci		priv->percpu_pools = 1;
750862306a36Sopenharmony_ci
750962306a36Sopenharmony_ci	mvpp2_setup_bm_pool();
751062306a36Sopenharmony_ci
751162306a36Sopenharmony_ci
751262306a36Sopenharmony_ci	priv->nthreads = min_t(unsigned int, num_present_cpus(),
751362306a36Sopenharmony_ci			       MVPP2_MAX_THREADS);
751462306a36Sopenharmony_ci
751562306a36Sopenharmony_ci	shared = num_present_cpus() - priv->nthreads;
751662306a36Sopenharmony_ci	if (shared > 0)
751762306a36Sopenharmony_ci		bitmap_set(&priv->lock_map, 0,
751862306a36Sopenharmony_ci			    min_t(int, shared, MVPP2_MAX_THREADS));
751962306a36Sopenharmony_ci
752062306a36Sopenharmony_ci	for (i = 0; i < MVPP2_MAX_THREADS; i++) {
752162306a36Sopenharmony_ci		u32 addr_space_sz;
752262306a36Sopenharmony_ci
752362306a36Sopenharmony_ci		addr_space_sz = (priv->hw_version == MVPP21 ?
752462306a36Sopenharmony_ci				 MVPP21_ADDR_SPACE_SZ : MVPP22_ADDR_SPACE_SZ);
752562306a36Sopenharmony_ci		priv->swth_base[i] = base + i * addr_space_sz;
752662306a36Sopenharmony_ci	}
752762306a36Sopenharmony_ci
752862306a36Sopenharmony_ci	if (priv->hw_version == MVPP21)
752962306a36Sopenharmony_ci		priv->max_port_rxqs = 8;
753062306a36Sopenharmony_ci	else
753162306a36Sopenharmony_ci		priv->max_port_rxqs = 32;
753262306a36Sopenharmony_ci
753362306a36Sopenharmony_ci	if (dev_of_node(&pdev->dev)) {
753462306a36Sopenharmony_ci		priv->pp_clk = devm_clk_get(&pdev->dev, "pp_clk");
753562306a36Sopenharmony_ci		if (IS_ERR(priv->pp_clk))
753662306a36Sopenharmony_ci			return PTR_ERR(priv->pp_clk);
753762306a36Sopenharmony_ci		err = clk_prepare_enable(priv->pp_clk);
753862306a36Sopenharmony_ci		if (err < 0)
753962306a36Sopenharmony_ci			return err;
754062306a36Sopenharmony_ci
754162306a36Sopenharmony_ci		priv->gop_clk = devm_clk_get(&pdev->dev, "gop_clk");
754262306a36Sopenharmony_ci		if (IS_ERR(priv->gop_clk)) {
754362306a36Sopenharmony_ci			err = PTR_ERR(priv->gop_clk);
754462306a36Sopenharmony_ci			goto err_pp_clk;
754562306a36Sopenharmony_ci		}
754662306a36Sopenharmony_ci		err = clk_prepare_enable(priv->gop_clk);
754762306a36Sopenharmony_ci		if (err < 0)
754862306a36Sopenharmony_ci			goto err_pp_clk;
754962306a36Sopenharmony_ci
755062306a36Sopenharmony_ci		if (priv->hw_version >= MVPP22) {
755162306a36Sopenharmony_ci			priv->mg_clk = devm_clk_get(&pdev->dev, "mg_clk");
755262306a36Sopenharmony_ci			if (IS_ERR(priv->mg_clk)) {
755362306a36Sopenharmony_ci				err = PTR_ERR(priv->mg_clk);
755462306a36Sopenharmony_ci				goto err_gop_clk;
755562306a36Sopenharmony_ci			}
755662306a36Sopenharmony_ci
755762306a36Sopenharmony_ci			err = clk_prepare_enable(priv->mg_clk);
755862306a36Sopenharmony_ci			if (err < 0)
755962306a36Sopenharmony_ci				goto err_gop_clk;
756062306a36Sopenharmony_ci
756162306a36Sopenharmony_ci			priv->mg_core_clk = devm_clk_get_optional(&pdev->dev, "mg_core_clk");
756262306a36Sopenharmony_ci			if (IS_ERR(priv->mg_core_clk)) {
756362306a36Sopenharmony_ci				err = PTR_ERR(priv->mg_core_clk);
756462306a36Sopenharmony_ci				goto err_mg_clk;
756562306a36Sopenharmony_ci			}
756662306a36Sopenharmony_ci
756762306a36Sopenharmony_ci			err = clk_prepare_enable(priv->mg_core_clk);
756862306a36Sopenharmony_ci			if (err < 0)
756962306a36Sopenharmony_ci				goto err_mg_clk;
757062306a36Sopenharmony_ci		}
757162306a36Sopenharmony_ci
757262306a36Sopenharmony_ci		priv->axi_clk = devm_clk_get_optional(&pdev->dev, "axi_clk");
757362306a36Sopenharmony_ci		if (IS_ERR(priv->axi_clk)) {
757462306a36Sopenharmony_ci			err = PTR_ERR(priv->axi_clk);
757562306a36Sopenharmony_ci			goto err_mg_core_clk;
757662306a36Sopenharmony_ci		}
757762306a36Sopenharmony_ci
757862306a36Sopenharmony_ci		err = clk_prepare_enable(priv->axi_clk);
757962306a36Sopenharmony_ci		if (err < 0)
758062306a36Sopenharmony_ci			goto err_mg_core_clk;
758162306a36Sopenharmony_ci
758262306a36Sopenharmony_ci		/* Get system's tclk rate */
758362306a36Sopenharmony_ci		priv->tclk = clk_get_rate(priv->pp_clk);
758462306a36Sopenharmony_ci	} else {
758562306a36Sopenharmony_ci		err = device_property_read_u32(&pdev->dev, "clock-frequency", &priv->tclk);
758662306a36Sopenharmony_ci		if (err) {
758762306a36Sopenharmony_ci			dev_err(&pdev->dev, "missing clock-frequency value\n");
758862306a36Sopenharmony_ci			return err;
758962306a36Sopenharmony_ci		}
759062306a36Sopenharmony_ci	}
759162306a36Sopenharmony_ci
759262306a36Sopenharmony_ci	if (priv->hw_version >= MVPP22) {
759362306a36Sopenharmony_ci		err = dma_set_mask(&pdev->dev, MVPP2_DESC_DMA_MASK);
759462306a36Sopenharmony_ci		if (err)
759562306a36Sopenharmony_ci			goto err_axi_clk;
759662306a36Sopenharmony_ci		/* Sadly, the BM pools all share the same register to
759762306a36Sopenharmony_ci		 * store the high 32 bits of their address. So they
759862306a36Sopenharmony_ci		 * must all have the same high 32 bits, which forces
759962306a36Sopenharmony_ci		 * us to restrict coherent memory to DMA_BIT_MASK(32).
760062306a36Sopenharmony_ci		 */
760162306a36Sopenharmony_ci		err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
760262306a36Sopenharmony_ci		if (err)
760362306a36Sopenharmony_ci			goto err_axi_clk;
760462306a36Sopenharmony_ci	}
760562306a36Sopenharmony_ci
760662306a36Sopenharmony_ci	/* Map DTS-active ports. Should be done before FIFO mvpp2_init */
760762306a36Sopenharmony_ci	fwnode_for_each_available_child_node(fwnode, port_fwnode) {
760862306a36Sopenharmony_ci		if (!fwnode_property_read_u32(port_fwnode, "port-id", &i))
760962306a36Sopenharmony_ci			priv->port_map |= BIT(i);
761062306a36Sopenharmony_ci	}
761162306a36Sopenharmony_ci
761262306a36Sopenharmony_ci	if (mvpp2_read(priv, MVPP2_VER_ID_REG) == MVPP2_VER_PP23)
761362306a36Sopenharmony_ci		priv->hw_version = MVPP23;
761462306a36Sopenharmony_ci
761562306a36Sopenharmony_ci	/* Init mss lock */
761662306a36Sopenharmony_ci	spin_lock_init(&priv->mss_spinlock);
761762306a36Sopenharmony_ci
761862306a36Sopenharmony_ci	/* Initialize network controller */
761962306a36Sopenharmony_ci	err = mvpp2_init(pdev, priv);
762062306a36Sopenharmony_ci	if (err < 0) {
762162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to initialize controller\n");
762262306a36Sopenharmony_ci		goto err_axi_clk;
762362306a36Sopenharmony_ci	}
762462306a36Sopenharmony_ci
762562306a36Sopenharmony_ci	err = mvpp22_tai_probe(&pdev->dev, priv);
762662306a36Sopenharmony_ci	if (err < 0)
762762306a36Sopenharmony_ci		goto err_axi_clk;
762862306a36Sopenharmony_ci
762962306a36Sopenharmony_ci	/* Initialize ports */
763062306a36Sopenharmony_ci	fwnode_for_each_available_child_node(fwnode, port_fwnode) {
763162306a36Sopenharmony_ci		err = mvpp2_port_probe(pdev, port_fwnode, priv);
763262306a36Sopenharmony_ci		if (err < 0)
763362306a36Sopenharmony_ci			goto err_port_probe;
763462306a36Sopenharmony_ci	}
763562306a36Sopenharmony_ci
763662306a36Sopenharmony_ci	if (priv->port_count == 0) {
763762306a36Sopenharmony_ci		dev_err(&pdev->dev, "no ports enabled\n");
763862306a36Sopenharmony_ci		err = -ENODEV;
763962306a36Sopenharmony_ci		goto err_axi_clk;
764062306a36Sopenharmony_ci	}
764162306a36Sopenharmony_ci
764262306a36Sopenharmony_ci	/* Statistics must be gathered regularly because some of them (like
764362306a36Sopenharmony_ci	 * packets counters) are 32-bit registers and could overflow quite
764462306a36Sopenharmony_ci	 * quickly. For instance, a 10Gb link used at full bandwidth with the
764562306a36Sopenharmony_ci	 * smallest packets (64B) will overflow a 32-bit counter in less than
764662306a36Sopenharmony_ci	 * 30 seconds. Then, use a workqueue to fill 64-bit counters.
764762306a36Sopenharmony_ci	 */
764862306a36Sopenharmony_ci	snprintf(priv->queue_name, sizeof(priv->queue_name),
764962306a36Sopenharmony_ci		 "stats-wq-%s%s", netdev_name(priv->port_list[0]->dev),
765062306a36Sopenharmony_ci		 priv->port_count > 1 ? "+" : "");
765162306a36Sopenharmony_ci	priv->stats_queue = create_singlethread_workqueue(priv->queue_name);
765262306a36Sopenharmony_ci	if (!priv->stats_queue) {
765362306a36Sopenharmony_ci		err = -ENOMEM;
765462306a36Sopenharmony_ci		goto err_port_probe;
765562306a36Sopenharmony_ci	}
765662306a36Sopenharmony_ci
765762306a36Sopenharmony_ci	if (priv->global_tx_fc && priv->hw_version >= MVPP22) {
765862306a36Sopenharmony_ci		err = mvpp2_enable_global_fc(priv);
765962306a36Sopenharmony_ci		if (err)
766062306a36Sopenharmony_ci			dev_warn(&pdev->dev, "Minimum of CM3 firmware 18.09 and chip revision B0 required for flow control\n");
766162306a36Sopenharmony_ci	}
766262306a36Sopenharmony_ci
766362306a36Sopenharmony_ci	mvpp2_dbgfs_init(priv, pdev->name);
766462306a36Sopenharmony_ci
766562306a36Sopenharmony_ci	platform_set_drvdata(pdev, priv);
766662306a36Sopenharmony_ci	return 0;
766762306a36Sopenharmony_ci
766862306a36Sopenharmony_cierr_port_probe:
766962306a36Sopenharmony_ci	fwnode_handle_put(port_fwnode);
767062306a36Sopenharmony_ci
767162306a36Sopenharmony_ci	i = 0;
767262306a36Sopenharmony_ci	fwnode_for_each_available_child_node(fwnode, port_fwnode) {
767362306a36Sopenharmony_ci		if (priv->port_list[i])
767462306a36Sopenharmony_ci			mvpp2_port_remove(priv->port_list[i]);
767562306a36Sopenharmony_ci		i++;
767662306a36Sopenharmony_ci	}
767762306a36Sopenharmony_cierr_axi_clk:
767862306a36Sopenharmony_ci	clk_disable_unprepare(priv->axi_clk);
767962306a36Sopenharmony_cierr_mg_core_clk:
768062306a36Sopenharmony_ci	clk_disable_unprepare(priv->mg_core_clk);
768162306a36Sopenharmony_cierr_mg_clk:
768262306a36Sopenharmony_ci	clk_disable_unprepare(priv->mg_clk);
768362306a36Sopenharmony_cierr_gop_clk:
768462306a36Sopenharmony_ci	clk_disable_unprepare(priv->gop_clk);
768562306a36Sopenharmony_cierr_pp_clk:
768662306a36Sopenharmony_ci	clk_disable_unprepare(priv->pp_clk);
768762306a36Sopenharmony_ci	return err;
768862306a36Sopenharmony_ci}
768962306a36Sopenharmony_ci
769062306a36Sopenharmony_cistatic int mvpp2_remove(struct platform_device *pdev)
769162306a36Sopenharmony_ci{
769262306a36Sopenharmony_ci	struct mvpp2 *priv = platform_get_drvdata(pdev);
769362306a36Sopenharmony_ci	struct fwnode_handle *fwnode = pdev->dev.fwnode;
769462306a36Sopenharmony_ci	int i = 0, poolnum = MVPP2_BM_POOLS_NUM;
769562306a36Sopenharmony_ci	struct fwnode_handle *port_fwnode;
769662306a36Sopenharmony_ci
769762306a36Sopenharmony_ci	mvpp2_dbgfs_cleanup(priv);
769862306a36Sopenharmony_ci
769962306a36Sopenharmony_ci	fwnode_for_each_available_child_node(fwnode, port_fwnode) {
770062306a36Sopenharmony_ci		if (priv->port_list[i]) {
770162306a36Sopenharmony_ci			mutex_destroy(&priv->port_list[i]->gather_stats_lock);
770262306a36Sopenharmony_ci			mvpp2_port_remove(priv->port_list[i]);
770362306a36Sopenharmony_ci		}
770462306a36Sopenharmony_ci		i++;
770562306a36Sopenharmony_ci	}
770662306a36Sopenharmony_ci
770762306a36Sopenharmony_ci	destroy_workqueue(priv->stats_queue);
770862306a36Sopenharmony_ci
770962306a36Sopenharmony_ci	if (priv->percpu_pools)
771062306a36Sopenharmony_ci		poolnum = mvpp2_get_nrxqs(priv) * 2;
771162306a36Sopenharmony_ci
771262306a36Sopenharmony_ci	for (i = 0; i < poolnum; i++) {
771362306a36Sopenharmony_ci		struct mvpp2_bm_pool *bm_pool = &priv->bm_pools[i];
771462306a36Sopenharmony_ci
771562306a36Sopenharmony_ci		mvpp2_bm_pool_destroy(&pdev->dev, priv, bm_pool);
771662306a36Sopenharmony_ci	}
771762306a36Sopenharmony_ci
771862306a36Sopenharmony_ci	for (i = 0; i < MVPP2_MAX_THREADS; i++) {
771962306a36Sopenharmony_ci		struct mvpp2_tx_queue *aggr_txq = &priv->aggr_txqs[i];
772062306a36Sopenharmony_ci
772162306a36Sopenharmony_ci		dma_free_coherent(&pdev->dev,
772262306a36Sopenharmony_ci				  MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE,
772362306a36Sopenharmony_ci				  aggr_txq->descs,
772462306a36Sopenharmony_ci				  aggr_txq->descs_dma);
772562306a36Sopenharmony_ci	}
772662306a36Sopenharmony_ci
772762306a36Sopenharmony_ci	if (is_acpi_node(port_fwnode))
772862306a36Sopenharmony_ci		return 0;
772962306a36Sopenharmony_ci
773062306a36Sopenharmony_ci	clk_disable_unprepare(priv->axi_clk);
773162306a36Sopenharmony_ci	clk_disable_unprepare(priv->mg_core_clk);
773262306a36Sopenharmony_ci	clk_disable_unprepare(priv->mg_clk);
773362306a36Sopenharmony_ci	clk_disable_unprepare(priv->pp_clk);
773462306a36Sopenharmony_ci	clk_disable_unprepare(priv->gop_clk);
773562306a36Sopenharmony_ci
773662306a36Sopenharmony_ci	return 0;
773762306a36Sopenharmony_ci}
773862306a36Sopenharmony_ci
773962306a36Sopenharmony_cistatic const struct of_device_id mvpp2_match[] = {
774062306a36Sopenharmony_ci	{
774162306a36Sopenharmony_ci		.compatible = "marvell,armada-375-pp2",
774262306a36Sopenharmony_ci		.data = (void *)MVPP21,
774362306a36Sopenharmony_ci	},
774462306a36Sopenharmony_ci	{
774562306a36Sopenharmony_ci		.compatible = "marvell,armada-7k-pp22",
774662306a36Sopenharmony_ci		.data = (void *)MVPP22,
774762306a36Sopenharmony_ci	},
774862306a36Sopenharmony_ci	{ }
774962306a36Sopenharmony_ci};
775062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mvpp2_match);
775162306a36Sopenharmony_ci
775262306a36Sopenharmony_ci#ifdef CONFIG_ACPI
775362306a36Sopenharmony_cistatic const struct acpi_device_id mvpp2_acpi_match[] = {
775462306a36Sopenharmony_ci	{ "MRVL0110", MVPP22 },
775562306a36Sopenharmony_ci	{ },
775662306a36Sopenharmony_ci};
775762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, mvpp2_acpi_match);
775862306a36Sopenharmony_ci#endif
775962306a36Sopenharmony_ci
776062306a36Sopenharmony_cistatic struct platform_driver mvpp2_driver = {
776162306a36Sopenharmony_ci	.probe = mvpp2_probe,
776262306a36Sopenharmony_ci	.remove = mvpp2_remove,
776362306a36Sopenharmony_ci	.driver = {
776462306a36Sopenharmony_ci		.name = MVPP2_DRIVER_NAME,
776562306a36Sopenharmony_ci		.of_match_table = mvpp2_match,
776662306a36Sopenharmony_ci		.acpi_match_table = ACPI_PTR(mvpp2_acpi_match),
776762306a36Sopenharmony_ci	},
776862306a36Sopenharmony_ci};
776962306a36Sopenharmony_ci
777062306a36Sopenharmony_cistatic int __init mvpp2_driver_init(void)
777162306a36Sopenharmony_ci{
777262306a36Sopenharmony_ci	return platform_driver_register(&mvpp2_driver);
777362306a36Sopenharmony_ci}
777462306a36Sopenharmony_cimodule_init(mvpp2_driver_init);
777562306a36Sopenharmony_ci
777662306a36Sopenharmony_cistatic void __exit mvpp2_driver_exit(void)
777762306a36Sopenharmony_ci{
777862306a36Sopenharmony_ci	platform_driver_unregister(&mvpp2_driver);
777962306a36Sopenharmony_ci	mvpp2_dbgfs_exit();
778062306a36Sopenharmony_ci}
778162306a36Sopenharmony_cimodule_exit(mvpp2_driver_exit);
778262306a36Sopenharmony_ci
778362306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell PPv2 Ethernet Driver - www.marvell.com");
778462306a36Sopenharmony_ciMODULE_AUTHOR("Marcin Wojtas <mw@semihalf.com>");
778562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
7786