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