diff mbox

[OpenWrt-Devel,2/3] b53: add logic to handle the broadcom header

Message ID 1424877864-30726-2-git-send-email-ardeleanalex@gmail.com
State Rejected
Delegated to: Jonas Gorski
Headers show

Commit Message

Alexandru Ardelean Feb. 25, 2015, 3:24 p.m. UTC
Feature implemented and tested on BCM53128.

This will enable the processing of the Broadcom Tag/Header,
which will insert 4 bytes between the MAC header and EtherType field.

Note that b53_enable_brcm_hdr(dev) is called before
b53_enable_management(dev), since it seems that the CPU port
may be disabled after b53_enable_brcm_hdr(dev).

Two important notes about this logic:
- Adding 4 bytes in place of the EtherType field may confuses some
  ethernet hardware chips, causing them to stop padding packets
  up to 64 bytes (min frame size).
  In that case, CONFIG_B53_HDR_TX_SW_PADDING may be enabled
- You should increase the MTU size of the same ethernet chip to
  MTU size + 4 bytes in '/etc/config/network'.
  This is such that packets from the switch chip do not get dropped
  by the ethernet chip.
  Try more than 4 bytes since the ethernet driver may do some of it's own
  MTU & frame size computation that needs some sizes to be 8 byte aligned
  when set into it's registers.

Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
---
 .../generic/files/drivers/net/phy/b53/Kconfig      |  21 ++++
 .../generic/files/drivers/net/phy/b53/Makefile     |   1 +
 .../generic/files/drivers/net/phy/b53/b53_common.c |  22 +++-
 .../generic/files/drivers/net/phy/b53/b53_hdr.c    | 129 +++++++++++++++++++++
 .../generic/files/drivers/net/phy/b53/b53_mdio.c   |   7 ++
 .../generic/files/drivers/net/phy/b53/b53_priv.h   |  18 +++
 6 files changed, 193 insertions(+), 5 deletions(-)
 create mode 100644 target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c
diff mbox

Patch

diff --git a/target/linux/generic/files/drivers/net/phy/b53/Kconfig b/target/linux/generic/files/drivers/net/phy/b53/Kconfig
index 545814a..ebf1b91 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/Kconfig
+++ b/target/linux/generic/files/drivers/net/phy/b53/Kconfig
@@ -48,3 +48,24 @@  config B53_HW_DEBUG_FEATURES
 	  So far they've been tested on BCM53128, and should work on BCM53125
 	  since that's a 4 port variant.
 
+config B53_HDR
+	bool "B53 Broadcom Header support"
+	depends on B53
+	select ETHERNET_PACKET_MANGLE
+	default n
+	help
+	  Select to enable the Broadcom tag/header, which will cause the 
+	  switch chip to insert 4 bytes between the MAC fields and
+	  ether-type field.
+
+config B53_HDR_TX_SW_PADDING
+	bool "Pad runt TX packets in software"
+	depends on B53_HDR
+	default n
+	help
+	  Enable this if your MAC driver has issues with adding padding
+	  after the Broadcom Header is added in the packet.
+	  If the packets will be < 64 bytes, they'll be padded up to 64
+	  so that the switch chip does not discard them.
+	  This is only used if the Broadcom Header is enabled via swconfig.
+
diff --git a/target/linux/generic/files/drivers/net/phy/b53/Makefile b/target/linux/generic/files/drivers/net/phy/b53/Makefile
index 7cc39c7..6c809f9 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/Makefile
+++ b/target/linux/generic/files/drivers/net/phy/b53/Makefile
@@ -1,4 +1,5 @@ 
 obj-$(CONFIG_B53)		+= b53_common.o
+obj-$(CONFIG_B53_HDR)		+= b53_hdr.o
 
 obj-$(CONFIG_B53_PHY_FIXUP)	+= b53_phy_fixup.o
 
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
index a7093ee..9459b22 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
@@ -320,7 +320,6 @@  static void b53_enable_vlan(struct b53_device *dev, int enable)
 static void b53_enable_management(struct b53_device *dev)
 {
 	u8 mgmt, gc;
-	u8 brcm_hdr_ctrl;
 
 	/* Management has been disabled by by clearing the SM_SW_FWD_MODE bit
 	 * in b53_enable_vlan() or b53_reset_switch() */
@@ -328,14 +327,12 @@  static void b53_enable_management(struct b53_device *dev)
 		return;
 	b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
 	b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
-	b53_read8(dev, B53_MGMT_PAGE, B53_BRCM_HDR_CTRL, &brcm_hdr_ctrl);
 
 	mgmt |= SM_SW_FWD_MODE;
 	gc |= GC_FRM_MGMT_PORT_MII;
-	/* Chip inserts tag in frame when managed mode is on; not needed yet */
-	brcm_hdr_ctrl &= ~B53_BRCM_HDR_EN;
+	/* Whether the Broadcom tag is (un)set is entirely
+	 * up to the b53_enable_brcm_hdr() function now */
 
-	b53_write8(dev, B53_CTRL_PAGE, B53_BRCM_HDR_CTRL, brcm_hdr_ctrl);
 	b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc);
 	b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
 }
@@ -692,6 +689,7 @@  static int b53_apply(struct b53_device *dev)
 
 	if (!is5325(dev) && !is5365(dev))
 		b53_set_jumbo(dev, dev->enable_jumbo, 1);
+	b53_enable_brcm_hdr(dev);
 	b53_enable_management(dev);
 
 	return 0;
@@ -776,6 +774,8 @@  static int b53_switch_reset(struct b53_device *dev)
 	}
 
 	b53_enable_mib(dev);
+	/* This will clear up a few things when resetting */
+	b53_enable_brcm_hdr(dev);
 
 	return b53_flush_arl(dev);
 }
@@ -1375,6 +1375,9 @@  static int b53_global_reset_switch(struct switch_dev *dev)
 	priv->enable_jumbo = 0;
 	priv->allow_vid_4095 = 0;
 	priv->enable_management = 0;
+#ifdef CONFIG_B53_HDR
+	priv->enable_brcm_hdr = 0;
+#endif
 
 	memset(priv->vlans, 0, sizeof(priv->vlans) * dev->vlans);
 	memset(priv->ports, 0, sizeof(priv->ports) * dev->ports);
@@ -1550,6 +1553,15 @@  static struct switch_attr b53_global_ops[] = {
 		.get = b53_global_get_management,
 		.max = 1,
 	},
+#ifdef CONFIG_B53_HDR
+	{
+		.type = SWITCH_TYPE_INT,
+		.name = "enable_brcm_hdr",
+		.description = "Enable Broadcom Header",
+		.set = b53_global_set_brcm_hdr,
+		.get = b53_global_get_brcm_hdr,
+	},
+#endif
 #ifdef CONFIG_B53_HW_DEBUG_FEATURES
 	{
 		.type = SWITCH_TYPE_STRING,
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c b/target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c
new file mode 100644
index 0000000..2a562a9
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c
@@ -0,0 +1,129 @@ 
+/*
+ * B53 switch driver logic for the Broadcom Header/Tag
+ *
+ * Copyright (C) 2015 Alexandru Ardelean <ardeleanalex@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/platform_data/b53.h>
+#include <linux/rtnetlink.h>
+
+#include "b53_regs.h"
+#include "b53_priv.h"
+
+/* This tag length is 4 bytes, older ones were 6 bytes, we do not
+ * handle them
+ */
+#define BRCM_HDR_LEN    4
+#define MIN_FRAME_LEN   64
+
+static struct sk_buff *b53_mangle_tx(struct net_device *dev, struct sk_buff *skb)
+{
+	if (unlikely(skb_headroom(skb) < BRCM_HDR_LEN)) {
+		if (pskb_expand_head(skb, BRCM_HDR_LEN, 0, GFP_ATOMIC) < 0)
+			goto out_err;
+	}
+
+	skb_push(skb, BRCM_HDR_LEN);
+
+	memmove(skb->data, skb->data + BRCM_HDR_LEN, 2 * ETH_ALEN);
+
+	/* Build the tag after the MAC Source Address */
+	memset(skb->data + 2 * ETH_ALEN, 0, BRCM_HDR_LEN);
+#ifdef CONFIG_B53_HDR_TX_SW_PADDING
+	/* FIXME: we're doing some padding here for runt ( < 64 bytes) packets;
+	 *        some drivers/hw refuse to add hw padding for us after we add
+	 *        the Broadcom tag in there, possibly because it's a invalid/null
+	 *        ethertype it sees;
+	 */
+	if (unlikely(skb->len < MIN_FRAME_LEN)) {
+		u8 *data;
+		int diff = (MIN_FRAME_LEN - skb->len);
+		if (skb_tailroom(skb) < diff)
+			pskb_expand_head(skb, 0, diff, GFP_ATOMIC);
+		data = skb_put(skb, diff);
+		memset(data, 0, diff);
+	}
+#endif
+
+	return skb;
+
+out_err:
+	dev_kfree_skb_any(skb);
+	return NULL;
+}
+
+static void b53_mangle_rx(struct net_device *dev, struct sk_buff *skb)
+{
+	skb_pull(skb, BRCM_HDR_LEN);
+	memmove(skb->data,
+		skb->data - BRCM_HDR_LEN,
+		2 * ETH_ALEN);
+}
+
+void b53_enable_brcm_hdr(struct b53_device *dev)
+{
+	u8 brcm_hdr_ctrl;
+
+	b53_read8(dev, B53_MGMT_PAGE, B53_BRCM_HDR_CTRL, &brcm_hdr_ctrl);
+	if (!dev->enable_brcm_hdr && dev->eth_dev) {
+		dev->eth_dev->phy_ptr = NULL;
+		dev->eth_dev->priv_flags &= ~IFF_NO_IP_ALIGN;
+		dev->eth_dev->eth_mangle_rx = NULL;
+		dev->eth_dev->eth_mangle_tx = NULL;
+		dev->eth_dev = NULL;
+	}
+	brcm_hdr_ctrl &= ~B53_BRCM_HDR_EN;
+	if (!dev->eth_dev) {
+		goto out;
+	}
+
+	/* Broadcom Header work in hw if managed mode is not enabled */
+	if (!dev->enable_management)
+		goto out;
+
+	dev->eth_dev->phy_ptr = dev;
+	dev->eth_dev->priv_flags |= IFF_NO_IP_ALIGN;
+	dev->eth_dev->eth_mangle_rx = b53_mangle_rx;
+	dev->eth_dev->eth_mangle_tx = b53_mangle_tx;
+	brcm_hdr_ctrl |= B53_BRCM_HDR_EN;
+	pr_info("%s: attached broadcom tag mangle code to %s\n",
+	        dev->sw_dev.name, dev->eth_dev->name);
+out:
+
+	b53_write8(dev, B53_MGMT_PAGE, B53_BRCM_HDR_CTRL, brcm_hdr_ctrl);
+}
+
+int b53_global_get_brcm_hdr(struct switch_dev *dev,
+		const struct switch_attr *attr,
+		struct switch_val *val)
+{
+	struct b53_device *priv = sw_to_b53(dev);
+
+	val->value.i = priv->enable_brcm_hdr;
+
+	return 0;
+}
+
+int b53_global_set_brcm_hdr(struct switch_dev *dev,
+		const struct switch_attr *attr,
+		struct switch_val *val)
+{
+	struct b53_device *priv = sw_to_b53(dev);
+
+	priv->enable_brcm_hdr = !!(val->value.i && !priv->eth_dev);
+
+	return 0;
+}
+
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c b/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c
index 3c25f0e..a3e8052 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c
@@ -293,6 +293,9 @@  static int b53_phy_config_init(struct phy_device *phydev)
 	dev->current_page = 0xff;
 	/* force the ethX as alias */
 	dev->sw_dev.alias = phydev->attached_dev->name;
+#ifdef CONFIG_B53_HDR
+	dev->eth_dev = phydev->attached_dev;
+#endif
 
 	ret = b53_switch_register(dev);
 	if (ret) {
@@ -312,6 +315,10 @@  static void b53_phy_remove(struct phy_device *phydev)
 	if (!priv)
 		return;
 
+#ifdef CONFIG_B53_HDR
+	priv->eth_dev = NULL;
+#endif
+
 	b53_switch_remove(priv);
 
 	phydev->priv = NULL;
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
index 15df201..f487bf2 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
@@ -23,6 +23,7 @@ 
 #include <linux/mutex.h>
 #include <linux/switch.h>
 #include <linux/if_ether.h>
+#include <linux/netdevice.h>
 
 struct b53_device;
 
@@ -142,6 +143,11 @@  struct b53_device {
 	struct b53_arl_ops *arl_ops;
 #endif
 
+#ifdef CONFIG_B53_HDR
+	struct net_device *eth_dev;
+	unsigned enable_brcm_hdr:1;
+#endif
+
 	struct b53_port *ports;
 	struct b53_vlan *vlans;
 
@@ -345,6 +351,18 @@  static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg,
 	return ret;
 }
 
+#ifdef CONFIG_B53_HDR
+void b53_enable_brcm_hdr(struct b53_device *dev);
+int b53_global_get_brcm_hdr(struct switch_dev *dev,
+		const struct switch_attr *attr,
+		struct switch_val *val);
+int b53_global_set_brcm_hdr(struct switch_dev *dev,
+		const struct switch_attr *attr,
+		struct switch_val *val);
+#else
+#define b53_enable_brcm_hdr(x)
+#endif
+
 #ifdef CONFIG_BCM47XX
 
 #include <bcm47xx_nvram.h>