root/net/dsa/tag_netc.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2025-2026 NXP
 */

#include <linux/dsa/tag_netc.h>

#include "tag.h"

#define NETC_NAME                       "nxp_netc"

/* Forward NXP switch tag */
#define NETC_TAG_FORWARD                0

/* To_Port NXP switch tag */
#define NETC_TAG_TO_PORT                1
/* SubType0: No request to perform timestamping */
#define NETC_TAG_TP_SUBTYPE0            0

/* To_Host NXP switch tag */
#define NETC_TAG_TO_HOST                2
/* SubType0: frames redirected or copied to CPU port */
#define NETC_TAG_TH_SUBTYPE0            0
/* SubType1: frames redirected or copied to CPU port with timestamp */
#define NETC_TAG_TH_SUBTYPE1            1
/* SubType2: Transmit timestamp response (two-step timestamping) */
#define NETC_TAG_TH_SUBTYPE2            2

/* NETC switch tag lengths */
#define NETC_TAG_FORWARD_LEN            6
#define NETC_TAG_TP_SUBTYPE0_LEN        6
#define NETC_TAG_TH_SUBTYPE0_LEN        6
#define NETC_TAG_TH_SUBTYPE1_LEN        14
#define NETC_TAG_TH_SUBTYPE2_LEN        14
#define NETC_TAG_CMN_LEN                5

#define NETC_TAG_SUBTYPE                GENMASK(3, 0)
#define NETC_TAG_TYPE                   GENMASK(7, 4)
#define NETC_TAG_QV                     BIT(0)
#define NETC_TAG_IPV                    GENMASK(4, 2)
#define NETC_TAG_SWITCH                 GENMASK(2, 0)
#define NETC_TAG_PORT                   GENMASK(7, 3)

struct netc_tag_cmn {
        __be16 tpid;
        u8 type;
        u8 qos;
        u8 switch_port;
} __packed;

static void netc_fill_common_tag(struct netc_tag_cmn *tag, u8 type,
                                 u8 subtype, u8 sw_id, u8 port, u8 ipv)
{
        tag->tpid = htons(ETH_P_NXP_NETC);
        tag->type = FIELD_PREP(NETC_TAG_TYPE, type) |
                    FIELD_PREP(NETC_TAG_SUBTYPE, subtype);
        tag->qos = NETC_TAG_QV | FIELD_PREP(NETC_TAG_IPV, ipv);
        tag->switch_port = FIELD_PREP(NETC_TAG_SWITCH, sw_id) |
                           FIELD_PREP(NETC_TAG_PORT, port);
}

static void *netc_fill_common_tp_tag(struct sk_buff *skb,
                                     struct net_device *ndev,
                                     u8 subtype, int tag_len)
{
        struct dsa_port *dp = dsa_user_to_port(ndev);
        u16 queue = skb_get_queue_mapping(skb);
        s8 ipv = netdev_txq_to_tc(ndev, queue);
        void *tag;

        if (unlikely(ipv < 0))
                ipv = 0;

        skb_push(skb, tag_len);
        dsa_alloc_etype_header(skb, tag_len);

        tag = dsa_etype_header_pos_tx(skb);
        memset(tag + NETC_TAG_CMN_LEN, 0, tag_len - NETC_TAG_CMN_LEN);
        /* As 'dsa,member' is a required property for NETC switch, the member
         * is used to specify the switch ID (thus the hardware switch ID and
         * the software switch ID are consistent), its range is 1 ~ 7. The
         * NETC switch driver will check this value, and if it is invalid,
         * the switch driver will fail the probe.
         * In addition, according to the nxp,netc-switch.yaml doc, the port
         * index will not be greater than 0xf.
         */
        netc_fill_common_tag(tag, NETC_TAG_TO_PORT, subtype,
                             dp->ds->index, dp->index, ipv);

        return tag;
}

static void netc_fill_tp_tag_subtype0(struct sk_buff *skb,
                                      struct net_device *ndev)
{
        netc_fill_common_tp_tag(skb, ndev, NETC_TAG_TP_SUBTYPE0,
                                NETC_TAG_TP_SUBTYPE0_LEN);
}

/* Currently only support To_Port tag, subtype 0 */
static struct sk_buff *netc_xmit(struct sk_buff *skb,
                                 struct net_device *ndev)
{
        netc_fill_tp_tag_subtype0(skb, ndev);

        return skb;
}

static int netc_get_rx_tag_len(int type, int subtype)
{
        /* Only NETC_TAG_TO_HOST and NETC_TAG_FORWARD are expected in RX,
         * NETC_TAG_TO_PORT is a TX switch tag that does not exist in RX.
         */
        if (type == NETC_TAG_TO_HOST) {
                if (subtype == NETC_TAG_TH_SUBTYPE1)
                        return NETC_TAG_TH_SUBTYPE1_LEN;
                else if (subtype == NETC_TAG_TH_SUBTYPE2)
                        return NETC_TAG_TH_SUBTYPE2_LEN;
                else
                        return NETC_TAG_TH_SUBTYPE0_LEN;
        }

        return NETC_TAG_FORWARD_LEN;
}

static struct sk_buff *netc_rcv(struct sk_buff *skb,
                                struct net_device *ndev)
{
        struct netc_tag_cmn *tag_cmn;
        int tag_len, sw_id, port;
        int type, subtype;

        if (unlikely(!pskb_may_pull(skb, NETC_TAG_MAX_LEN)))
                goto err_free_skb;

        tag_cmn = dsa_etype_header_pos_rx(skb);
        if (ntohs(tag_cmn->tpid) != ETH_P_NXP_NETC) {
                dev_warn_ratelimited(&ndev->dev, "Unknown TPID 0x%04x\n",
                                     ntohs(tag_cmn->tpid));
                goto err_free_skb;
        }

        if (tag_cmn->qos & NETC_TAG_QV)
                skb->priority = FIELD_GET(NETC_TAG_IPV, tag_cmn->qos);

        sw_id = FIELD_GET(NETC_TAG_SWITCH, tag_cmn->switch_port);
        /* ENETC VEPA switch ID (0) is not supported yet */
        if (!sw_id) {
                dev_warn_ratelimited(&ndev->dev,
                                     "VEPA switch ID is not supported yet\n");
                goto err_free_skb;
        }

        port = FIELD_GET(NETC_TAG_PORT, tag_cmn->switch_port);
        skb->dev = dsa_conduit_find_user(ndev, sw_id, port);
        if (!skb->dev)
                goto err_free_skb;

        type = FIELD_GET(NETC_TAG_TYPE, tag_cmn->type);
        subtype = FIELD_GET(NETC_TAG_SUBTYPE, tag_cmn->type);
        if (type == NETC_TAG_FORWARD) {
                dsa_default_offload_fwd_mark(skb);
        } else if (type == NETC_TAG_TO_HOST) {
                /* Currently only subtype0 supported */
                if (subtype != NETC_TAG_TH_SUBTYPE0)
                        goto err_free_skb;
        } else {
                dev_warn_ratelimited(&ndev->dev,
                                     "Unexpected  tag type %d\n", type);
                goto err_free_skb;
        }

        /* Remove Switch tag from the frame */
        tag_len = netc_get_rx_tag_len(type, subtype);
        skb_pull_rcsum(skb, tag_len);
        dsa_strip_etype_header(skb, tag_len);

        return skb;

err_free_skb:
        kfree_skb(skb);
        return NULL;
}

static void netc_flow_dissect(const struct sk_buff *skb, __be16 *proto,
                              int *offset)
{
        struct netc_tag_cmn *tag_cmn = (struct netc_tag_cmn *)(skb->data - 2);
        int subtype = FIELD_GET(NETC_TAG_SUBTYPE, tag_cmn->type);
        int type = FIELD_GET(NETC_TAG_TYPE, tag_cmn->type);
        int tag_len = netc_get_rx_tag_len(type, subtype);

        /* The RX minimum frame length of the NETC switch port is 64 bytes,
         * and the frame is received by the ENETC driver. From the hardware
         * perspective, the receive buffer of RX BD is at least 128 bytes,
         * so the switch tag header is guaranteed to be in the linear region
         * of the skb.
         */
        *offset = tag_len;
        *proto = ((__be16 *)skb->data)[(tag_len / 2) - 1];
}

static const struct dsa_device_ops netc_netdev_ops = {
        .name                   = NETC_NAME,
        .proto                  = DSA_TAG_PROTO_NETC,
        .xmit                   = netc_xmit,
        .rcv                    = netc_rcv,
        .needed_headroom        = NETC_TAG_MAX_LEN,
        .flow_dissect           = netc_flow_dissect,
};

MODULE_DESCRIPTION("DSA tag driver for NXP NETC switch family");
MODULE_LICENSE("GPL");

MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_NETC, NETC_NAME);
module_dsa_tag_driver(netc_netdev_ops);