15037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli/* 25037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * Broadcom tag support 35037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * 45037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * Copyright (C) 2014 Broadcom Corporation 55037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * 65037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * This program is free software; you can redistribute it and/or modify 75037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * it under the terms of the GNU General Public License as published by 85037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * the Free Software Foundation; either version 2 of the License, or 95037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * (at your option) any later version. 105037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli */ 115037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 125037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#include <linux/etherdevice.h> 135037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#include <linux/list.h> 145037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#include <linux/slab.h> 155037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#include "dsa_priv.h" 165037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 175037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli/* This tag length is 4 bytes, older ones were 6 bytes, we do not 185037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * handle them 195037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli */ 205037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_TAG_LEN 4 215037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 225037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli/* Tag is constructed and desconstructed using byte by byte access 235037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * because the tag is placed after the MAC Source Address, which does 245037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * not make it 4-bytes aligned, so this might cause unaligned accesses 255037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * on most systems where this is used. 265037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli */ 275037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 285037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli/* Ingress and egress opcodes */ 295037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_OPCODE_SHIFT 5 305037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_OPCODE_MASK 0x7 315037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 325037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli/* Ingress fields */ 335037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli/* 1st byte in the tag */ 345037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_IG_TC_SHIFT 2 355037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_IG_TC_MASK 0x7 365037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli/* 2nd byte in the tag */ 375037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_IG_TE_MASK 0x3 385037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_IG_TS_SHIFT 7 395037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli/* 3rd byte in the tag */ 405037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_IG_DSTMAP2_MASK 1 415037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_IG_DSTMAP1_MASK 0xff 425037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 435037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli/* Egress fields */ 445037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 455037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli/* 2nd byte in the tag */ 465037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_CID_MASK 0xff 475037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 485037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli/* 3rd byte in the tag */ 495037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_RC_MASK 0xff 505037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_RC_RSVD (3 << 6) 515037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_RC_EXCEPTION (1 << 5) 525037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_RC_PROT_SNOOP (1 << 4) 535037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_RC_PROT_TERM (1 << 3) 545037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_RC_SWITCH (1 << 2) 555037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_RC_MAC_LEARN (1 << 1) 565037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_RC_MIRROR (1 << 0) 575037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_TC_SHIFT 5 585037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_TC_MASK 0x7 595037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli#define BRCM_EG_PID_MASK 0x1f 605037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 615037d532b83d7325a2743dffe82882a64697a8e8Florian Fainellistatic netdev_tx_t brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev) 625037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli{ 635037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli struct dsa_slave_priv *p = netdev_priv(dev); 645037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli u8 *brcm_tag; 655037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 665037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli dev->stats.tx_packets++; 675037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli dev->stats.tx_bytes += skb->len; 685037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 695037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli if (skb_cow_head(skb, BRCM_TAG_LEN) < 0) 705037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli goto out_free; 715037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 725037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli skb_push(skb, BRCM_TAG_LEN); 735037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 745037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli memmove(skb->data, skb->data + BRCM_TAG_LEN, 2 * ETH_ALEN); 755037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 765037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli /* Build the tag after the MAC Source Address */ 775037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli brcm_tag = skb->data + 2 * ETH_ALEN; 785037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 795037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli /* Set the ingress opcode, traffic class, tag enforcment is 805037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * deprecated 815037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli */ 825037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli brcm_tag[0] = (1 << BRCM_OPCODE_SHIFT) | 835037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli ((skb->priority << BRCM_IG_TC_SHIFT) & BRCM_IG_TC_MASK); 845037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli brcm_tag[1] = 0; 855037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli brcm_tag[2] = 0; 865037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli if (p->port == 8) 875037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli brcm_tag[2] = BRCM_IG_DSTMAP2_MASK; 885037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli brcm_tag[3] = (1 << p->port) & BRCM_IG_DSTMAP1_MASK; 895037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 905037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli /* Queue the SKB for transmission on the parent interface, but 915037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * do not modify its EtherType 925037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli */ 935037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli skb->dev = p->parent->dst->master_netdev; 945037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli dev_queue_xmit(skb); 955037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 965037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli return NETDEV_TX_OK; 975037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 985037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelliout_free: 995037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli kfree_skb(skb); 1005037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli return NETDEV_TX_OK; 1015037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli} 1025037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1035037d532b83d7325a2743dffe82882a64697a8e8Florian Fainellistatic int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, 1045037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli struct packet_type *pt, struct net_device *orig_dev) 1055037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli{ 1065037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli struct dsa_switch_tree *dst = dev->dsa_ptr; 1075037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli struct dsa_switch *ds; 1085037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli int source_port; 1095037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli u8 *brcm_tag; 1105037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1115037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli if (unlikely(dst == NULL)) 1125037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli goto out_drop; 1135037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1145037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli ds = dst->ds[0]; 1155037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1165037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli skb = skb_unshare(skb, GFP_ATOMIC); 1175037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli if (skb == NULL) 1185037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli goto out; 1195037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1205037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN))) 1215037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli goto out_drop; 1225037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1235037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli /* skb->data points to the EtherType, the tag is right before it */ 1245037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli brcm_tag = skb->data - 2; 1255037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1265037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli /* The opcode should never be different than 0b000 */ 1275037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK)) 1285037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli goto out_drop; 1295037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1305037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli /* We should never see a reserved reason code without knowing how to 1315037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli * handle it 1325037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli */ 1335037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli WARN_ON(brcm_tag[2] & BRCM_EG_RC_RSVD); 1345037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1355037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli /* Locate which port this is coming from */ 1365037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli source_port = brcm_tag[3] & BRCM_EG_PID_MASK; 1375037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1385037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli /* Validate port against switch setup, either the port is totally */ 1395037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) 1405037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli goto out_drop; 1415037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1425037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli /* Remove Broadcom tag and update checksum */ 1435037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli skb_pull_rcsum(skb, BRCM_TAG_LEN); 1445037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1455037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli /* Move the Ethernet DA and SA */ 1465037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli memmove(skb->data - ETH_HLEN, 1475037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli skb->data - ETH_HLEN - BRCM_TAG_LEN, 1485037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 2 * ETH_ALEN); 1495037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1505037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli skb_push(skb, ETH_HLEN); 1515037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli skb->pkt_type = PACKET_HOST; 1525037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli skb->dev = ds->ports[source_port]; 1535037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli skb->protocol = eth_type_trans(skb, skb->dev); 1545037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1555037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli skb->dev->stats.rx_packets++; 1565037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli skb->dev->stats.rx_bytes += skb->len; 1575037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1585037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli netif_receive_skb(skb); 1595037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1605037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli return 0; 1615037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1625037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelliout_drop: 1635037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli kfree_skb(skb); 1645037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelliout: 1655037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli return 0; 1665037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli} 1675037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli 1685037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelliconst struct dsa_device_ops brcm_netdev_ops = { 1695037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli .xmit = brcm_tag_xmit, 1705037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli .rcv = brcm_tag_rcv, 1715037d532b83d7325a2743dffe82882a64697a8e8Florian Fainelli}; 172