Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  stmmac_main.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*******************************************************************************
  This is the driver for the ST MAC 10/100/1000 on-chip Ethernet controllers.
  ST Ethernet IPs are built around a Synopsys IP Core.

Copyright(C) 2007-2011 STMicroelectronics Ltd


  Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>

  Documentation available at:
http://www.stlinux.com
  Support available at:
https://bugzilla.stlinux.com/
*******************************************************************************/


#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/if_ether.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/prefetch.h>
#include <linux/pinctrl/consumer.h>
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#endif /* CONFIG_DEBUG_FS */
#include <linux/net_tstamp.h>
#include <linux/phylink.h>
#include <linux/udp.h>
#include <linux/bpf_trace.h>
#include <net/page_pool/helpers.h>
#include <net/pkt_cls.h>
#include <net/xdp_sock_drv.h>
#include "stmmac_ptp.h"
#include "stmmac_fpe.h"
#include "stmmac.h"
#include "stmmac_xdp.h"
#include <linux/reset.h>
#include <linux/of_mdio.h>
#include "dwmac1000.h"
#include "dwxgmac2.h"
#include "hwif.h"

/* As long as the interface is active, we keep the timestamping counter enabled
 * with fine resolution and binary rollover. This avoid non-monotonic behavior
 * (clock jumps) when changing timestamping settings at runtime.
 */

#define STMMAC_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | \
     PTP_TCR_TSCTRLSSR)

#define STMMAC_ALIGN(x)  ALIGN(ALIGN(x, SMP_CACHE_BYTES), 16)
#define TSO_MAX_BUFF_SIZE (SZ_16K - 1)

/* Module parameters */
#define TX_TIMEO 5000
static int watchdog = TX_TIMEO;
module_param(watchdog, int, 0644);
MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds (default 5s)");

static int debug = -1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Message Level (-1: default, 0: no output, 16: all)");

static int phyaddr = -1;
module_param(phyaddr, int, 0444);
MODULE_PARM_DESC(phyaddr, "Physical device address");

#define STMMAC_TX_THRESH(x) ((x)->dma_conf.dma_tx_size / 4)

/* Limit to make sure XDP TX and slow path can coexist */
#define STMMAC_XSK_TX_BUDGET_MAX 256
#define STMMAC_TX_XSK_AVAIL  16
#define STMMAC_RX_FILL_BATCH  16

#define STMMAC_XDP_PASS  0
#define STMMAC_XDP_CONSUMED BIT(0)
#define STMMAC_XDP_TX  BIT(1)
#define STMMAC_XDP_REDIRECT BIT(2)

static int flow_ctrl = 0xdead;
module_param(flow_ctrl, int, 0644);
MODULE_PARM_DESC(flow_ctrl, "Flow control ability [on/off] (obsolete)");

static int pause = PAUSE_TIME;
module_param(pause, int, 0644);
MODULE_PARM_DESC(pause, "Flow Control Pause Time (units of 512 bit times)");

#define TC_DEFAULT 64
static int tc = TC_DEFAULT;
module_param(tc, int, 0644);
MODULE_PARM_DESC(tc, "DMA threshold control value");

/* This is unused */
#define DEFAULT_BUFSIZE 1536
static int buf_sz = DEFAULT_BUFSIZE;
module_param(buf_sz, int, 0644);
MODULE_PARM_DESC(buf_sz, "DMA buffer size");

static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
          NETIF_MSG_LINK | NETIF_MSG_IFUP |
          NETIF_MSG_IFDOWN | NETIF_MSG_TIMER);

#define STMMAC_DEFAULT_LPI_TIMER 1000
static unsigned int eee_timer = STMMAC_DEFAULT_LPI_TIMER;
module_param(eee_timer, uint, 0644);
MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec");
#define STMMAC_LPI_T(x) (jiffies + usecs_to_jiffies(x))

/* By default the driver will use the ring mode to manage tx and rx descriptors,
 * but allow user to force to use the chain instead of the ring
 */

static unsigned int chain_mode;
module_param(chain_mode, int, 0444);
MODULE_PARM_DESC(chain_mode, "To use chain instead of ring mode");

static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
/* For MSI interrupts handling */
static irqreturn_t stmmac_mac_interrupt(int irq, void *dev_id);
static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id);
static irqreturn_t stmmac_msi_intr_tx(int irq, void *data);
static irqreturn_t stmmac_msi_intr_rx(int irq, void *data);
static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue);
static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue);
static void stmmac_reset_queues_param(struct stmmac_priv *priv);
static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue);
static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue);
static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
       u32 rxmode, u32 chan);

#ifdef CONFIG_DEBUG_FS
static const struct net_device_ops stmmac_netdev_ops;
static void stmmac_init_fs(struct net_device *dev);
static void stmmac_exit_fs(struct net_device *dev);
#endif

#define STMMAC_COAL_TIMER(x) (ns_to_ktime((x) * NSEC_PER_USEC))

int stmmac_bus_clks_config(struct stmmac_priv *priv, bool enabled)
{
 int ret = 0;

 if (enabled) {
  ret = clk_prepare_enable(priv->plat->stmmac_clk);
  if (ret)
   return ret;
  ret = clk_prepare_enable(priv->plat->pclk);
  if (ret) {
   clk_disable_unprepare(priv->plat->stmmac_clk);
   return ret;
  }
  if (priv->plat->clks_config) {
   ret = priv->plat->clks_config(priv->plat->bsp_priv, enabled);
   if (ret) {
    clk_disable_unprepare(priv->plat->stmmac_clk);
    clk_disable_unprepare(priv->plat->pclk);
    return ret;
   }
  }
 } else {
  clk_disable_unprepare(priv->plat->stmmac_clk);
  clk_disable_unprepare(priv->plat->pclk);
  if (priv->plat->clks_config)
   priv->plat->clks_config(priv->plat->bsp_priv, enabled);
 }

 return ret;
}
EXPORT_SYMBOL_GPL(stmmac_bus_clks_config);

/**
 * stmmac_set_clk_tx_rate() - set the clock rate for the MAC transmit clock
 * @bsp_priv: BSP private data structure (unused)
 * @clk_tx_i: the transmit clock
 * @interface: the selected interface mode
 * @speed: the speed that the MAC will be operating at
 *
 * Set the transmit clock rate for the MAC, normally 2.5MHz for 10Mbps,
 * 25MHz for 100Mbps and 125MHz for 1Gbps. This is suitable for at least
 * MII, GMII, RGMII and RMII interface modes. Platforms can hook this into
 * the plat_data->set_clk_tx_rate method directly, call it via their own
 * implementation, or implement their own method should they have more
 * complex requirements. It is intended to only be used in this method.
 *
 * plat_data->clk_tx_i must be filled in.
 */

int stmmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
      phy_interface_t interface, int speed)
{
 long rate = rgmii_clock(speed);

 /* Silently ignore unsupported speeds as rgmii_clock() only
 * supports 10, 100 and 1000Mbps. We do not want to spit
 * errors for 2500 and higher speeds here.
 */

 if (rate < 0)
  return 0;

 return clk_set_rate(clk_tx_i, rate);
}
EXPORT_SYMBOL_GPL(stmmac_set_clk_tx_rate);

/**
 * stmmac_verify_args - verify the driver parameters.
 * Description: it checks the driver parameters and set a default in case of
 * errors.
 */

static void stmmac_verify_args(void)
{
 if (unlikely(watchdog < 0))
  watchdog = TX_TIMEO;
 if (unlikely((pause < 0) || (pause > 0xffff)))
  pause = PAUSE_TIME;

 if (flow_ctrl != 0xdead)
  pr_warn("stmmac: module parameter 'flow_ctrl' is obsolete - please remove from your module configuration\n");
}

static void __stmmac_disable_all_queues(struct stmmac_priv *priv)
{
 u32 rx_queues_cnt = priv->plat->rx_queues_to_use;
 u32 tx_queues_cnt = priv->plat->tx_queues_to_use;
 u32 maxq = max(rx_queues_cnt, tx_queues_cnt);
 u32 queue;

 for (queue = 0; queue < maxq; queue++) {
  struct stmmac_channel *ch = &priv->channel[queue];

  if (stmmac_xdp_is_enabled(priv) &&
      test_bit(queue, priv->af_xdp_zc_qps)) {
   napi_disable(&ch->rxtx_napi);
   continue;
  }

  if (queue < rx_queues_cnt)
   napi_disable(&ch->rx_napi);
  if (queue < tx_queues_cnt)
   napi_disable(&ch->tx_napi);
 }
}

/**
 * stmmac_disable_all_queues - Disable all queues
 * @priv: driver private structure
 */

static void stmmac_disable_all_queues(struct stmmac_priv *priv)
{
 u32 rx_queues_cnt = priv->plat->rx_queues_to_use;
 struct stmmac_rx_queue *rx_q;
 u32 queue;

 /* synchronize_rcu() needed for pending XDP buffers to drain */
 for (queue = 0; queue < rx_queues_cnt; queue++) {
  rx_q = &priv->dma_conf.rx_queue[queue];
  if (rx_q->xsk_pool) {
   synchronize_rcu();
   break;
  }
 }

 __stmmac_disable_all_queues(priv);
}

/**
 * stmmac_enable_all_queues - Enable all queues
 * @priv: driver private structure
 */

static void stmmac_enable_all_queues(struct stmmac_priv *priv)
{
 u32 rx_queues_cnt = priv->plat->rx_queues_to_use;
 u32 tx_queues_cnt = priv->plat->tx_queues_to_use;
 u32 maxq = max(rx_queues_cnt, tx_queues_cnt);
 u32 queue;

 for (queue = 0; queue < maxq; queue++) {
  struct stmmac_channel *ch = &priv->channel[queue];

  if (stmmac_xdp_is_enabled(priv) &&
      test_bit(queue, priv->af_xdp_zc_qps)) {
   napi_enable(&ch->rxtx_napi);
   continue;
  }

  if (queue < rx_queues_cnt)
   napi_enable(&ch->rx_napi);
  if (queue < tx_queues_cnt)
   napi_enable(&ch->tx_napi);
 }
}

static void stmmac_service_event_schedule(struct stmmac_priv *priv)
{
 if (!test_bit(STMMAC_DOWN, &priv->state) &&
     !test_and_set_bit(STMMAC_SERVICE_SCHED, &priv->state))
  queue_work(priv->wq, &priv->service_task);
}

static void stmmac_global_err(struct stmmac_priv *priv)
{
 netif_carrier_off(priv->dev);
 set_bit(STMMAC_RESET_REQUESTED, &priv->state);
 stmmac_service_event_schedule(priv);
}

/**
 * stmmac_clk_csr_set - dynamically set the MDC clock
 * @priv: driver private structure
 * Description: this is to dynamically set the MDC clock according to the csr
 * clock input.
 * Note:
 * If a specific clk_csr value is passed from the platform
 * this means that the CSR Clock Range selection cannot be
 * changed at run-time and it is fixed (as reported in the driver
 * documentation). Viceversa the driver will try to set the MDC
 * clock dynamically according to the actual clock input.
 */

static void stmmac_clk_csr_set(struct stmmac_priv *priv)
{
 unsigned long clk_rate;

 clk_rate = clk_get_rate(priv->plat->stmmac_clk);

 /* Platform provided default clk_csr would be assumed valid
 * for all other cases except for the below mentioned ones.
 * For values higher than the IEEE 802.3 specified frequency
 * we can not estimate the proper divider as it is not known
 * the frequency of clk_csr_i. So we do not change the default
 * divider.
 */

 if (!(priv->clk_csr & MAC_CSR_H_FRQ_MASK)) {
  if (clk_rate < CSR_F_35M)
   priv->clk_csr = STMMAC_CSR_20_35M;
  else if ((clk_rate >= CSR_F_35M) && (clk_rate < CSR_F_60M))
   priv->clk_csr = STMMAC_CSR_35_60M;
  else if ((clk_rate >= CSR_F_60M) && (clk_rate < CSR_F_100M))
   priv->clk_csr = STMMAC_CSR_60_100M;
  else if ((clk_rate >= CSR_F_100M) && (clk_rate < CSR_F_150M))
   priv->clk_csr = STMMAC_CSR_100_150M;
  else if ((clk_rate >= CSR_F_150M) && (clk_rate < CSR_F_250M))
   priv->clk_csr = STMMAC_CSR_150_250M;
  else if ((clk_rate >= CSR_F_250M) && (clk_rate <= CSR_F_300M))
   priv->clk_csr = STMMAC_CSR_250_300M;
  else if ((clk_rate >= CSR_F_300M) && (clk_rate < CSR_F_500M))
   priv->clk_csr = STMMAC_CSR_300_500M;
  else if ((clk_rate >= CSR_F_500M) && (clk_rate < CSR_F_800M))
   priv->clk_csr = STMMAC_CSR_500_800M;
 }

 if (priv->plat->flags & STMMAC_FLAG_HAS_SUN8I) {
  if (clk_rate > 160000000)
   priv->clk_csr = 0x03;
  else if (clk_rate > 80000000)
   priv->clk_csr = 0x02;
  else if (clk_rate > 40000000)
   priv->clk_csr = 0x01;
  else
   priv->clk_csr = 0;
 }

 if (priv->plat->has_xgmac) {
  if (clk_rate > 400000000)
   priv->clk_csr = 0x5;
  else if (clk_rate > 350000000)
   priv->clk_csr = 0x4;
  else if (clk_rate > 300000000)
   priv->clk_csr = 0x3;
  else if (clk_rate > 250000000)
   priv->clk_csr = 0x2;
  else if (clk_rate > 150000000)
   priv->clk_csr = 0x1;
  else
   priv->clk_csr = 0x0;
 }
}

static void print_pkt(unsigned char *buf, int len)
{
 pr_debug("len = %d byte, buf addr: 0x%p\n", len, buf);
 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len);
}

static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue)
{
 struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
 u32 avail;

 if (tx_q->dirty_tx > tx_q->cur_tx)
  avail = tx_q->dirty_tx - tx_q->cur_tx - 1;
 else
  avail = priv->dma_conf.dma_tx_size - tx_q->cur_tx + tx_q->dirty_tx - 1;

 return avail;
}

/**
 * stmmac_rx_dirty - Get RX queue dirty
 * @priv: driver private structure
 * @queue: RX queue index
 */

static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue)
{
 struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
 u32 dirty;

 if (rx_q->dirty_rx <= rx_q->cur_rx)
  dirty = rx_q->cur_rx - rx_q->dirty_rx;
 else
  dirty = priv->dma_conf.dma_rx_size - rx_q->dirty_rx + rx_q->cur_rx;

 return dirty;
}

static bool stmmac_eee_tx_busy(struct stmmac_priv *priv)
{
 u32 tx_cnt = priv->plat->tx_queues_to_use;
 u32 queue;

 /* check if all TX queues have the work finished */
 for (queue = 0; queue < tx_cnt; queue++) {
  struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];

  if (tx_q->dirty_tx != tx_q->cur_tx)
   return true/* still unfinished work */
 }

 return false;
}

static void stmmac_restart_sw_lpi_timer(struct stmmac_priv *priv)
{
 mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_T(priv->tx_lpi_timer));
}

/**
 * stmmac_try_to_start_sw_lpi - check and enter in LPI mode
 * @priv: driver private structure
 * Description: this function is to verify and enter in LPI mode in case of
 * EEE.
 */

static void stmmac_try_to_start_sw_lpi(struct stmmac_priv *priv)
{
 if (stmmac_eee_tx_busy(priv)) {
  stmmac_restart_sw_lpi_timer(priv);
  return;
 }

 /* Check and enter in LPI mode */
 if (!priv->tx_path_in_lpi_mode)
  stmmac_set_lpi_mode(priv, priv->hw, STMMAC_LPI_FORCED,
        priv->tx_lpi_clk_stop, 0);
}

/**
 * stmmac_stop_sw_lpi - stop transmitting LPI
 * @priv: driver private structure
 * Description: When using software-controlled LPI, stop transmitting LPI state.
 */

static void stmmac_stop_sw_lpi(struct stmmac_priv *priv)
{
 timer_delete_sync(&priv->eee_ctrl_timer);
 stmmac_set_lpi_mode(priv, priv->hw, STMMAC_LPI_DISABLE, false, 0);
 priv->tx_path_in_lpi_mode = false;
}

/**
 * stmmac_eee_ctrl_timer - EEE TX SW timer.
 * @t:  timer_list struct containing private info
 * Description:
 *  if there is no data transfer and if we are not in LPI state,
 *  then MAC Transmitter can be moved to LPI state.
 */

static void stmmac_eee_ctrl_timer(struct timer_list *t)
{
 struct stmmac_priv *priv = timer_container_of(priv, t, eee_ctrl_timer);

 stmmac_try_to_start_sw_lpi(priv);
}

/* stmmac_get_tx_hwtstamp - get HW TX timestamps
 * @priv: driver private structure
 * @p : descriptor pointer
 * @skb : the socket buffer
 * Description :
 * This function will read timestamp from the descriptor & pass it to stack.
 * and also perform some sanity checks.
 */

static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv,
       struct dma_desc *p, struct sk_buff *skb)
{
 struct skb_shared_hwtstamps shhwtstamp;
 bool found = false;
 u64 ns = 0;

 if (!priv->hwts_tx_en)
  return;

 /* exit if skb doesn't support hw tstamp */
 if (likely(!skb || !(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)))
  return;

 /* check tx tstamp status */
 if (stmmac_get_tx_timestamp_status(priv, p)) {
  stmmac_get_timestamp(priv, p, priv->adv_ts, &ns);
  found = true;
 } else if (!stmmac_get_mac_tx_timestamp(priv, priv->hw, &ns)) {
  found = true;
 }

 if (found) {
  ns -= priv->plat->cdc_error_adj;

  memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
  shhwtstamp.hwtstamp = ns_to_ktime(ns);

  netdev_dbg(priv->dev, "get valid TX hw timestamp %llu\n", ns);
  /* pass tstamp to stack */
  skb_tstamp_tx(skb, &shhwtstamp);
 }
}

/* stmmac_get_rx_hwtstamp - get HW RX timestamps
 * @priv: driver private structure
 * @p : descriptor pointer
 * @np : next descriptor pointer
 * @skb : the socket buffer
 * Description :
 * This function will read received packet's timestamp from the descriptor
 * and pass it to stack. It also perform some sanity checks.
 */

static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p,
       struct dma_desc *np, struct sk_buff *skb)
{
 struct skb_shared_hwtstamps *shhwtstamp = NULL;
 struct dma_desc *desc = p;
 u64 ns = 0;

 if (!priv->hwts_rx_en)
  return;
 /* For GMAC4, the valid timestamp is from CTX next desc. */
 if (priv->plat->has_gmac4 || priv->plat->has_xgmac)
  desc = np;

 /* Check if timestamp is available */
 if (stmmac_get_rx_timestamp_status(priv, p, np, priv->adv_ts)) {
  stmmac_get_timestamp(priv, desc, priv->adv_ts, &ns);

  ns -= priv->plat->cdc_error_adj;

  netdev_dbg(priv->dev, "get valid RX hw timestamp %llu\n", ns);
  shhwtstamp = skb_hwtstamps(skb);
  memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
  shhwtstamp->hwtstamp = ns_to_ktime(ns);
 } else  {
  netdev_dbg(priv->dev, "cannot get RX hw timestamp\n");
 }
}

/**
 *  stmmac_hwtstamp_set - control hardware timestamping.
 *  @dev: device pointer.
 *  @config: the timestamping configuration.
 *  @extack: netlink extended ack structure for error reporting.
 *  Description:
 *  This function configures the MAC to enable/disable both outgoing(TX)
 *  and incoming(RX) packets time stamping based on user input.
 *  Return Value:
 *  0 on success and an appropriate -ve integer on failure.
 */

static int stmmac_hwtstamp_set(struct net_device *dev,
          struct kernel_hwtstamp_config *config,
          struct netlink_ext_ack *extack)
{
 struct stmmac_priv *priv = netdev_priv(dev);
 u32 ptp_v2 = 0;
 u32 tstamp_all = 0;
 u32 ptp_over_ipv4_udp = 0;
 u32 ptp_over_ipv6_udp = 0;
 u32 ptp_over_ethernet = 0;
 u32 snap_type_sel = 0;
 u32 ts_master_en = 0;
 u32 ts_event_en = 0;

 if (!(priv->dma_cap.time_stamp || priv->adv_ts)) {
  NL_SET_ERR_MSG_MOD(extack, "No support for HW time stamping");
  priv->hwts_tx_en = 0;
  priv->hwts_rx_en = 0;

  return -EOPNOTSUPP;
 }

 if (!netif_running(dev)) {
  NL_SET_ERR_MSG_MOD(extack,
       "Cannot change timestamping configuration while down");
  return -ENODEV;
 }

 netdev_dbg(priv->dev, "%s config flags:0x%x, tx_type:0x%x, rx_filter:0x%x\n",
     __func__, config->flags, config->tx_type, config->rx_filter);

 if (config->tx_type != HWTSTAMP_TX_OFF &&
     config->tx_type != HWTSTAMP_TX_ON)
  return -ERANGE;

 if (priv->adv_ts) {
  switch (config->rx_filter) {
  case HWTSTAMP_FILTER_NONE:
   /* time stamp no incoming packet at all */
   config->rx_filter = HWTSTAMP_FILTER_NONE;
   break;

  case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
   /* PTP v1, UDP, any kind of event packet */
   config->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
   /* 'xmac' hardware can support Sync, Pdelay_Req and
 * Pdelay_resp by setting bit14 and bits17/16 to 01
 * This leaves Delay_Req timestamps out.
 * Enable all events *and* general purpose message
 * timestamping
 */

   snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
   ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
   ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
   break;

  case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
   /* PTP v1, UDP, Sync packet */
   config->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC;
   /* take time stamp for SYNC messages only */
   ts_event_en = PTP_TCR_TSEVNTENA;

   ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
   ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
   break;

  case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
   /* PTP v1, UDP, Delay_req packet */
   config->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ;
   /* take time stamp for Delay_Req messages only */
   ts_master_en = PTP_TCR_TSMSTRENA;
   ts_event_en = PTP_TCR_TSEVNTENA;

   ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
   ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
   break;

  case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
   /* PTP v2, UDP, any kind of event packet */
   config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
   ptp_v2 = PTP_TCR_TSVER2ENA;
   /* take time stamp for all event messages */
   snap_type_sel = PTP_TCR_SNAPTYPSEL_1;

   ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
   ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
   break;

  case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
   /* PTP v2, UDP, Sync packet */
   config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_SYNC;
   ptp_v2 = PTP_TCR_TSVER2ENA;
   /* take time stamp for SYNC messages only */
   ts_event_en = PTP_TCR_TSEVNTENA;

   ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
   ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
   break;

  case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
   /* PTP v2, UDP, Delay_req packet */
   config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ;
   ptp_v2 = PTP_TCR_TSVER2ENA;
   /* take time stamp for Delay_Req messages only */
   ts_master_en = PTP_TCR_TSMSTRENA;
   ts_event_en = PTP_TCR_TSEVNTENA;

   ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
   ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
   break;

  case HWTSTAMP_FILTER_PTP_V2_EVENT:
   /* PTP v2/802.AS1 any layer, any kind of event packet */
   config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
   ptp_v2 = PTP_TCR_TSVER2ENA;
   snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
   if (priv->synopsys_id < DWMAC_CORE_4_10)
    ts_event_en = PTP_TCR_TSEVNTENA;
   ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
   ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
   ptp_over_ethernet = PTP_TCR_TSIPENA;
   break;

  case HWTSTAMP_FILTER_PTP_V2_SYNC:
   /* PTP v2/802.AS1, any layer, Sync packet */
   config->rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC;
   ptp_v2 = PTP_TCR_TSVER2ENA;
   /* take time stamp for SYNC messages only */
   ts_event_en = PTP_TCR_TSEVNTENA;

   ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
   ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
   ptp_over_ethernet = PTP_TCR_TSIPENA;
   break;

  case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
   /* PTP v2/802.AS1, any layer, Delay_req packet */
   config->rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ;
   ptp_v2 = PTP_TCR_TSVER2ENA;
   /* take time stamp for Delay_Req messages only */
   ts_master_en = PTP_TCR_TSMSTRENA;
   ts_event_en = PTP_TCR_TSEVNTENA;

   ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
   ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
   ptp_over_ethernet = PTP_TCR_TSIPENA;
   break;

  case HWTSTAMP_FILTER_NTP_ALL:
  case HWTSTAMP_FILTER_ALL:
   /* time stamp any incoming packet */
   config->rx_filter = HWTSTAMP_FILTER_ALL;
   tstamp_all = PTP_TCR_TSENALL;
   break;

  default:
   return -ERANGE;
  }
 } else {
  switch (config->rx_filter) {
  case HWTSTAMP_FILTER_NONE:
   config->rx_filter = HWTSTAMP_FILTER_NONE;
   break;
  default:
   /* PTP v1, UDP, any kind of event packet */
   config->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
   break;
  }
 }
 priv->hwts_rx_en = config->rx_filter != HWTSTAMP_FILTER_NONE;
 priv->hwts_tx_en = config->tx_type == HWTSTAMP_TX_ON;

 priv->systime_flags = STMMAC_HWTS_ACTIVE;

 if (priv->hwts_tx_en || priv->hwts_rx_en) {
  priv->systime_flags |= tstamp_all | ptp_v2 |
           ptp_over_ethernet | ptp_over_ipv6_udp |
           ptp_over_ipv4_udp | ts_event_en |
           ts_master_en | snap_type_sel;
 }

 stmmac_config_hw_tstamping(priv, priv->ptpaddr, priv->systime_flags);

 priv->tstamp_config = *config;

 return 0;
}

/**
 *  stmmac_hwtstamp_get - read hardware timestamping.
 *  @dev: device pointer.
 *  @config: the timestamping configuration.
 *  Description:
 *  This function obtain the current hardware timestamping settings
 *  as requested.
 */

static int stmmac_hwtstamp_get(struct net_device *dev,
          struct kernel_hwtstamp_config *config)
{
 struct stmmac_priv *priv = netdev_priv(dev);

 if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp))
  return -EOPNOTSUPP;

 *config = priv->tstamp_config;

 return 0;
}

/**
 * stmmac_init_tstamp_counter - init hardware timestamping counter
 * @priv: driver private structure
 * @systime_flags: timestamping flags
 * Description:
 * Initialize hardware counter for packet timestamping.
 * This is valid as long as the interface is open and not suspended.
 * Will be rerun after resuming from suspend, case in which the timestamping
 * flags updated by stmmac_hwtstamp_set() also need to be restored.
 */

int stmmac_init_tstamp_counter(struct stmmac_priv *priv, u32 systime_flags)
{
 bool xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
 struct timespec64 now;
 u32 sec_inc = 0;
 u64 temp = 0;

 if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp))
  return -EOPNOTSUPP;

 if (!priv->plat->clk_ptp_rate) {
  netdev_err(priv->dev, "Invalid PTP clock rate");
  return -EINVAL;
 }

 stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags);
 priv->systime_flags = systime_flags;

 /* program Sub Second Increment reg */
 stmmac_config_sub_second_increment(priv, priv->ptpaddr,
        priv->plat->clk_ptp_rate,
        xmac, &sec_inc);
 temp = div_u64(1000000000ULL, sec_inc);

 /* Store sub second increment for later use */
 priv->sub_second_inc = sec_inc;

 /* calculate default added value:
 * formula is :
 * addend = (2^32)/freq_div_ratio;
 * where, freq_div_ratio = 1e9ns/sec_inc
 */

 temp = (u64)(temp << 32);
 priv->default_addend = div_u64(temp, priv->plat->clk_ptp_rate);
 stmmac_config_addend(priv, priv->ptpaddr, priv->default_addend);

 /* initialize system time */
 ktime_get_real_ts64(&now);

 /* lower 32 bits of tv_sec are safe until y2106 */
 stmmac_init_systime(priv, priv->ptpaddr, (u32)now.tv_sec, now.tv_nsec);

 return 0;
}
EXPORT_SYMBOL_GPL(stmmac_init_tstamp_counter);

/**
 * stmmac_init_ptp - init PTP
 * @priv: driver private structure
 * Description: this is to verify if the HW supports the PTPv1 or PTPv2.
 * This is done by looking at the HW cap. register.
 * This function also registers the ptp driver.
 */

static int stmmac_init_ptp(struct stmmac_priv *priv)
{
 bool xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
 int ret;

 if (priv->plat->ptp_clk_freq_config)
  priv->plat->ptp_clk_freq_config(priv);

 ret = stmmac_init_tstamp_counter(priv, STMMAC_HWTS_ACTIVE);
 if (ret)
  return ret;

 priv->adv_ts = 0;
 /* Check if adv_ts can be enabled for dwmac 4.x / xgmac core */
 if (xmac && priv->dma_cap.atime_stamp)
  priv->adv_ts = 1;
 /* Dwmac 3.x core with extend_desc can support adv_ts */
 else if (priv->extend_desc && priv->dma_cap.atime_stamp)
  priv->adv_ts = 1;

 if (priv->dma_cap.time_stamp)
  netdev_info(priv->dev, "IEEE 1588-2002 Timestamp supported\n");

 if (priv->adv_ts)
  netdev_info(priv->dev,
       "IEEE 1588-2008 Advanced Timestamp supported\n");

 priv->hwts_tx_en = 0;
 priv->hwts_rx_en = 0;

 if (priv->plat->flags & STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY)
  stmmac_hwtstamp_correct_latency(priv, priv);

 return 0;
}

static void stmmac_release_ptp(struct stmmac_priv *priv)
{
 clk_disable_unprepare(priv->plat->clk_ptp_ref);
 stmmac_ptp_unregister(priv);
}

/**
 *  stmmac_mac_flow_ctrl - Configure flow control in all queues
 *  @priv: driver private structure
 *  @duplex: duplex passed to the next function
 *  @flow_ctrl: desired flow control modes
 *  Description: It is used for configuring the flow control in all queues
 */

static void stmmac_mac_flow_ctrl(struct stmmac_priv *priv, u32 duplex,
     unsigned int flow_ctrl)
{
 u32 tx_cnt = priv->plat->tx_queues_to_use;

 stmmac_flow_ctrl(priv, priv->hw, duplex, flow_ctrl, priv->pause_time,
    tx_cnt);
}

static unsigned long stmmac_mac_get_caps(struct phylink_config *config,
      phy_interface_t interface)
{
 struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));

 /* Refresh the MAC-specific capabilities */
 stmmac_mac_update_caps(priv);

 config->mac_capabilities = priv->hw->link.caps;

 if (priv->plat->max_speed)
  phylink_limit_mac_speed(config, priv->plat->max_speed);

 return config->mac_capabilities;
}

static struct phylink_pcs *stmmac_mac_select_pcs(struct phylink_config *config,
       phy_interface_t interface)
{
 struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
 struct phylink_pcs *pcs;

 if (priv->plat->select_pcs) {
  pcs = priv->plat->select_pcs(priv, interface);
  if (!IS_ERR(pcs))
   return pcs;
 }

 return NULL;
}

static void stmmac_mac_config(struct phylink_config *config, unsigned int mode,
         const struct phylink_link_state *state)
{
 /* Nothing to do, xpcs_config() handles everything */
}

static void stmmac_mac_link_down(struct phylink_config *config,
     unsigned int mode, phy_interface_t interface)
{
 struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));

 stmmac_mac_set(priv, priv->ioaddr, false);
 if (priv->dma_cap.eee)
  stmmac_set_eee_pls(priv, priv->hw, false);

 if (stmmac_fpe_supported(priv))
  ethtool_mmsv_link_state_handle(&priv->fpe_cfg.mmsv, false);
}

static void stmmac_mac_link_up(struct phylink_config *config,
          struct phy_device *phy,
          unsigned int mode, phy_interface_t interface,
          int speed, int duplex,
          bool tx_pause, bool rx_pause)
{
 struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
 unsigned int flow_ctrl;
 u32 old_ctrl, ctrl;
 int ret;

 if ((priv->plat->flags & STMMAC_FLAG_SERDES_UP_AFTER_PHY_LINKUP) &&
     priv->plat->serdes_powerup)
  priv->plat->serdes_powerup(priv->dev, priv->plat->bsp_priv);

 old_ctrl = readl(priv->ioaddr + MAC_CTRL_REG);
 ctrl = old_ctrl & ~priv->hw->link.speed_mask;

 if (interface == PHY_INTERFACE_MODE_USXGMII) {
  switch (speed) {
  case SPEED_10000:
   ctrl |= priv->hw->link.xgmii.speed10000;
   break;
  case SPEED_5000:
   ctrl |= priv->hw->link.xgmii.speed5000;
   break;
  case SPEED_2500:
   ctrl |= priv->hw->link.xgmii.speed2500;
   break;
  default:
   return;
  }
 } else if (interface == PHY_INTERFACE_MODE_XLGMII) {
  switch (speed) {
  case SPEED_100000:
   ctrl |= priv->hw->link.xlgmii.speed100000;
   break;
  case SPEED_50000:
   ctrl |= priv->hw->link.xlgmii.speed50000;
   break;
  case SPEED_40000:
   ctrl |= priv->hw->link.xlgmii.speed40000;
   break;
  case SPEED_25000:
   ctrl |= priv->hw->link.xlgmii.speed25000;
   break;
  case SPEED_10000:
   ctrl |= priv->hw->link.xgmii.speed10000;
   break;
  case SPEED_2500:
   ctrl |= priv->hw->link.speed2500;
   break;
  case SPEED_1000:
   ctrl |= priv->hw->link.speed1000;
   break;
  default:
   return;
  }
 } else {
  switch (speed) {
  case SPEED_2500:
   ctrl |= priv->hw->link.speed2500;
   break;
  case SPEED_1000:
   ctrl |= priv->hw->link.speed1000;
   break;
  case SPEED_100:
   ctrl |= priv->hw->link.speed100;
   break;
  case SPEED_10:
   ctrl |= priv->hw->link.speed10;
   break;
  default:
   return;
  }
 }

 if (priv->plat->fix_mac_speed)
  priv->plat->fix_mac_speed(priv->plat->bsp_priv, speed, mode);

 if (!duplex)
  ctrl &= ~priv->hw->link.duplex;
 else
  ctrl |= priv->hw->link.duplex;

 /* Flow Control operation */
 if (rx_pause && tx_pause)
  flow_ctrl = FLOW_AUTO;
 else if (rx_pause && !tx_pause)
  flow_ctrl = FLOW_RX;
 else if (!rx_pause && tx_pause)
  flow_ctrl = FLOW_TX;
 else
  flow_ctrl = FLOW_OFF;

 stmmac_mac_flow_ctrl(priv, duplex, flow_ctrl);

 if (ctrl != old_ctrl)
  writel(ctrl, priv->ioaddr + MAC_CTRL_REG);

 if (priv->plat->set_clk_tx_rate) {
  ret = priv->plat->set_clk_tx_rate(priv->plat->bsp_priv,
      priv->plat->clk_tx_i,
      interface, speed);
  if (ret < 0)
   netdev_err(priv->dev,
       "failed to configure %s transmit clock for %dMbps: %pe\n",
       phy_modes(interface), speed, ERR_PTR(ret));
 }

 stmmac_mac_set(priv, priv->ioaddr, true);
 if (priv->dma_cap.eee)
  stmmac_set_eee_pls(priv, priv->hw, true);

 if (stmmac_fpe_supported(priv))
  ethtool_mmsv_link_state_handle(&priv->fpe_cfg.mmsv, true);

 if (priv->plat->flags & STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY)
  stmmac_hwtstamp_correct_latency(priv, priv);
}

static void stmmac_mac_disable_tx_lpi(struct phylink_config *config)
{
 struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));

 priv->eee_active = false;

 mutex_lock(&priv->lock);

 priv->eee_enabled = false;

 netdev_dbg(priv->dev, "disable EEE\n");
 priv->eee_sw_timer_en = false;
 timer_delete_sync(&priv->eee_ctrl_timer);
 stmmac_set_lpi_mode(priv, priv->hw, STMMAC_LPI_DISABLE, false, 0);
 priv->tx_path_in_lpi_mode = false;

 stmmac_set_eee_timer(priv, priv->hw, 0, STMMAC_DEFAULT_TWT_LS);
 mutex_unlock(&priv->lock);
}

static int stmmac_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
        bool tx_clk_stop)
{
 struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
 int ret;

 priv->tx_lpi_timer = timer;
 priv->eee_active = true;

 mutex_lock(&priv->lock);

 priv->eee_enabled = true;

 /* Update the transmit clock stop according to PHY capability if
 * the platform allows
 */

 if (priv->plat->flags & STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP)
  priv->tx_lpi_clk_stop = tx_clk_stop;

 stmmac_set_eee_timer(priv, priv->hw, STMMAC_DEFAULT_LIT_LS,
        STMMAC_DEFAULT_TWT_LS);

 /* Try to cnfigure the hardware timer. */
 ret = stmmac_set_lpi_mode(priv, priv->hw, STMMAC_LPI_TIMER,
      priv->tx_lpi_clk_stop, priv->tx_lpi_timer);

 if (ret) {
  /* Hardware timer mode not supported, or value out of range.
 * Fall back to using software LPI mode
 */

  priv->eee_sw_timer_en = true;
  stmmac_restart_sw_lpi_timer(priv);
 }

 mutex_unlock(&priv->lock);
 netdev_dbg(priv->dev, "Energy-Efficient Ethernet initialized\n");

 return 0;
}

static int stmmac_mac_finish(struct phylink_config *config, unsigned int mode,
        phy_interface_t interface)
{
 struct net_device *ndev = to_net_dev(config->dev);
 struct stmmac_priv *priv = netdev_priv(ndev);

 if (priv->plat->mac_finish)
  priv->plat->mac_finish(ndev, priv->plat->bsp_priv, mode, interface);

 return 0;
}

static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
 .mac_get_caps = stmmac_mac_get_caps,
 .mac_select_pcs = stmmac_mac_select_pcs,
 .mac_config = stmmac_mac_config,
 .mac_link_down = stmmac_mac_link_down,
 .mac_link_up = stmmac_mac_link_up,
 .mac_disable_tx_lpi = stmmac_mac_disable_tx_lpi,
 .mac_enable_tx_lpi = stmmac_mac_enable_tx_lpi,
 .mac_finish = stmmac_mac_finish,
};

/**
 * stmmac_check_pcs_mode - verify if RGMII/SGMII is supported
 * @priv: driver private structure
 * Description: this is to verify if the HW supports the PCS.
 * Physical Coding Sublayer (PCS) interface that can be used when the MAC is
 * configured for the TBI, RTBI, or SGMII PHY interface.
 */

static void stmmac_check_pcs_mode(struct stmmac_priv *priv)
{
 int interface = priv->plat->mac_interface;

 if (priv->dma_cap.pcs) {
  if ((interface == PHY_INTERFACE_MODE_RGMII) ||
      (interface == PHY_INTERFACE_MODE_RGMII_ID) ||
      (interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
      (interface == PHY_INTERFACE_MODE_RGMII_TXID)) {
   netdev_dbg(priv->dev, "PCS RGMII support enabled\n");
   priv->hw->pcs = STMMAC_PCS_RGMII;
  } else if (interface == PHY_INTERFACE_MODE_SGMII) {
   netdev_dbg(priv->dev, "PCS SGMII support enabled\n");
   priv->hw->pcs = STMMAC_PCS_SGMII;
  }
 }
}

/**
 * stmmac_init_phy - PHY initialization
 * @dev: net device structure
 * Description: it initializes the driver's PHY state, and attaches the PHY
 * to the mac driver.
 *  Return value:
 *  0 on success
 */

static int stmmac_init_phy(struct net_device *dev)
{
 struct stmmac_priv *priv = netdev_priv(dev);
 struct fwnode_handle *phy_fwnode;
 struct fwnode_handle *fwnode;
 int ret;

 if (!phylink_expects_phy(priv->phylink))
  return 0;

 fwnode = priv->plat->port_node;
 if (!fwnode)
  fwnode = dev_fwnode(priv->device);

 if (fwnode)
  phy_fwnode = fwnode_get_phy_node(fwnode);
 else
  phy_fwnode = NULL;

 /* Some DT bindings do not set-up the PHY handle. Let's try to
 * manually parse it
 */

 if (!phy_fwnode || IS_ERR(phy_fwnode)) {
  int addr = priv->plat->phy_addr;
  struct phy_device *phydev;

  if (addr < 0) {
   netdev_err(priv->dev, "no phy found\n");
   return -ENODEV;
  }

  phydev = mdiobus_get_phy(priv->mii, addr);
  if (!phydev) {
   netdev_err(priv->dev, "no phy at addr %d\n", addr);
   return -ENODEV;
  }

  ret = phylink_connect_phy(priv->phylink, phydev);
 } else {
  fwnode_handle_put(phy_fwnode);
  ret = phylink_fwnode_phy_connect(priv->phylink, fwnode, 0);
 }

 if (ret == 0) {
  struct ethtool_keee eee;

  /* Configure phylib's copy of the LPI timer. Normally,
 * phylink_config.lpi_timer_default would do this, but there is
 * a chance that userspace could change the eee_timer setting
 * via sysfs before the first open. Thus, preserve existing
 * behaviour.
 */

  if (!phylink_ethtool_get_eee(priv->phylink, &eee)) {
   eee.tx_lpi_timer = priv->tx_lpi_timer;
   phylink_ethtool_set_eee(priv->phylink, &eee);
  }
 }

 if (!priv->plat->pmt) {
  struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };

  phylink_ethtool_get_wol(priv->phylink, &wol);
  device_set_wakeup_capable(priv->device, !!wol.supported);
  device_set_wakeup_enable(priv->device, !!wol.wolopts);
 }

 return ret;
}

static int stmmac_phy_setup(struct stmmac_priv *priv)
{
 struct stmmac_mdio_bus_data *mdio_bus_data;
 struct phylink_config *config;
 struct fwnode_handle *fwnode;
 struct phylink_pcs *pcs;
 struct phylink *phylink;

 config = &priv->phylink_config;

 config->dev = &priv->dev->dev;
 config->type = PHYLINK_NETDEV;
 config->mac_managed_pm = true;

 /* Stmmac always requires an RX clock for hardware initialization */
 config->mac_requires_rxc = true;

 if (!(priv->plat->flags & STMMAC_FLAG_RX_CLK_RUNS_IN_LPI))
  config->eee_rx_clk_stop_enable = true;

 /* Set the default transmit clock stop bit based on the platform glue */
 priv->tx_lpi_clk_stop = priv->plat->flags &
    STMMAC_FLAG_EN_TX_LPI_CLOCKGATING;

 mdio_bus_data = priv->plat->mdio_bus_data;
 if (mdio_bus_data)
  config->default_an_inband = mdio_bus_data->default_an_inband;

 /* Get the PHY interface modes (at the PHY end of the link) that
 * are supported by the platform.
 */

 if (priv->plat->get_interfaces)
  priv->plat->get_interfaces(priv, priv->plat->bsp_priv,
        config->supported_interfaces);

 /* Set the platform/firmware specified interface mode if the
 * supported interfaces have not already been provided using
 * phy_interface as a last resort.
 */

 if (phy_interface_empty(config->supported_interfaces))
  __set_bit(priv->plat->phy_interface,
     config->supported_interfaces);

 /* If we have an xpcs, it defines which PHY interfaces are supported. */
 if (priv->hw->xpcs)
  pcs = xpcs_to_phylink_pcs(priv->hw->xpcs);
 else
  pcs = priv->hw->phylink_pcs;

 if (pcs)
  phy_interface_or(config->supported_interfaces,
     config->supported_interfaces,
     pcs->supported_interfaces);

 if (priv->dma_cap.eee) {
  /* Assume all supported interfaces also support LPI */
  memcpy(config->lpi_interfaces, config->supported_interfaces,
         sizeof(config->lpi_interfaces));

  /* All full duplex speeds above 100Mbps are supported */
  config->lpi_capabilities = ~(MAC_1000FD - 1) | MAC_100FD;
  config->lpi_timer_default = eee_timer * 1000;
  config->eee_enabled_default = true;
 }

 fwnode = priv->plat->port_node;
 if (!fwnode)
  fwnode = dev_fwnode(priv->device);

 phylink = phylink_create(config, fwnode, priv->plat->phy_interface,
     &stmmac_phylink_mac_ops);
 if (IS_ERR(phylink))
  return PTR_ERR(phylink);

 priv->phylink = phylink;
 return 0;
}

static void stmmac_display_rx_rings(struct stmmac_priv *priv,
        struct stmmac_dma_conf *dma_conf)
{
 u32 rx_cnt = priv->plat->rx_queues_to_use;
 unsigned int desc_size;
 void *head_rx;
 u32 queue;

 /* Display RX rings */
 for (queue = 0; queue < rx_cnt; queue++) {
  struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];

  pr_info("\tRX Queue %u rings\n", queue);

  if (priv->extend_desc) {
   head_rx = (void *)rx_q->dma_erx;
   desc_size = sizeof(struct dma_extended_desc);
  } else {
   head_rx = (void *)rx_q->dma_rx;
   desc_size = sizeof(struct dma_desc);
  }

  /* Display RX ring */
  stmmac_display_ring(priv, head_rx, dma_conf->dma_rx_size, true,
        rx_q->dma_rx_phy, desc_size);
 }
}

static void stmmac_display_tx_rings(struct stmmac_priv *priv,
        struct stmmac_dma_conf *dma_conf)
{
 u32 tx_cnt = priv->plat->tx_queues_to_use;
 unsigned int desc_size;
 void *head_tx;
 u32 queue;

 /* Display TX rings */
 for (queue = 0; queue < tx_cnt; queue++) {
  struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];

  pr_info("\tTX Queue %d rings\n", queue);

  if (priv->extend_desc) {
   head_tx = (void *)tx_q->dma_etx;
   desc_size = sizeof(struct dma_extended_desc);
  } else if (tx_q->tbs & STMMAC_TBS_AVAIL) {
   head_tx = (void *)tx_q->dma_entx;
   desc_size = sizeof(struct dma_edesc);
  } else {
   head_tx = (void *)tx_q->dma_tx;
   desc_size = sizeof(struct dma_desc);
  }

  stmmac_display_ring(priv, head_tx, dma_conf->dma_tx_size, false,
        tx_q->dma_tx_phy, desc_size);
 }
}

static void stmmac_display_rings(struct stmmac_priv *priv,
     struct stmmac_dma_conf *dma_conf)
{
 /* Display RX ring */
 stmmac_display_rx_rings(priv, dma_conf);

 /* Display TX ring */
 stmmac_display_tx_rings(priv, dma_conf);
}

static unsigned int stmmac_rx_offset(struct stmmac_priv *priv)
{
 if (stmmac_xdp_is_enabled(priv))
  return XDP_PACKET_HEADROOM;

 return NET_SKB_PAD;
}

static int stmmac_set_bfsize(int mtu, int bufsize)
{
 int ret = bufsize;

 if (mtu >= BUF_SIZE_8KiB)
  ret = BUF_SIZE_16KiB;
 else if (mtu >= BUF_SIZE_4KiB)
  ret = BUF_SIZE_8KiB;
 else if (mtu >= BUF_SIZE_2KiB)
  ret = BUF_SIZE_4KiB;
 else if (mtu > DEFAULT_BUFSIZE)
  ret = BUF_SIZE_2KiB;
 else
  ret = DEFAULT_BUFSIZE;

 return ret;
}

/**
 * stmmac_clear_rx_descriptors - clear RX descriptors
 * @priv: driver private structure
 * @dma_conf: structure to take the dma data
 * @queue: RX queue index
 * Description: this function is called to clear the RX descriptors
 * in case of both basic and extended descriptors are used.
 */

static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv,
     struct stmmac_dma_conf *dma_conf,
     u32 queue)
{
 struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
 int i;

 /* Clear the RX descriptors */
 for (i = 0; i < dma_conf->dma_rx_size; i++)
  if (priv->extend_desc)
   stmmac_init_rx_desc(priv, &rx_q->dma_erx[i].basic,
     priv->use_riwt, priv->mode,
     (i == dma_conf->dma_rx_size - 1),
     dma_conf->dma_buf_sz);
  else
   stmmac_init_rx_desc(priv, &rx_q->dma_rx[i],
     priv->use_riwt, priv->mode,
     (i == dma_conf->dma_rx_size - 1),
     dma_conf->dma_buf_sz);
}

/**
 * stmmac_clear_tx_descriptors - clear tx descriptors
 * @priv: driver private structure
 * @dma_conf: structure to take the dma data
 * @queue: TX queue index.
 * Description: this function is called to clear the TX descriptors
 * in case of both basic and extended descriptors are used.
 */

static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv,
     struct stmmac_dma_conf *dma_conf,
     u32 queue)
{
 struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
 int i;

 /* Clear the TX descriptors */
 for (i = 0; i < dma_conf->dma_tx_size; i++) {
  int last = (i == (dma_conf->dma_tx_size - 1));
  struct dma_desc *p;

  if (priv->extend_desc)
   p = &tx_q->dma_etx[i].basic;
  else if (tx_q->tbs & STMMAC_TBS_AVAIL)
   p = &tx_q->dma_entx[i].basic;
  else
   p = &tx_q->dma_tx[i];

  stmmac_init_tx_desc(priv, p, priv->mode, last);
 }
}

/**
 * stmmac_clear_descriptors - clear descriptors
 * @priv: driver private structure
 * @dma_conf: structure to take the dma data
 * Description: this function is called to clear the TX and RX descriptors
 * in case of both basic and extended descriptors are used.
 */

static void stmmac_clear_descriptors(struct stmmac_priv *priv,
         struct stmmac_dma_conf *dma_conf)
{
 u32 rx_queue_cnt = priv->plat->rx_queues_to_use;
 u32 tx_queue_cnt = priv->plat->tx_queues_to_use;
 u32 queue;

 /* Clear the RX descriptors */
 for (queue = 0; queue < rx_queue_cnt; queue++)
  stmmac_clear_rx_descriptors(priv, dma_conf, queue);

 /* Clear the TX descriptors */
 for (queue = 0; queue < tx_queue_cnt; queue++)
  stmmac_clear_tx_descriptors(priv, dma_conf, queue);
}

/**
 * stmmac_init_rx_buffers - init the RX descriptor buffer.
 * @priv: driver private structure
 * @dma_conf: structure to take the dma data
 * @p: descriptor pointer
 * @i: descriptor index
 * @flags: gfp flag
 * @queue: RX queue index
 * Description: this function is called to allocate a receive buffer, perform
 * the DMA mapping and init the descriptor.
 */

static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
      struct stmmac_dma_conf *dma_conf,
      struct dma_desc *p,
      int i, gfp_t flags, u32 queue)
{
 struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
 struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
 gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);

 if (priv->dma_cap.host_dma_width <= 32)
  gfp |= GFP_DMA32;

 if (!buf->page) {
  buf->page = page_pool_alloc_pages(rx_q->page_pool, gfp);
  if (!buf->page)
   return -ENOMEM;
  buf->page_offset = stmmac_rx_offset(priv);
 }

 if (priv->sph && !buf->sec_page) {
  buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
  if (!buf->sec_page)
   return -ENOMEM;

  buf->sec_addr = page_pool_get_dma_addr(buf->sec_page);
  stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true);
 } else {
  buf->sec_page = NULL;
  stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, false);
 }

 buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;

 stmmac_set_desc_addr(priv, p, buf->addr);
 if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
  stmmac_init_desc3(priv, p);

 return 0;
}

/**
 * stmmac_free_rx_buffer - free RX dma buffers
 * @priv: private structure
 * @rx_q: RX queue
 * @i: buffer index.
 */

static void stmmac_free_rx_buffer(struct stmmac_priv *priv,
      struct stmmac_rx_queue *rx_q,
      int i)
{
 struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];

 if (buf->page)
  page_pool_put_full_page(rx_q->page_pool, buf->page, false);
 buf->page = NULL;

 if (buf->sec_page)
  page_pool_put_full_page(rx_q->page_pool, buf->sec_page, false);
 buf->sec_page = NULL;
}

/**
 * stmmac_free_tx_buffer - free RX dma buffers
 * @priv: private structure
 * @dma_conf: structure to take the dma data
 * @queue: RX queue index
 * @i: buffer index.
 */

static void stmmac_free_tx_buffer(struct stmmac_priv *priv,
      struct stmmac_dma_conf *dma_conf,
      u32 queue, int i)
{
 struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];

 if (tx_q->tx_skbuff_dma[i].buf &&
     tx_q->tx_skbuff_dma[i].buf_type != STMMAC_TXBUF_T_XDP_TX) {
  if (tx_q->tx_skbuff_dma[i].map_as_page)
   dma_unmap_page(priv->device,
           tx_q->tx_skbuff_dma[i].buf,
           tx_q->tx_skbuff_dma[i].len,
           DMA_TO_DEVICE);
  else
   dma_unmap_single(priv->device,
      tx_q->tx_skbuff_dma[i].buf,
      tx_q->tx_skbuff_dma[i].len,
      DMA_TO_DEVICE);
 }

 if (tx_q->xdpf[i] &&
     (tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_XDP_TX ||
      tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_XDP_NDO)) {
  xdp_return_frame(tx_q->xdpf[i]);
  tx_q->xdpf[i] = NULL;
 }

 if (tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_XSK_TX)
  tx_q->xsk_frames_done++;

 if (tx_q->tx_skbuff[i] &&
     tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_SKB) {
  dev_kfree_skb_any(tx_q->tx_skbuff[i]);
  tx_q->tx_skbuff[i] = NULL;
 }

 tx_q->tx_skbuff_dma[i].buf = 0;
 tx_q->tx_skbuff_dma[i].map_as_page = false;
}

/**
 * dma_free_rx_skbufs - free RX dma buffers
 * @priv: private structure
 * @dma_conf: structure to take the dma data
 * @queue: RX queue index
 */

static void dma_free_rx_skbufs(struct stmmac_priv *priv,
          struct stmmac_dma_conf *dma_conf,
          u32 queue)
{
 struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
 int i;

 for (i = 0; i < dma_conf->dma_rx_size; i++)
  stmmac_free_rx_buffer(priv, rx_q, i);
}

static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv,
       struct stmmac_dma_conf *dma_conf,
       u32 queue, gfp_t flags)
{
 struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
 int i;

 for (i = 0; i < dma_conf->dma_rx_size; i++) {
  struct dma_desc *p;
  int ret;

  if (priv->extend_desc)
   p = &((rx_q->dma_erx + i)->basic);
  else
   p = rx_q->dma_rx + i;

  ret = stmmac_init_rx_buffers(priv, dma_conf, p, i, flags,
          queue);
  if (ret)
   return ret;

  rx_q->buf_alloc_num++;
 }

 return 0;
}

/**
 * dma_free_rx_xskbufs - free RX dma buffers from XSK pool
 * @priv: private structure
 * @dma_conf: structure to take the dma data
 * @queue: RX queue index
 */

static void dma_free_rx_xskbufs(struct stmmac_priv *priv,
    struct stmmac_dma_conf *dma_conf,
    u32 queue)
{
 struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
 int i;

 for (i = 0; i < dma_conf->dma_rx_size; i++) {
  struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];

  if (!buf->xdp)
   continue;

  xsk_buff_free(buf->xdp);
  buf->xdp = NULL;
 }
}

static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv,
          struct stmmac_dma_conf *dma_conf,
          u32 queue)
{
 struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
 int i;

 /* struct stmmac_xdp_buff is using cb field (maximum size of 24 bytes)
 * in struct xdp_buff_xsk to stash driver specific information. Thus,
 * use this macro to make sure no size violations.
 */

 XSK_CHECK_PRIV_TYPE(struct stmmac_xdp_buff);

 for (i = 0; i < dma_conf->dma_rx_size; i++) {
  struct stmmac_rx_buffer *buf;
  dma_addr_t dma_addr;
  struct dma_desc *p;

  if (priv->extend_desc)
   p = (struct dma_desc *)(rx_q->dma_erx + i);
  else
   p = rx_q->dma_rx + i;

  buf = &rx_q->buf_pool[i];

  buf->xdp = xsk_buff_alloc(rx_q->xsk_pool);
  if (!buf->xdp)
   return -ENOMEM;

  dma_addr = xsk_buff_xdp_get_dma(buf->xdp);
  stmmac_set_desc_addr(priv, p, dma_addr);
  rx_q->buf_alloc_num++;
 }

 return 0;
}

static struct xsk_buff_pool *stmmac_get_xsk_pool(struct stmmac_priv *priv, u32 queue)
{
 if (!stmmac_xdp_is_enabled(priv) || !test_bit(queue, priv->af_xdp_zc_qps))
  return NULL;

 return xsk_get_pool_from_qid(priv->dev, queue);
}

/**
 * __init_dma_rx_desc_rings - init the RX descriptor ring (per queue)
 * @priv: driver private structure
 * @dma_conf: structure to take the dma data
 * @queue: RX queue index
 * @flags: gfp flag.
 * Description: this function initializes the DMA RX descriptors
 * and allocates the socket buffers. It supports the chained and ring
 * modes.
 */

static int __init_dma_rx_desc_rings(struct stmmac_priv *priv,
        struct stmmac_dma_conf *dma_conf,
        u32 queue, gfp_t flags)
{
 struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
 int ret;

 netif_dbg(priv, probe, priv->dev,
    "(%s) dma_rx_phy=0x%08x\n", __func__,
    (u32)rx_q->dma_rx_phy);

 stmmac_clear_rx_descriptors(priv, dma_conf, queue);

 xdp_rxq_info_unreg_mem_model(&rx_q->xdp_rxq);

 rx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue);

 if (rx_q->xsk_pool) {
  WARN_ON(xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq,
         MEM_TYPE_XSK_BUFF_POOL,
         NULL));
  netdev_info(priv->dev,
       "Register MEM_TYPE_XSK_BUFF_POOL RxQ-%d\n",
       rx_q->queue_index);
  xsk_pool_set_rxq_info(rx_q->xsk_pool, &rx_q->xdp_rxq);
 } else {
  WARN_ON(xdp_rxq_info_reg_mem_model(&rx_q->xdp_rxq,
         MEM_TYPE_PAGE_POOL,
         rx_q->page_pool));
  netdev_info(priv->dev,
       "Register MEM_TYPE_PAGE_POOL RxQ-%d\n",
       rx_q->queue_index);
 }

 if (rx_q->xsk_pool) {
  /* RX XDP ZC buffer pool may not be populated, e.g.
 * xdpsock TX-only.
 */

  stmmac_alloc_rx_buffers_zc(priv, dma_conf, queue);
 } else {
  ret = stmmac_alloc_rx_buffers(priv, dma_conf, queue, flags);
  if (ret < 0)
   return -ENOMEM;
 }

 /* Setup the chained descriptor addresses */
 if (priv->mode == STMMAC_CHAIN_MODE) {
  if (priv->extend_desc)
   stmmac_mode_init(priv, rx_q->dma_erx,
      rx_q->dma_rx_phy,
      dma_conf->dma_rx_size, 1);
  else
   stmmac_mode_init(priv, rx_q->dma_rx,
      rx_q->dma_rx_phy,
      dma_conf->dma_rx_size, 0);
 }

 return 0;
}

static int init_dma_rx_desc_rings(struct net_device *dev,
      struct stmmac_dma_conf *dma_conf,
      gfp_t flags)
{
 struct stmmac_priv *priv = netdev_priv(dev);
 u32 rx_count = priv->plat->rx_queues_to_use;
 int queue;
 int ret;

 /* RX INITIALIZATION */
 netif_dbg(priv, probe, priv->dev,
    "SKB addresses:\nskb\t\tskb data\tdma data\n");

 for (queue = 0; queue < rx_count; queue++) {
  ret = __init_dma_rx_desc_rings(priv, dma_conf, queue, flags);
  if (ret)
   goto err_init_rx_buffers;
 }

 return 0;

err_init_rx_buffers:
 while (queue >= 0) {
  struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];

  if (rx_q->xsk_pool)
   dma_free_rx_xskbufs(priv, dma_conf, queue);
  else
   dma_free_rx_skbufs(priv, dma_conf, queue);

  rx_q->buf_alloc_num = 0;
  rx_q->xsk_pool = NULL;

  queue--;
 }

 return ret;
}

/**
 * __init_dma_tx_desc_rings - init the TX descriptor ring (per queue)
 * @priv: driver private structure
 * @dma_conf: structure to take the dma data
 * @queue: TX queue index
 * Description: this function initializes the DMA TX descriptors
 * and allocates the socket buffers. It supports the chained and ring
 * modes.
 */

static int __init_dma_tx_desc_rings(struct stmmac_priv *priv,
        struct stmmac_dma_conf *dma_conf,
        u32 queue)
{
 struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
 int i;

 netif_dbg(priv, probe, priv->dev,
    "(%s) dma_tx_phy=0x%08x\n", __func__,
    (u32)tx_q->dma_tx_phy);

 /* Setup the chained descriptor addresses */
 if (priv->mode == STMMAC_CHAIN_MODE) {
  if (priv->extend_desc)
   stmmac_mode_init(priv, tx_q->dma_etx,
      tx_q->dma_tx_phy,
      dma_conf->dma_tx_size, 1);
  else if (!(tx_q->tbs & STMMAC_TBS_AVAIL))
   stmmac_mode_init(priv, tx_q->dma_tx,
      tx_q->dma_tx_phy,
      dma_conf->dma_tx_size, 0);
 }

 tx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue);

 for (i = 0; i < dma_conf->dma_tx_size; i++) {
  struct dma_desc *p;

  if (priv->extend_desc)
   p = &((tx_q->dma_etx + i)->basic);
  else if (tx_q->tbs & STMMAC_TBS_AVAIL)
   p = &((tx_q->dma_entx + i)->basic);
  else
   p = tx_q->dma_tx + i;

  stmmac_clear_desc(priv, p);

  tx_q->tx_skbuff_dma[i].buf = 0;
  tx_q->tx_skbuff_dma[i].map_as_page = false;
  tx_q->tx_skbuff_dma[i].len = 0;
  tx_q->tx_skbuff_dma[i].last_segment = false;
  tx_q->tx_skbuff[i] = NULL;
 }

 return 0;
}

static int init_dma_tx_desc_rings(struct net_device *dev,
      struct stmmac_dma_conf *dma_conf)
{
 struct stmmac_priv *priv = netdev_priv(dev);
 u32 tx_queue_cnt;
 u32 queue;

 tx_queue_cnt = priv->plat->tx_queues_to_use;

 for (queue = 0; queue < tx_queue_cnt; queue++)
  __init_dma_tx_desc_rings(priv, dma_conf, queue);

 return 0;
}

/**
 * init_dma_desc_rings - init the RX/TX descriptor rings
 * @dev: net device structure
 * @dma_conf: structure to take the dma data
 * @flags: gfp flag.
 * Description: this function initializes the DMA RX/TX descriptors
 * and allocates the socket buffers. It supports the chained and ring
 * modes.
 */

static int init_dma_desc_rings(struct net_device *dev,
          struct stmmac_dma_conf *dma_conf,
          gfp_t flags)
{
 struct stmmac_priv *priv = netdev_priv(dev);
 int ret;

 ret = init_dma_rx_desc_rings(dev, dma_conf, flags);
 if (ret)
  return ret;

 ret = init_dma_tx_desc_rings(dev, dma_conf);

 stmmac_clear_descriptors(priv, dma_conf);

 if (netif_msg_hw(priv))
  stmmac_display_rings(priv, dma_conf);

 return ret;
}

/**
 * dma_free_tx_skbufs - free TX dma buffers
 * @priv: private structure
 * @dma_conf: structure to take the dma data
 * @queue: TX queue index
 */

static void dma_free_tx_skbufs(struct stmmac_priv *priv,
          struct stmmac_dma_conf *dma_conf,
          u32 queue)
{
 struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
 int i;

 tx_q->xsk_frames_done = 0;

 for (i = 0; i < dma_conf->dma_tx_size; i++)
  stmmac_free_tx_buffer(priv, dma_conf, queue, i);

 if (tx_q->xsk_pool && tx_q->xsk_frames_done) {
  xsk_tx_completed(tx_q->xsk_pool, tx_q->xsk_frames_done);
  tx_q->xsk_frames_done = 0;
  tx_q->xsk_pool = NULL;
 }
}

/**
 * stmmac_free_tx_skbufs - free TX skb buffers
 * @priv: private structure
 */

static void stmmac_free_tx_skbufs(struct stmmac_priv *priv)
{
 u32 tx_queue_cnt = priv->plat->tx_queues_to_use;
 u32 queue;

 for (queue = 0; queue < tx_queue_cnt; queue++)
  dma_free_tx_skbufs(priv, &priv->dma_conf, queue);
}

/**
 * __free_dma_rx_desc_resources - free RX dma desc resources (per queue)
 * @priv: private structure
 * @dma_conf: structure to take the dma data
 * @queue: RX queue index
 */

static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
      struct stmmac_dma_conf *dma_conf,
      u32 queue)
{
 struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];

 /* Release the DMA RX socket buffers */
 if (rx_q->xsk_pool)
  dma_free_rx_xskbufs(priv, dma_conf, queue);
 else
  dma_free_rx_skbufs(priv, dma_conf, queue);

 rx_q->buf_alloc_num = 0;
 rx_q->xsk_pool = NULL;

 /* Free DMA regions of consistent memory previously allocated */
 if (!priv->extend_desc)
  dma_free_coherent(priv->device, dma_conf->dma_rx_size *
      sizeof(struct dma_desc),
      rx_q->dma_rx, rx_q->dma_rx_phy);
 else
  dma_free_coherent(priv->device, dma_conf->dma_rx_size *
      sizeof(struct dma_extended_desc),
      rx_q->dma_erx, rx_q->dma_rx_phy);

 if (xdp_rxq_info_is_reg(&rx_q->xdp_rxq))
  xdp_rxq_info_unreg(&rx_q->xdp_rxq);

 kfree(rx_q->buf_pool);
 if (rx_q->page_pool)
  page_pool_destroy(rx_q->page_pool);
}

static void free_dma_rx_desc_resources(struct stmmac_priv *priv,
           struct stmmac_dma_conf *dma_conf)
{
 u32 rx_count = priv->plat->rx_queues_to_use;
 u32 queue;

 /* Free RX queue resources */
 for (queue = 0; queue < rx_count; queue++)
  __free_dma_rx_desc_resources(priv, dma_conf, queue);
}

/**
 * __free_dma_tx_desc_resources - free TX dma desc resources (per queue)
 * @priv: private structure
 * @dma_conf: structure to take the dma data
 * @queue: TX queue index
 */

static void __free_dma_tx_desc_resources(struct stmmac_priv *priv,
      struct stmmac_dma_conf *dma_conf,
      u32 queue)
{
 struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
 size_t size;
 void *addr;

 /* Release the DMA TX socket buffers */
 dma_free_tx_skbufs(priv, dma_conf, queue);

 if (priv->extend_desc) {
  size = sizeof(struct dma_extended_desc);
  addr = tx_q->dma_etx;
 } else if (tx_q->tbs & STMMAC_TBS_AVAIL) {
  size = sizeof(struct dma_edesc);
  addr = tx_q->dma_entx;
 } else {
  size = sizeof(struct dma_desc);
  addr = tx_q->dma_tx;
 }

 size *= dma_conf->dma_tx_size;

 dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy);

 kfree(tx_q->tx_skbuff_dma);
 kfree(tx_q->tx_skbuff);
}

static void free_dma_tx_desc_resources(struct stmmac_priv *priv,
           struct stmmac_dma_conf *dma_conf)
{
 u32 tx_count = priv->plat->tx_queues_to_use;
 u32 queue;

 /* Free TX queue resources */
 for (queue = 0; queue < tx_count; queue++)
  __free_dma_tx_desc_resources(priv, dma_conf, queue);
}

/**
 * __alloc_dma_rx_desc_resources - alloc RX resources (per queue).
 * @priv: private structure
 * @dma_conf: structure to take the dma data
 * @queue: RX queue index
 * Description: according to which descriptor can be used (extend or basic)
 * this function allocates the resources for TX and RX paths. In case of
 * reception, for example, it pre-allocated the RX socket buffer in order to
 * allow zero-copy mechanism.
 */

static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
      struct stmmac_dma_conf *dma_conf,
      u32 queue)
{
 struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
 struct stmmac_channel *ch = &priv->channel[queue];
 bool xdp_prog = stmmac_xdp_is_enabled(priv);
 struct page_pool_params pp_params = { 0 };
 unsigned int dma_buf_sz_pad, num_pages;
 unsigned int napi_id;
 int ret;

 dma_buf_sz_pad = stmmac_rx_offset(priv) + dma_conf->dma_buf_sz +
    SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 num_pages = DIV_ROUND_UP(dma_buf_sz_pad, PAGE_SIZE);

 rx_q->queue_index = queue;
 rx_q->priv_data = priv;
 rx_q->napi_skb_frag_size = num_pages * PAGE_SIZE;

 pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
 pp_params.pool_size = dma_conf->dma_rx_size;
 pp_params.order = order_base_2(num_pages);
 pp_params.nid = dev_to_node(priv->device);
 pp_params.dev = priv->device;
 pp_params.dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
 pp_params.offset = stmmac_rx_offset(priv);
 pp_params.max_len = dma_conf->dma_buf_sz;

 if (priv->sph) {
  pp_params.offset = 0;
  pp_params.max_len += stmmac_rx_offset(priv);
 }

 rx_q->page_pool = page_pool_create(&pp_params);
 if (IS_ERR(rx_q->page_pool)) {
  ret = PTR_ERR(rx_q->page_pool);
  rx_q->page_pool = NULL;
  return ret;
 }

 rx_q->buf_pool = kcalloc(dma_conf->dma_rx_size,
     sizeof(*rx_q->buf_pool),
     GFP_KERNEL);
 if (!rx_q->buf_pool)
  return -ENOMEM;

 if (priv->extend_desc) {
  rx_q->dma_erx = dma_alloc_coherent(priv->device,
         dma_conf->dma_rx_size *
         sizeof(struct dma_extended_desc),
         &rx_q->dma_rx_phy,
         GFP_KERNEL);
  if (!rx_q->dma_erx)
   return -ENOMEM;

 } else {
  rx_q->dma_rx = dma_alloc_coherent(priv->device,
        dma_conf->dma_rx_size *
        sizeof(struct dma_desc),
        &rx_q->dma_rx_phy,
        GFP_KERNEL);
  if (!rx_q->dma_rx)
   return -ENOMEM;
 }

 if (stmmac_xdp_is_enabled(priv) &&
     test_bit(queue, priv->af_xdp_zc_qps))
  napi_id = ch->rxtx_napi.napi_id;
 else
  napi_id = ch->rx_napi.napi_id;

 ret = xdp_rxq_info_reg(&rx_q->xdp_rxq, priv->dev,
          rx_q->queue_index,
          napi_id);
 if (ret) {
  netdev_err(priv->dev, "Failed to register xdp rxq info\n");
  return -EINVAL;
 }

 return 0;
}

static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
           struct stmmac_dma_conf *dma_conf)
{
 u32 rx_count = priv->plat->rx_queues_to_use;
 u32 queue;
 int ret;

 /* RX queues buffers and DMA */
 for (queue = 0; queue < rx_count; queue++) {
  ret = __alloc_dma_rx_desc_resources(priv, dma_conf, queue);
  if (ret)
   goto err_dma;
 }

 return 0;

err_dma:
 free_dma_rx_desc_resources(priv, dma_conf);

 return ret;
}

/**
 * __alloc_dma_tx_desc_resources - alloc TX resources (per queue).
 * @priv: private structure
 * @dma_conf: structure to take the dma data
 * @queue: TX queue index
 * Description: according to which descriptor can be used (extend or basic)
 * this function allocates the resources for TX and RX paths. In case of
 * reception, for example, it pre-allocated the RX socket buffer in order to
 * allow zero-copy mechanism.
 */

static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv,
      struct stmmac_dma_conf *dma_conf,
      u32 queue)
{
 struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
 size_t size;
 void *addr;

 tx_q->queue_index = queue;
 tx_q->priv_data = priv;

 tx_q->tx_skbuff_dma = kcalloc(dma_conf->dma_tx_size,
          sizeof(*tx_q->tx_skbuff_dma),
          GFP_KERNEL);
 if (!tx_q->tx_skbuff_dma)
  return -ENOMEM;

 tx_q->tx_skbuff = kcalloc(dma_conf->dma_tx_size,
      sizeof(struct sk_buff *),
      GFP_KERNEL);
 if (!tx_q->tx_skbuff)
  return -ENOMEM;

 if (priv->extend_desc)
  size = sizeof(struct dma_extended_desc);
 else if (tx_q->tbs & STMMAC_TBS_AVAIL)
  size = sizeof(struct dma_edesc);
 else
  size = sizeof(struct dma_desc);

 size *= dma_conf->dma_tx_size;

 addr = dma_alloc_coherent(priv->device, size,
      &tx_q->dma_tx_phy, GFP_KERNEL);
 if (!addr)
  return -ENOMEM;

 if (priv->extend_desc)
  tx_q->dma_etx = addr;
 else if (tx_q->tbs & STMMAC_TBS_AVAIL)
  tx_q->dma_entx = addr;
 else
  tx_q->dma_tx = addr;

 return 0;
}

static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv,
           struct stmmac_dma_conf *dma_conf)
{
 u32 tx_count = priv->plat->tx_queues_to_use;
 u32 queue;
 int ret;

 /* TX queues buffers and DMA */
 for (queue = 0; queue < tx_count; queue++) {
  ret = __alloc_dma_tx_desc_resources(priv, dma_conf, queue);
  if (ret)
   goto err_dma;
 }

 return 0;

err_dma:
 free_dma_tx_desc_resources(priv, dma_conf);
 return ret;
}

/**
 * alloc_dma_desc_resources - alloc TX/RX resources.
 * @priv: private structure
 * @dma_conf: structure to take the dma data
 * Description: according to which descriptor can be used (extend or basic)
 * this function allocates the resources for TX and RX paths. In case of
 * reception, for example, it pre-allocated the RX socket buffer in order to
 * allow zero-copy mechanism.
 */

static int alloc_dma_desc_resources(struct stmmac_priv *priv,
        struct stmmac_dma_conf *dma_conf)
{
 /* RX Allocation */
 int ret = alloc_dma_rx_desc_resources(priv, dma_conf);

 if (ret)
  return ret;

 ret = alloc_dma_tx_desc_resources(priv, dma_conf);

 return ret;
}

/**
 * free_dma_desc_resources - free dma desc resources
 * @priv: private structure
 * @dma_conf: structure to take the dma data
 */

static void free_dma_desc_resources(struct stmmac_priv *priv,
        struct stmmac_dma_conf *dma_conf)
{
 /* Release the DMA TX socket buffers */
 free_dma_tx_desc_resources(priv, dma_conf);

 /* Release the DMA RX socket buffers later
 * to ensure all pending XDP_TX buffers are returned.
 */

 free_dma_rx_desc_resources(priv, dma_conf);
}

/**
 *  stmmac_mac_enable_rx_queues - Enable MAC rx queues
 *  @priv: driver private structure
 *  Description: It is used for enabling the rx queues in the MAC
 */

static void stmmac_mac_enable_rx_queues(struct stmmac_priv *priv)
{
 u32 rx_queues_count = priv->plat->rx_queues_to_use;
 int queue;
 u8 mode;

 for (queue = 0; queue < rx_queues_count; queue++) {
  mode = priv->plat->rx_queues_cfg[queue].mode_to_use;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge