| // 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 "dsa_priv.h" |
| |
| #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_SHIFT 1 |
| #define GSWIP_TX_PORT_MAP_MASK 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) |
| { |
| struct dsa_port *dp = dsa_slave_to_port(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] = BIT(dp->index + GSWIP_TX_PORT_MAP_SHIFT) & GSWIP_TX_PORT_MAP_MASK; |
| 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_master_find_slave(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", |
| .proto = DSA_TAG_PROTO_GSWIP, |
| .xmit = gswip_tag_xmit, |
| .rcv = gswip_tag_rcv, |
| .needed_headroom = GSWIP_RX_HEADER_LEN, |
| }; |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_GSWIP); |
| |
| module_dsa_tag_driver(gswip_netdev_ops); |