root/net/dsa/tag_gswip.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Intel / Lantiq GSWIP V2.0 PMAC tag support
 *
 * Copyright (C) 2017 - 2018 Hauke Mehrtens <hauke@hauke-m.de>
 */

#include <linux/bitops.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <net/dsa.h>

#include "tag.h"

#define GSWIP_NAME                      "gswip"

#define GSWIP_TX_HEADER_LEN             4

/* special tag in TX path header */
/* Byte 0 */
#define GSWIP_TX_SLPID_SHIFT            0       /* source port ID */
#define  GSWIP_TX_SLPID_CPU             2
#define  GSWIP_TX_SLPID_APP1            3
#define  GSWIP_TX_SLPID_APP2            4
#define  GSWIP_TX_SLPID_APP3            5
#define  GSWIP_TX_SLPID_APP4            6
#define  GSWIP_TX_SLPID_APP5            7

/* Byte 1 */
#define GSWIP_TX_CRCGEN_DIS             BIT(7)
#define GSWIP_TX_DPID_SHIFT             0       /* destination group ID */
#define  GSWIP_TX_DPID_ELAN             0
#define  GSWIP_TX_DPID_EWAN             1
#define  GSWIP_TX_DPID_CPU              2
#define  GSWIP_TX_DPID_APP1             3
#define  GSWIP_TX_DPID_APP2             4
#define  GSWIP_TX_DPID_APP3             5
#define  GSWIP_TX_DPID_APP4             6
#define  GSWIP_TX_DPID_APP5             7

/* Byte 2 */
#define GSWIP_TX_PORT_MAP_EN            BIT(7)
#define GSWIP_TX_PORT_MAP_SEL           BIT(6)
#define GSWIP_TX_LRN_DIS                BIT(5)
#define GSWIP_TX_CLASS_EN               BIT(4)
#define GSWIP_TX_CLASS_SHIFT            0
#define GSWIP_TX_CLASS_MASK             GENMASK(3, 0)

/* Byte 3 */
#define GSWIP_TX_DPID_EN                BIT(0)
#define GSWIP_TX_PORT_MAP               GENMASK(6, 1)

#define GSWIP_RX_HEADER_LEN     8

/* special tag in RX path header */
/* Byte 7 */
#define GSWIP_RX_SPPID_SHIFT            4
#define GSWIP_RX_SPPID_MASK             GENMASK(6, 4)

static struct sk_buff *gswip_tag_xmit(struct sk_buff *skb,
                                      struct net_device *dev)
{
        u8 *gswip_tag;

        skb_push(skb, GSWIP_TX_HEADER_LEN);

        gswip_tag = skb->data;
        gswip_tag[0] = GSWIP_TX_SLPID_CPU;
        gswip_tag[1] = GSWIP_TX_DPID_ELAN;
        gswip_tag[2] = GSWIP_TX_PORT_MAP_EN | GSWIP_TX_PORT_MAP_SEL;
        gswip_tag[3] = FIELD_PREP(GSWIP_TX_PORT_MAP, dsa_xmit_port_mask(skb, dev));
        gswip_tag[3] |= GSWIP_TX_DPID_EN;

        return skb;
}

static struct sk_buff *gswip_tag_rcv(struct sk_buff *skb,
                                     struct net_device *dev)
{
        int port;
        u8 *gswip_tag;

        if (unlikely(!pskb_may_pull(skb, GSWIP_RX_HEADER_LEN)))
                return NULL;

        gswip_tag = skb->data - ETH_HLEN;

        /* Get source port information */
        port = (gswip_tag[7] & GSWIP_RX_SPPID_MASK) >> GSWIP_RX_SPPID_SHIFT;
        skb->dev = dsa_conduit_find_user(dev, 0, port);
        if (!skb->dev)
                return NULL;

        /* remove GSWIP tag */
        skb_pull_rcsum(skb, GSWIP_RX_HEADER_LEN);

        return skb;
}

static const struct dsa_device_ops gswip_netdev_ops = {
        .name = GSWIP_NAME,
        .proto  = DSA_TAG_PROTO_GSWIP,
        .xmit = gswip_tag_xmit,
        .rcv = gswip_tag_rcv,
        .needed_headroom = GSWIP_RX_HEADER_LEN,
};

MODULE_DESCRIPTION("DSA tag driver for Lantiq / Intel GSWIP switches");
MODULE_LICENSE("GPL");
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_GSWIP, GSWIP_NAME);

module_dsa_tag_driver(gswip_netdev_ops);