ipq40xx: include ipq40xx-ized qca8k version
[openwrt/staging/chunkeey.git] / target / linux / ipq40xx / files / net / dsa / tag_qca.c
diff --git a/target/linux/ipq40xx/files/net/dsa/tag_qca.c b/target/linux/ipq40xx/files/net/dsa/tag_qca.c
new file mode 100644 (file)
index 0000000..6156a9d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/etherdevice.h>
+
+#include "dsa_priv.h"
+
+/* Both the IPQESS (essedma) + ESS-Switch Cores are part of the
+ * IPQ40XX SoC. Because of their "proximity" the ethernet rx and
+ * tx descriptor have dedicated fields set aside for the port id.
+ */
+static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct dsa_port *dp = dsa_slave_to_port(dev);
+
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += skb->len;
+
+       skb->dev_scratch = BIT(dp->index);
+
+       return skb;
+}
+
+static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev,
+                                  struct packet_type *pt)
+{
+       int port;
+       __le16 *rrd1;
+
+#define EDMA_PORT_ID_SHIFT     12
+#define EDMA_PORT_ID_MASK      0x7
+
+       /* port_id is part of the hardware's rx descriptor (2nd word)
+        * to access it, we have to be a little naughty and access the
+        * data that comes "in front of the start of the frame".
+        */
+       rrd1 = (__le16 *)(skb->data - 2 - 6 - 6 - 14);
+
+       port = (le16_to_cpu(*rrd1) >> EDMA_PORT_ID_SHIFT) & EDMA_PORT_ID_MASK;
+
+       skb->dev = dsa_master_find_slave(dev, 0, port);
+       if (!skb->dev)
+               return NULL;
+
+       return skb;
+}
+
+const struct dsa_device_ops qca_netdev_ops = {
+       .xmit   = qca_tag_xmit,
+       .rcv    = qca_tag_rcv,
+};