linux/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/* Microchip Sparx5 Switch driver
   3 *
   4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
   5 */
   6
   7#include "sparx5_main_regs.h"
   8#include "sparx5_main.h"
   9
  10#define XTR_EOF_0     ntohl((__force __be32)0x80000000u)
  11#define XTR_EOF_1     ntohl((__force __be32)0x80000001u)
  12#define XTR_EOF_2     ntohl((__force __be32)0x80000002u)
  13#define XTR_EOF_3     ntohl((__force __be32)0x80000003u)
  14#define XTR_PRUNED    ntohl((__force __be32)0x80000004u)
  15#define XTR_ABORT     ntohl((__force __be32)0x80000005u)
  16#define XTR_ESCAPE    ntohl((__force __be32)0x80000006u)
  17#define XTR_NOT_READY ntohl((__force __be32)0x80000007u)
  18
  19#define XTR_VALID_BYTES(x)      (4 - ((x) & 3))
  20
  21#define INJ_TIMEOUT_NS 50000
  22
  23void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp)
  24{
  25        /* Start flush */
  26        spx5_wr(QS_XTR_FLUSH_FLUSH_SET(BIT(grp)), sparx5, QS_XTR_FLUSH);
  27
  28        /* Allow to drain */
  29        mdelay(1);
  30
  31        /* All Queues normal */
  32        spx5_wr(0, sparx5, QS_XTR_FLUSH);
  33}
  34
  35void sparx5_ifh_parse(u32 *ifh, struct frame_info *info)
  36{
  37        u8 *xtr_hdr = (u8 *)ifh;
  38
  39        /* FWD is bit 45-72 (28 bits), but we only read the 27 LSB for now */
  40        u32 fwd =
  41                ((u32)xtr_hdr[27] << 24) |
  42                ((u32)xtr_hdr[28] << 16) |
  43                ((u32)xtr_hdr[29] <<  8) |
  44                ((u32)xtr_hdr[30] <<  0);
  45        fwd = (fwd >> 5);
  46        info->src_port = FIELD_GET(GENMASK(7, 1), fwd);
  47}
  48
  49static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap)
  50{
  51        bool eof_flag = false, pruned_flag = false, abort_flag = false;
  52        struct net_device *netdev;
  53        struct sparx5_port *port;
  54        struct frame_info fi;
  55        int i, byte_cnt = 0;
  56        struct sk_buff *skb;
  57        u32 ifh[IFH_LEN];
  58        u32 *rxbuf;
  59
  60        /* Get IFH */
  61        for (i = 0; i < IFH_LEN; i++)
  62                ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
  63
  64        /* Decode IFH (whats needed) */
  65        sparx5_ifh_parse(ifh, &fi);
  66
  67        /* Map to port netdev */
  68        port = fi.src_port < SPX5_PORTS ?
  69                sparx5->ports[fi.src_port] : NULL;
  70        if (!port || !port->ndev) {
  71                dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);
  72                sparx5_xtr_flush(sparx5, grp);
  73                return;
  74        }
  75
  76        /* Have netdev, get skb */
  77        netdev = port->ndev;
  78        skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
  79        if (!skb) {
  80                sparx5_xtr_flush(sparx5, grp);
  81                dev_err(sparx5->dev, "No skb allocated\n");
  82                netdev->stats.rx_dropped++;
  83                return;
  84        }
  85        rxbuf = (u32 *)skb->data;
  86
  87        /* Now, pull frame data */
  88        while (!eof_flag) {
  89                u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
  90                u32 cmp = val;
  91
  92                if (byte_swap)
  93                        cmp = ntohl((__force __be32)val);
  94
  95                switch (cmp) {
  96                case XTR_NOT_READY:
  97                        break;
  98                case XTR_ABORT:
  99                        /* No accompanying data */
 100                        abort_flag = true;
 101                        eof_flag = true;
 102                        break;
 103                case XTR_EOF_0:
 104                case XTR_EOF_1:
 105                case XTR_EOF_2:
 106                case XTR_EOF_3:
 107                        /* This assumes STATUS_WORD_POS == 1, Status
 108                         * just after last data
 109                         */
 110                        byte_cnt -= (4 - XTR_VALID_BYTES(val));
 111                        eof_flag = true;
 112                        break;
 113                case XTR_PRUNED:
 114                        /* But get the last 4 bytes as well */
 115                        eof_flag = true;
 116                        pruned_flag = true;
 117                        fallthrough;
 118                case XTR_ESCAPE:
 119                        *rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
 120                        byte_cnt += 4;
 121                        rxbuf++;
 122                        break;
 123                default:
 124                        *rxbuf = val;
 125                        byte_cnt += 4;
 126                        rxbuf++;
 127                }
 128        }
 129
 130        if (abort_flag || pruned_flag || !eof_flag) {
 131                netdev_err(netdev, "Discarded frame: abort:%d pruned:%d eof:%d\n",
 132                           abort_flag, pruned_flag, eof_flag);
 133                kfree_skb(skb);
 134                netdev->stats.rx_dropped++;
 135                return;
 136        }
 137
 138        /* Everything we see on an interface that is in the HW bridge
 139         * has already been forwarded
 140         */
 141        if (test_bit(port->portno, sparx5->bridge_mask))
 142                skb->offload_fwd_mark = 1;
 143
 144        /* Finish up skb */
 145        skb_put(skb, byte_cnt - ETH_FCS_LEN);
 146        eth_skb_pad(skb);
 147        skb->protocol = eth_type_trans(skb, netdev);
 148        netif_rx(skb);
 149        netdev->stats.rx_bytes += skb->len;
 150        netdev->stats.rx_packets++;
 151}
 152
 153static int sparx5_inject(struct sparx5 *sparx5,
 154                         u32 *ifh,
 155                         struct sk_buff *skb,
 156                         struct net_device *ndev)
 157{
 158        int grp = INJ_QUEUE;
 159        u32 val, w, count;
 160        u8 *buf;
 161
 162        val = spx5_rd(sparx5, QS_INJ_STATUS);
 163        if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp))) {
 164                pr_err_ratelimited("Injection: Queue not ready: 0x%lx\n",
 165                                   QS_INJ_STATUS_FIFO_RDY_GET(val));
 166                return -EBUSY;
 167        }
 168
 169        /* Indicate SOF */
 170        spx5_wr(QS_INJ_CTRL_SOF_SET(1) |
 171                QS_INJ_CTRL_GAP_SIZE_SET(1),
 172                sparx5, QS_INJ_CTRL(grp));
 173
 174        /* Write the IFH to the chip. */
 175        for (w = 0; w < IFH_LEN; w++)
 176                spx5_wr(ifh[w], sparx5, QS_INJ_WR(grp));
 177
 178        /* Write words, round up */
 179        count = DIV_ROUND_UP(skb->len, 4);
 180        buf = skb->data;
 181        for (w = 0; w < count; w++, buf += 4) {
 182                val = get_unaligned((const u32 *)buf);
 183                spx5_wr(val, sparx5, QS_INJ_WR(grp));
 184        }
 185
 186        /* Add padding */
 187        while (w < (60 / 4)) {
 188                spx5_wr(0, sparx5, QS_INJ_WR(grp));
 189                w++;
 190        }
 191
 192        /* Indicate EOF and valid bytes in last word */
 193        spx5_wr(QS_INJ_CTRL_GAP_SIZE_SET(1) |
 194                QS_INJ_CTRL_VLD_BYTES_SET(skb->len < 60 ? 0 : skb->len % 4) |
 195                QS_INJ_CTRL_EOF_SET(1),
 196                sparx5, QS_INJ_CTRL(grp));
 197
 198        /* Add dummy CRC */
 199        spx5_wr(0, sparx5, QS_INJ_WR(grp));
 200        w++;
 201
 202        val = spx5_rd(sparx5, QS_INJ_STATUS);
 203        if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {
 204                struct sparx5_port *port = netdev_priv(ndev);
 205
 206                pr_err_ratelimited("Injection: Watermark reached: 0x%lx\n",
 207                                   QS_INJ_STATUS_WMARK_REACHED_GET(val));
 208                netif_stop_queue(ndev);
 209                hrtimer_start(&port->inj_timer, INJ_TIMEOUT_NS,
 210                              HRTIMER_MODE_REL);
 211        }
 212
 213        return NETDEV_TX_OK;
 214}
 215
 216int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev)
 217{
 218        struct net_device_stats *stats = &dev->stats;
 219        struct sparx5_port *port = netdev_priv(dev);
 220        struct sparx5 *sparx5 = port->sparx5;
 221        int ret;
 222
 223        if (sparx5->fdma_irq > 0)
 224                ret = sparx5_fdma_xmit(sparx5, port->ifh, skb);
 225        else
 226                ret = sparx5_inject(sparx5, port->ifh, skb, dev);
 227
 228        if (ret == NETDEV_TX_OK) {
 229                stats->tx_bytes += skb->len;
 230                stats->tx_packets++;
 231                skb_tx_timestamp(skb);
 232                dev_kfree_skb_any(skb);
 233        } else {
 234                stats->tx_dropped++;
 235        }
 236        return ret;
 237}
 238
 239static enum hrtimer_restart sparx5_injection_timeout(struct hrtimer *tmr)
 240{
 241        struct sparx5_port *port = container_of(tmr, struct sparx5_port,
 242                                                inj_timer);
 243        int grp = INJ_QUEUE;
 244        u32 val;
 245
 246        val = spx5_rd(port->sparx5, QS_INJ_STATUS);
 247        if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {
 248                pr_err_ratelimited("Injection: Reset watermark count\n");
 249                /* Reset Watermark count to restart */
 250                spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1),
 251                         DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR,
 252                         port->sparx5,
 253                         DSM_DEV_TX_STOP_WM_CFG(port->portno));
 254        }
 255        netif_wake_queue(port->ndev);
 256        return HRTIMER_NORESTART;
 257}
 258
 259int sparx5_manual_injection_mode(struct sparx5 *sparx5)
 260{
 261        const int byte_swap = 1;
 262        int portno;
 263
 264        /* Change mode to manual extraction and injection */
 265        spx5_wr(QS_XTR_GRP_CFG_MODE_SET(1) |
 266                QS_XTR_GRP_CFG_STATUS_WORD_POS_SET(1) |
 267                QS_XTR_GRP_CFG_BYTE_SWAP_SET(byte_swap),
 268                sparx5, QS_XTR_GRP_CFG(XTR_QUEUE));
 269        spx5_wr(QS_INJ_GRP_CFG_MODE_SET(1) |
 270                QS_INJ_GRP_CFG_BYTE_SWAP_SET(byte_swap),
 271                sparx5, QS_INJ_GRP_CFG(INJ_QUEUE));
 272
 273        /* CPU ports capture setup */
 274        for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++) {
 275                /* ASM CPU port: No preamble, IFH, enable padding */
 276                spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) |
 277                        ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) |
 278                        ASM_PORT_CFG_INJ_FORMAT_CFG_SET(1), /* 1 = IFH */
 279                        sparx5, ASM_PORT_CFG(portno));
 280
 281                /* Reset WM cnt to unclog queued frames */
 282                spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1),
 283                         DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR,
 284                         sparx5,
 285                         DSM_DEV_TX_STOP_WM_CFG(portno));
 286
 287                /* Set Disassembler Stop Watermark level */
 288                spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(0),
 289                         DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,
 290                         sparx5,
 291                         DSM_DEV_TX_STOP_WM_CFG(portno));
 292
 293                /* Enable Disassembler buffer underrun watchdog
 294                 */
 295                spx5_rmw(DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS_SET(0),
 296                         DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS,
 297                         sparx5,
 298                         DSM_BUF_CFG(portno));
 299        }
 300        return 0;
 301}
 302
 303irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
 304{
 305        struct sparx5 *s5 = _sparx5;
 306        int poll = 64;
 307
 308        /* Check data in queue */
 309        while (spx5_rd(s5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE) && poll-- > 0)
 310                sparx5_xtr_grp(s5, XTR_QUEUE, false);
 311
 312        return IRQ_HANDLED;
 313}
 314
 315void sparx5_port_inj_timer_setup(struct sparx5_port *port)
 316{
 317        hrtimer_init(&port->inj_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
 318        port->inj_timer.function = sparx5_injection_timeout;
 319}
 320