Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/ethernet/microchip/sparx5/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 13 kB image not shown  

Quelle  sparx5_fdma.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/* Microchip Sparx5 Switch driver
 *
 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
 *
 * The Sparx5 Chip Register Model can be browsed at this location:
 * https://github.com/microchip-ung/sparx-5_reginfo
 */


#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/interrupt.h>
#include <linux/ip.h>
#include <linux/dma-mapping.h>

#include "sparx5_main_regs.h"
#include "sparx5_main.h"
#include "sparx5_port.h"

#define FDMA_XTR_BUFFER_SIZE  2048
#define FDMA_WEIGHT   4

static int sparx5_fdma_tx_dataptr_cb(struct fdma *fdma, int dcb, int db,
         u64 *dataptr)
{
 *dataptr = fdma->dma + (sizeof(struct fdma_dcb) * fdma->n_dcbs) +
     ((dcb * fdma->n_dbs + db) * fdma->db_size);

 return 0;
}

static int sparx5_fdma_rx_dataptr_cb(struct fdma *fdma, int dcb, int db,
         u64 *dataptr)
{
 struct sparx5 *sparx5 = fdma->priv;
 struct sparx5_rx *rx = &sparx5->rx;
 struct sk_buff *skb;

 skb = __netdev_alloc_skb(rx->ndev, fdma->db_size, GFP_ATOMIC);
 if (unlikely(!skb))
  return -ENOMEM;

 *dataptr = virt_to_phys(skb->data);

 rx->skb[dcb][db] = skb;

 return 0;
}

static void sparx5_fdma_rx_activate(struct sparx5 *sparx5, struct sparx5_rx *rx)
{
 struct fdma *fdma = &rx->fdma;

 /* Write the buffer address in the LLP and LLP1 regs */
 spx5_wr(((u64)fdma->dma) & GENMASK(31, 0), sparx5,
  FDMA_DCB_LLP(fdma->channel_id));
 spx5_wr(((u64)fdma->dma) >> 32, sparx5,
  FDMA_DCB_LLP1(fdma->channel_id));

 /* Set the number of RX DBs to be used, and DB end-of-frame interrupt */
 spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(fdma->n_dbs) |
  FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) |
  FDMA_CH_CFG_CH_INJ_PORT_SET(XTR_QUEUE),
  sparx5, FDMA_CH_CFG(fdma->channel_id));

 /* Set the RX Watermark to max */
 spx5_rmw(FDMA_XTR_CFG_XTR_FIFO_WM_SET(31), FDMA_XTR_CFG_XTR_FIFO_WM,
   sparx5,
   FDMA_XTR_CFG);

 /* Start RX fdma */
 spx5_rmw(FDMA_PORT_CTRL_XTR_STOP_SET(0), FDMA_PORT_CTRL_XTR_STOP,
   sparx5, FDMA_PORT_CTRL(0));

 /* Enable RX channel DB interrupt */
 spx5_rmw(BIT(fdma->channel_id),
   BIT(fdma->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA,
   sparx5, FDMA_INTR_DB_ENA);

 /* Activate the RX channel */
 spx5_wr(BIT(fdma->channel_id), sparx5, FDMA_CH_ACTIVATE);
}

static void sparx5_fdma_rx_deactivate(struct sparx5 *sparx5, struct sparx5_rx *rx)
{
 struct fdma *fdma = &rx->fdma;

 /* Deactivate the RX channel */
 spx5_rmw(0, BIT(fdma->channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE,
   sparx5, FDMA_CH_ACTIVATE);

 /* Disable RX channel DB interrupt */
 spx5_rmw(0, BIT(fdma->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA,
   sparx5, FDMA_INTR_DB_ENA);

 /* Stop RX fdma */
 spx5_rmw(FDMA_PORT_CTRL_XTR_STOP_SET(1), FDMA_PORT_CTRL_XTR_STOP,
   sparx5, FDMA_PORT_CTRL(0));
}

static void sparx5_fdma_tx_activate(struct sparx5 *sparx5, struct sparx5_tx *tx)
{
 struct fdma *fdma = &tx->fdma;

 /* Write the buffer address in the LLP and LLP1 regs */
 spx5_wr(((u64)fdma->dma) & GENMASK(31, 0), sparx5,
  FDMA_DCB_LLP(fdma->channel_id));
 spx5_wr(((u64)fdma->dma) >> 32, sparx5,
  FDMA_DCB_LLP1(fdma->channel_id));

 /* Set the number of TX DBs to be used, and DB end-of-frame interrupt */
 spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(fdma->n_dbs) |
  FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) |
  FDMA_CH_CFG_CH_INJ_PORT_SET(INJ_QUEUE),
  sparx5, FDMA_CH_CFG(fdma->channel_id));

 /* Start TX fdma */
 spx5_rmw(FDMA_PORT_CTRL_INJ_STOP_SET(0), FDMA_PORT_CTRL_INJ_STOP,
   sparx5, FDMA_PORT_CTRL(0));

 /* Activate the channel */
 spx5_wr(BIT(fdma->channel_id), sparx5, FDMA_CH_ACTIVATE);
}

static void sparx5_fdma_tx_deactivate(struct sparx5 *sparx5, struct sparx5_tx *tx)
{
 /* Disable the channel */
 spx5_rmw(0, BIT(tx->fdma.channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE,
   sparx5, FDMA_CH_ACTIVATE);
}

void sparx5_fdma_reload(struct sparx5 *sparx5, struct fdma *fdma)
{
 /* Reload the RX channel */
 spx5_wr(BIT(fdma->channel_id), sparx5, FDMA_CH_RELOAD);
}

static bool sparx5_fdma_rx_get_frame(struct sparx5 *sparx5, struct sparx5_rx *rx)
{
 struct fdma *fdma = &rx->fdma;
 struct sparx5_port *port;
 struct fdma_db *db_hw;
 struct frame_info fi;
 struct sk_buff *skb;

 /* Check if the DCB is done */
 db_hw = fdma_db_next_get(fdma);
 if (unlikely(!fdma_db_is_done(db_hw)))
  return false;
 skb = rx->skb[fdma->dcb_index][fdma->db_index];
 skb_put(skb, fdma_db_len_get(db_hw));
 /* Now do the normal processing of the skb */
 sparx5_ifh_parse(sparx5, (u32 *)skb->data, &fi);
 /* Map to port netdev */
 port = fi.src_port < sparx5->data->consts->n_ports ?
         sparx5->ports[fi.src_port] :
         NULL;
 if (!port || !port->ndev) {
  dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);
  sparx5_xtr_flush(sparx5, XTR_QUEUE);
  return false;
 }
 skb->dev = port->ndev;
 skb_pull(skb, IFH_LEN * sizeof(u32));
 if (likely(!(skb->dev->features & NETIF_F_RXFCS)))
  skb_trim(skb, skb->len - ETH_FCS_LEN);

 sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp);
 skb->protocol = eth_type_trans(skb, skb->dev);
 /* Everything we see on an interface that is in the HW bridge
 * has already been forwarded
 */

 if (test_bit(port->portno, sparx5->bridge_mask))
  skb->offload_fwd_mark = 1;
 skb->dev->stats.rx_bytes += skb->len;
 skb->dev->stats.rx_packets++;
 rx->packets++;
 netif_receive_skb(skb);
 return true;
}

int sparx5_fdma_napi_callback(struct napi_struct *napi, int weight)
{
 struct sparx5_rx *rx = container_of(napi, struct sparx5_rx, napi);
 struct sparx5 *sparx5 = container_of(rx, struct sparx5, rx);
 struct fdma *fdma = &rx->fdma;
 int counter = 0;

 while (counter < weight && sparx5_fdma_rx_get_frame(sparx5, rx)) {
  fdma_db_advance(fdma);
  counter++;
  /* Check if the DCB can be reused */
  if (fdma_dcb_is_reusable(fdma))
   continue;
  fdma_dcb_add(fdma, fdma->dcb_index,
        FDMA_DCB_INFO_DATAL(fdma->db_size),
        FDMA_DCB_STATUS_INTR);
  fdma_db_reset(fdma);
  fdma_dcb_advance(fdma);
 }
 if (counter < weight) {
  napi_complete_done(&rx->napi, counter);
  spx5_rmw(BIT(fdma->channel_id),
    BIT(fdma->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA,
    sparx5, FDMA_INTR_DB_ENA);
 }
 if (counter)
  sparx5_fdma_reload(sparx5, fdma);
 return counter;
}

int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb,
       struct net_device *dev)
{
 struct sparx5_tx *tx = &sparx5->tx;
 struct fdma *fdma = &tx->fdma;
 void *virt_addr;

 fdma_dcb_advance(fdma);
 if (!fdma_db_is_done(fdma_db_get(fdma, fdma->dcb_index, 0)))
  return -EINVAL;

 /* Get the virtual address of the dataptr for the next DB */
 virt_addr = ((u8 *)fdma->dcbs +
       (sizeof(struct fdma_dcb) * fdma->n_dcbs) +
       ((fdma->dcb_index * fdma->n_dbs) * fdma->db_size));

 memcpy(virt_addr, ifh, IFH_LEN * 4);
 memcpy(virt_addr + IFH_LEN * 4, skb->data, skb->len);

 fdma_dcb_add(fdma, fdma->dcb_index, 0,
       FDMA_DCB_STATUS_SOF |
       FDMA_DCB_STATUS_EOF |
       FDMA_DCB_STATUS_BLOCKO(0) |
       FDMA_DCB_STATUS_BLOCKL(skb->len + IFH_LEN * 4 + 4));

 sparx5_fdma_reload(sparx5, fdma);

 return NETDEV_TX_OK;
}

static int sparx5_fdma_rx_alloc(struct sparx5 *sparx5)
{
 struct sparx5_rx *rx = &sparx5->rx;
 struct fdma *fdma = &rx->fdma;
 int err;

 err = fdma_alloc_phys(fdma);
 if (err)
  return err;

 fdma_dcbs_init(fdma, FDMA_DCB_INFO_DATAL(fdma->db_size),
         FDMA_DCB_STATUS_INTR);

 return 0;
}

static int sparx5_fdma_tx_alloc(struct sparx5 *sparx5)
{
 struct sparx5_tx *tx = &sparx5->tx;
 struct fdma *fdma = &tx->fdma;
 int err;

 err = fdma_alloc_phys(fdma);
 if (err)
  return err;

 fdma_dcbs_init(fdma, FDMA_DCB_INFO_DATAL(fdma->db_size),
         FDMA_DCB_STATUS_DONE);

 return 0;
}

static void sparx5_fdma_rx_init(struct sparx5 *sparx5,
    struct sparx5_rx *rx, int channel)
{
 struct fdma *fdma = &rx->fdma;
 int idx;

 fdma->channel_id = channel;
 fdma->n_dcbs = FDMA_DCB_MAX;
 fdma->n_dbs = FDMA_RX_DCB_MAX_DBS;
 fdma->priv = sparx5;
 fdma->db_size = ALIGN(FDMA_XTR_BUFFER_SIZE, PAGE_SIZE);
 fdma->size = fdma_get_size(&sparx5->rx.fdma);
 fdma->ops.dataptr_cb = &sparx5_fdma_rx_dataptr_cb;
 fdma->ops.nextptr_cb = &fdma_nextptr_cb;
 /* Fetch a netdev for SKB and NAPI use, any will do */
 for (idx = 0; idx < sparx5->data->consts->n_ports; ++idx) {
  struct sparx5_port *port = sparx5->ports[idx];

  if (port && port->ndev) {
   rx->ndev = port->ndev;
   break;
  }
 }
}

static void sparx5_fdma_tx_init(struct sparx5 *sparx5,
    struct sparx5_tx *tx, int channel)
{
 struct fdma *fdma = &tx->fdma;

 fdma->channel_id = channel;
 fdma->n_dcbs = FDMA_DCB_MAX;
 fdma->n_dbs = FDMA_TX_DCB_MAX_DBS;
 fdma->priv = sparx5;
 fdma->db_size = ALIGN(FDMA_XTR_BUFFER_SIZE, PAGE_SIZE);
 fdma->size = fdma_get_size_contiguous(&sparx5->tx.fdma);
 fdma->ops.dataptr_cb = &sparx5_fdma_tx_dataptr_cb;
 fdma->ops.nextptr_cb = &fdma_nextptr_cb;
}

irqreturn_t sparx5_fdma_handler(int irq, void *args)
{
 struct sparx5 *sparx5 = args;
 u32 db = 0, err = 0;

 db = spx5_rd(sparx5, FDMA_INTR_DB);
 err = spx5_rd(sparx5, FDMA_INTR_ERR);
 /* Clear interrupt */
 if (db) {
  spx5_wr(0, sparx5, FDMA_INTR_DB_ENA);
  spx5_wr(db, sparx5, FDMA_INTR_DB);
  napi_schedule(&sparx5->rx.napi);
 }
 if (err) {
  u32 err_type = spx5_rd(sparx5, FDMA_ERRORS);

  dev_err_ratelimited(sparx5->dev,
        "ERR: int: %#x, type: %#x\n",
        err, err_type);
  spx5_wr(err, sparx5, FDMA_INTR_ERR);
  spx5_wr(err_type, sparx5, FDMA_ERRORS);
 }
 return IRQ_HANDLED;
}

void sparx5_fdma_injection_mode(struct sparx5 *sparx5)
{
 const int byte_swap = 1;
 int portno;
 int urgency;

 /* Change mode to fdma extraction and injection */
 spx5_wr(QS_XTR_GRP_CFG_MODE_SET(2) |
  QS_XTR_GRP_CFG_STATUS_WORD_POS_SET(1) |
  QS_XTR_GRP_CFG_BYTE_SWAP_SET(byte_swap),
  sparx5, QS_XTR_GRP_CFG(XTR_QUEUE));
 spx5_wr(QS_INJ_GRP_CFG_MODE_SET(2) |
  QS_INJ_GRP_CFG_BYTE_SWAP_SET(byte_swap),
  sparx5, QS_INJ_GRP_CFG(INJ_QUEUE));

 /* CPU ports capture setup */
 for (portno = sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_0);
      portno <= sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_1);
      portno++) {
  /* ASM CPU port: No preamble, IFH, enable padding */
  spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) |
   ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) |
   ASM_PORT_CFG_INJ_FORMAT_CFG_SET(1), /* 1 = IFH */
   sparx5, ASM_PORT_CFG(portno));

  /* Reset WM cnt to unclog queued frames */
  spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1),
    DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR,
    sparx5,
    DSM_DEV_TX_STOP_WM_CFG(portno));

  /* Set Disassembler Stop Watermark level */
  spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(100),
    DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,
    sparx5,
    DSM_DEV_TX_STOP_WM_CFG(portno));

  /* Enable port in queue system */
  urgency = sparx5_port_fwd_urg(sparx5, SPEED_2500);
  spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1) |
    QFWD_SWITCH_PORT_MODE_FWD_URGENCY_SET(urgency),
    QFWD_SWITCH_PORT_MODE_PORT_ENA |
    QFWD_SWITCH_PORT_MODE_FWD_URGENCY,
    sparx5,
    QFWD_SWITCH_PORT_MODE(portno));

  /* Disable Disassembler buffer underrun watchdog
 * to avoid truncated packets in XTR
 */

  spx5_rmw(DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS_SET(1),
    DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS,
    sparx5,
    DSM_BUF_CFG(portno));

  /* Disabling frame aging */
  spx5_rmw(HSCH_PORT_MODE_AGE_DIS_SET(1),
    HSCH_PORT_MODE_AGE_DIS,
    sparx5,
    HSCH_PORT_MODE(portno));
 }
}

int sparx5_fdma_init(struct sparx5 *sparx5)
{
 int err;

 /* Reset FDMA state */
 spx5_wr(FDMA_CTRL_NRESET_SET(0), sparx5, FDMA_CTRL);
 spx5_wr(FDMA_CTRL_NRESET_SET(1), sparx5, FDMA_CTRL);

 /* Force ACP caching but disable read/write allocation */
 spx5_rmw(CPU_PROC_CTRL_ACP_CACHE_FORCE_ENA_SET(1) |
   CPU_PROC_CTRL_ACP_AWCACHE_SET(0) |
   CPU_PROC_CTRL_ACP_ARCACHE_SET(0),
   CPU_PROC_CTRL_ACP_CACHE_FORCE_ENA |
   CPU_PROC_CTRL_ACP_AWCACHE |
   CPU_PROC_CTRL_ACP_ARCACHE,
   sparx5, CPU_PROC_CTRL);

 sparx5_fdma_injection_mode(sparx5);
 sparx5_fdma_rx_init(sparx5, &sparx5->rx, FDMA_XTR_CHANNEL);
 sparx5_fdma_tx_init(sparx5, &sparx5->tx, FDMA_INJ_CHANNEL);
 err = sparx5_fdma_rx_alloc(sparx5);
 if (err) {
  dev_err(sparx5->dev, "Could not allocate RX buffers: %d\n", err);
  return err;
 }
 err = sparx5_fdma_tx_alloc(sparx5);
 if (err) {
  dev_err(sparx5->dev, "Could not allocate TX buffers: %d\n", err);
  return err;
 }
 return err;
}

int sparx5_fdma_deinit(struct sparx5 *sparx5)
{
 sparx5_fdma_stop(sparx5);
 fdma_free_phys(&sparx5->rx.fdma);
 fdma_free_phys(&sparx5->tx.fdma);

 return 0;
}

static u32 sparx5_fdma_port_ctrl(struct sparx5 *sparx5)
{
 return spx5_rd(sparx5, FDMA_PORT_CTRL(0));
}

int sparx5_fdma_start(struct sparx5 *sparx5)
{
 const struct sparx5_ops *ops = sparx5->data->ops;
 struct sparx5_rx *rx = &sparx5->rx;
 struct sparx5_tx *tx = &sparx5->tx;

 netif_napi_add_weight(rx->ndev,
         &rx->napi,
         ops->fdma_poll,
         FDMA_WEIGHT);

 napi_enable(&rx->napi);

 sparx5_fdma_rx_activate(sparx5, rx);
 sparx5_fdma_tx_activate(sparx5, tx);

 return 0;
}

int sparx5_fdma_stop(struct sparx5 *sparx5)
{
 struct sparx5_rx *rx = &sparx5->rx;
 struct sparx5_tx *tx = &sparx5->tx;
 u32 val;

 napi_disable(&rx->napi);

 /* Stop the fdma and channel interrupts */
 sparx5_fdma_rx_deactivate(sparx5, rx);
 sparx5_fdma_tx_deactivate(sparx5, tx);

 /* Wait for the RX channel to stop */
 read_poll_timeout(sparx5_fdma_port_ctrl, val,
     FDMA_PORT_CTRL_XTR_BUF_IS_EMPTY_GET(val) == 0,
     500, 10000, 0, sparx5);

 return 0;
}

Messung V0.5
C=92 H=98 G=94

¤ Dauer der Verarbeitung: 0.3 Sekunden  ¤

*© 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.