From e175b199f8b5947ee498d049b2f4dff694e08e72 Mon Sep 17 00:00:00 2001
From: =?utf8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Thu, 23 Apr 2015 19:11:11 +0000
Subject: [PATCH] kernel: backport rest of bgmac patches to 4.0
MIME-Version: 1.0
Content-Type: text/plain; charset=utf8
Content-Transfer-Encoding: 8bit

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>

SVN-Revision: 45573
---
 ...rrupts-disabled-as-long-as-there-is-.patch |  87 +++++++++
 ...received-skb-headroom-to-NET_SKB_PAD.patch |  66 +++++++
 ...bgmac-simplify-rx-DMA-error-handling.patch | 130 +++++++++++++
 ...gmac-add-check-for-oversized-packets.patch |  27 +++
 ...ncrease-rx-ring-size-from-511-to-512.patch |  23 +++
 ...2-10-bgmac-simplify-dma-init-cleanup.patch | 184 ++++++++++++++++++
 .../072-11-bgmac-fix-DMA-rx-corruption.patch  |  88 +++++++++
 .../072-12-bgmac-drop-ring-num_slots.patch    | 132 +++++++++++++
 ...fix-MAC-soft-reset-bit-for-corerev-4.patch |  24 +++
 ...bgmac-reset-all-4-GMAC-cores-on-init.patch |  28 +++
 ...ts-for-extra-polling-calls-from-NAPI.patch |  35 ++++
 11 files changed, 824 insertions(+)
 create mode 100644 target/linux/generic/patches-4.0/072-05-bgmac-leave-interrupts-disabled-as-long-as-there-is-.patch
 create mode 100644 target/linux/generic/patches-4.0/072-06-bgmac-set-received-skb-headroom-to-NET_SKB_PAD.patch
 create mode 100644 target/linux/generic/patches-4.0/072-07-bgmac-simplify-rx-DMA-error-handling.patch
 create mode 100644 target/linux/generic/patches-4.0/072-08-bgmac-add-check-for-oversized-packets.patch
 create mode 100644 target/linux/generic/patches-4.0/072-09-bgmac-increase-rx-ring-size-from-511-to-512.patch
 create mode 100644 target/linux/generic/patches-4.0/072-10-bgmac-simplify-dma-init-cleanup.patch
 create mode 100644 target/linux/generic/patches-4.0/072-11-bgmac-fix-DMA-rx-corruption.patch
 create mode 100644 target/linux/generic/patches-4.0/072-12-bgmac-drop-ring-num_slots.patch
 create mode 100644 target/linux/generic/patches-4.0/072-13-bgmac-fix-MAC-soft-reset-bit-for-corerev-4.patch
 create mode 100644 target/linux/generic/patches-4.0/072-14-bgmac-reset-all-4-GMAC-cores-on-init.patch
 create mode 100644 target/linux/generic/patches-4.0/072-15-bgmac-fix-requests-for-extra-polling-calls-from-NAPI.patch

diff --git a/target/linux/generic/patches-4.0/072-05-bgmac-leave-interrupts-disabled-as-long-as-there-is-.patch b/target/linux/generic/patches-4.0/072-05-bgmac-leave-interrupts-disabled-as-long-as-there-is-.patch
new file mode 100644
index 0000000000..3bbe9ea9cb
--- /dev/null
+++ b/target/linux/generic/patches-4.0/072-05-bgmac-leave-interrupts-disabled-as-long-as-there-is-.patch
@@ -0,0 +1,87 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 12 Apr 2015 10:08:04 +0200
+Subject: [PATCH] bgmac: leave interrupts disabled as long as there is work
+ to do
+
+Always poll rx and tx during NAPI poll instead of relying on the status
+of the first interrupt. This prevents bgmac_poll from leaving unfinished
+work around until the next IRQ.
+In my tests this makes bridging/routing throughput under heavy load more
+stable and ensures that no new IRQs arrive as long as bgmac_poll uses up
+the entire budget.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -1109,8 +1109,6 @@ static void bgmac_chip_reset(struct bgma
+ 	bgmac_phy_init(bgmac);
+ 
+ 	netdev_reset_queue(bgmac->net_dev);
+-
+-	bgmac->int_status = 0;
+ }
+ 
+ static void bgmac_chip_intrs_on(struct bgmac *bgmac)
+@@ -1225,14 +1223,13 @@ static irqreturn_t bgmac_interrupt(int i
+ 	if (!int_status)
+ 		return IRQ_NONE;
+ 
+-	/* Ack */
+-	bgmac_write(bgmac, BGMAC_INT_STATUS, int_status);
++	int_status &= ~(BGMAC_IS_TX0 | BGMAC_IS_RX);
++	if (int_status)
++		bgmac_err(bgmac, "Unknown IRQs: 0x%08X\n", int_status);
+ 
+ 	/* Disable new interrupts until handling existing ones */
+ 	bgmac_chip_intrs_off(bgmac);
+ 
+-	bgmac->int_status = int_status;
+-
+ 	napi_schedule(&bgmac->napi);
+ 
+ 	return IRQ_HANDLED;
+@@ -1241,25 +1238,17 @@ static irqreturn_t bgmac_interrupt(int i
+ static int bgmac_poll(struct napi_struct *napi, int weight)
+ {
+ 	struct bgmac *bgmac = container_of(napi, struct bgmac, napi);
+-	struct bgmac_dma_ring *ring;
+ 	int handled = 0;
+ 
+-	if (bgmac->int_status & BGMAC_IS_TX0) {
+-		ring = &bgmac->tx_ring[0];
+-		bgmac_dma_tx_free(bgmac, ring);
+-		bgmac->int_status &= ~BGMAC_IS_TX0;
+-	}
++	/* Ack */
++	bgmac_write(bgmac, BGMAC_INT_STATUS, ~0);
+ 
+-	if (bgmac->int_status & BGMAC_IS_RX) {
+-		ring = &bgmac->rx_ring[0];
+-		handled += bgmac_dma_rx_read(bgmac, ring, weight);
+-		bgmac->int_status &= ~BGMAC_IS_RX;
+-	}
++	bgmac_dma_tx_free(bgmac, &bgmac->tx_ring[0]);
++	handled += bgmac_dma_rx_read(bgmac, &bgmac->rx_ring[0], weight);
+ 
+-	if (bgmac->int_status) {
+-		bgmac_err(bgmac, "Unknown IRQs: 0x%08X\n", bgmac->int_status);
+-		bgmac->int_status = 0;
+-	}
++	/* Poll again if more events arrived in the meantime */
++	if (bgmac_read(bgmac, BGMAC_INT_STATUS) & (BGMAC_IS_TX0 | BGMAC_IS_RX))
++		return handled;
+ 
+ 	if (handled < weight) {
+ 		napi_complete(napi);
+--- a/drivers/net/ethernet/broadcom/bgmac.h
++++ b/drivers/net/ethernet/broadcom/bgmac.h
+@@ -452,7 +452,6 @@ struct bgmac {
+ 
+ 	/* Int */
+ 	u32 int_mask;
+-	u32 int_status;
+ 
+ 	/* Current MAC state */
+ 	int mac_speed;
diff --git a/target/linux/generic/patches-4.0/072-06-bgmac-set-received-skb-headroom-to-NET_SKB_PAD.patch b/target/linux/generic/patches-4.0/072-06-bgmac-set-received-skb-headroom-to-NET_SKB_PAD.patch
new file mode 100644
index 0000000000..2c490efbbb
--- /dev/null
+++ b/target/linux/generic/patches-4.0/072-06-bgmac-set-received-skb-headroom-to-NET_SKB_PAD.patch
@@ -0,0 +1,66 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 12 Apr 2015 10:13:28 +0200
+Subject: [PATCH] bgmac: set received skb headroom to NET_SKB_PAD
+
+A packet buffer offset of 30 bytes is inefficient, because the first 2
+bytes end up in a different cacheline.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -346,13 +346,13 @@ static int bgmac_dma_rx_skb_for_slot(str
+ 		return -ENOMEM;
+ 
+ 	/* Poison - if everything goes fine, hardware will overwrite it */
+-	rx = buf;
++	rx = buf + BGMAC_RX_BUF_OFFSET;
+ 	rx->len = cpu_to_le16(0xdead);
+ 	rx->flags = cpu_to_le16(0xbeef);
+ 
+ 	/* Map skb for the DMA */
+-	dma_addr = dma_map_single(dma_dev, buf, BGMAC_RX_BUF_SIZE,
+-				  DMA_FROM_DEVICE);
++	dma_addr = dma_map_single(dma_dev, buf + BGMAC_RX_BUF_OFFSET,
++				  BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
+ 	if (dma_mapping_error(dma_dev, dma_addr)) {
+ 		bgmac_err(bgmac, "DMA mapping error\n");
+ 		put_page(virt_to_head_page(buf));
+@@ -403,7 +403,7 @@ static int bgmac_dma_rx_read(struct bgma
+ 	while (ring->start != ring->end) {
+ 		struct device *dma_dev = bgmac->core->dma_dev;
+ 		struct bgmac_slot_info *slot = &ring->slots[ring->start];
+-		struct bgmac_rx_header *rx = slot->buf;
++		struct bgmac_rx_header *rx = slot->buf + BGMAC_RX_BUF_OFFSET;
+ 		struct sk_buff *skb;
+ 		void *buf = slot->buf;
+ 		u16 len, flags;
+@@ -454,8 +454,10 @@ static int bgmac_dma_rx_read(struct bgma
+ 					 BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
+ 
+ 			skb = build_skb(buf, BGMAC_RX_ALLOC_SIZE);
+-			skb_put(skb, BGMAC_RX_FRAME_OFFSET + len);
+-			skb_pull(skb, BGMAC_RX_FRAME_OFFSET);
++			skb_put(skb, BGMAC_RX_FRAME_OFFSET +
++				BGMAC_RX_BUF_OFFSET + len);
++			skb_pull(skb, BGMAC_RX_FRAME_OFFSET +
++				 BGMAC_RX_BUF_OFFSET);
+ 
+ 			skb_checksum_none_assert(skb);
+ 			skb->protocol = eth_type_trans(skb, bgmac->net_dev);
+--- a/drivers/net/ethernet/broadcom/bgmac.h
++++ b/drivers/net/ethernet/broadcom/bgmac.h
+@@ -360,9 +360,11 @@
+ 
+ #define BGMAC_RX_HEADER_LEN			28		/* Last 24 bytes are unused. Well... */
+ #define BGMAC_RX_FRAME_OFFSET			30		/* There are 2 unused bytes between header and real data */
++#define BGMAC_RX_BUF_OFFSET			(NET_SKB_PAD + NET_IP_ALIGN - \
++						 BGMAC_RX_FRAME_OFFSET)
+ #define BGMAC_RX_MAX_FRAME_SIZE			1536		/* Copied from b44/tg3 */
+ #define BGMAC_RX_BUF_SIZE			(BGMAC_RX_FRAME_OFFSET + BGMAC_RX_MAX_FRAME_SIZE)
+-#define BGMAC_RX_ALLOC_SIZE			(SKB_DATA_ALIGN(BGMAC_RX_BUF_SIZE) + \
++#define BGMAC_RX_ALLOC_SIZE			(SKB_DATA_ALIGN(BGMAC_RX_BUF_SIZE + BGMAC_RX_BUF_OFFSET) + \
+ 						 SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+ 
+ #define BGMAC_BFL_ENETROBO			0x0010		/* has ephy roboswitch spi */
diff --git a/target/linux/generic/patches-4.0/072-07-bgmac-simplify-rx-DMA-error-handling.patch b/target/linux/generic/patches-4.0/072-07-bgmac-simplify-rx-DMA-error-handling.patch
new file mode 100644
index 0000000000..2ca9d10f63
--- /dev/null
+++ b/target/linux/generic/patches-4.0/072-07-bgmac-simplify-rx-DMA-error-handling.patch
@@ -0,0 +1,130 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 12 Apr 2015 22:23:07 +0200
+Subject: [PATCH] bgmac: simplify/optimize rx DMA error handling
+
+Allocate a new buffer before processing the completed one. If allocation
+fails, reuse the old buffer.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -386,6 +386,19 @@ static void bgmac_dma_rx_setup_desc(stru
+ 	dma_desc->ctl1 = cpu_to_le32(ctl1);
+ }
+ 
++static void bgmac_dma_rx_poison_buf(struct device *dma_dev,
++				    struct bgmac_slot_info *slot)
++{
++	struct bgmac_rx_header *rx = slot->buf + BGMAC_RX_BUF_OFFSET;
++
++	dma_sync_single_for_cpu(dma_dev, slot->dma_addr, BGMAC_RX_BUF_SIZE,
++				DMA_FROM_DEVICE);
++	rx->len = cpu_to_le16(0xdead);
++	rx->flags = cpu_to_le16(0xbeef);
++	dma_sync_single_for_device(dma_dev, slot->dma_addr, BGMAC_RX_BUF_SIZE,
++				   DMA_FROM_DEVICE);
++}
++
+ static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring,
+ 			     int weight)
+ {
+@@ -406,53 +419,35 @@ static int bgmac_dma_rx_read(struct bgma
+ 		struct bgmac_rx_header *rx = slot->buf + BGMAC_RX_BUF_OFFSET;
+ 		struct sk_buff *skb;
+ 		void *buf = slot->buf;
++		dma_addr_t dma_addr = slot->dma_addr;
+ 		u16 len, flags;
+ 
+-		/* Unmap buffer to make it accessible to the CPU */
+-		dma_sync_single_for_cpu(dma_dev, slot->dma_addr,
+-					BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
+-
+-		/* Get info from the header */
+-		len = le16_to_cpu(rx->len);
+-		flags = le16_to_cpu(rx->flags);
+-
+ 		do {
+-			dma_addr_t old_dma_addr = slot->dma_addr;
+-			int err;
++			/* Prepare new skb as replacement */
++			if (bgmac_dma_rx_skb_for_slot(bgmac, slot)) {
++				bgmac_dma_rx_poison_buf(dma_dev, slot);
++				break;
++			}
++
++			/* Unmap buffer to make it accessible to the CPU */
++			dma_unmap_single(dma_dev, dma_addr,
++					 BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
++
++			/* Get info from the header */
++			len = le16_to_cpu(rx->len);
++			flags = le16_to_cpu(rx->flags);
+ 
+ 			/* Check for poison and drop or pass the packet */
+ 			if (len == 0xdead && flags == 0xbeef) {
+ 				bgmac_err(bgmac, "Found poisoned packet at slot %d, DMA issue!\n",
+ 					  ring->start);
+-				dma_sync_single_for_device(dma_dev,
+-							   slot->dma_addr,
+-							   BGMAC_RX_BUF_SIZE,
+-							   DMA_FROM_DEVICE);
++				put_page(virt_to_head_page(buf));
+ 				break;
+ 			}
+ 
+ 			/* Omit CRC. */
+ 			len -= ETH_FCS_LEN;
+ 
+-			/* Prepare new skb as replacement */
+-			err = bgmac_dma_rx_skb_for_slot(bgmac, slot);
+-			if (err) {
+-				/* Poison the old skb */
+-				rx->len = cpu_to_le16(0xdead);
+-				rx->flags = cpu_to_le16(0xbeef);
+-
+-				dma_sync_single_for_device(dma_dev,
+-							   slot->dma_addr,
+-							   BGMAC_RX_BUF_SIZE,
+-							   DMA_FROM_DEVICE);
+-				break;
+-			}
+-			bgmac_dma_rx_setup_desc(bgmac, ring, ring->start);
+-
+-			/* Unmap old skb, we'll pass it to the netfif */
+-			dma_unmap_single(dma_dev, old_dma_addr,
+-					 BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
+-
+ 			skb = build_skb(buf, BGMAC_RX_ALLOC_SIZE);
+ 			skb_put(skb, BGMAC_RX_FRAME_OFFSET +
+ 				BGMAC_RX_BUF_OFFSET + len);
+@@ -465,6 +460,8 @@ static int bgmac_dma_rx_read(struct bgma
+ 			handled++;
+ 		} while (0);
+ 
++		bgmac_dma_rx_setup_desc(bgmac, ring, ring->start);
++
+ 		if (++ring->start >= BGMAC_RX_RING_SLOTS)
+ 			ring->start = 0;
+ 
+@@ -532,14 +529,14 @@ static void bgmac_dma_rx_ring_free(struc
+ 
+ 	for (i = 0; i < ring->num_slots; i++) {
+ 		slot = &ring->slots[i];
+-		if (!slot->buf)
++		if (!slot->dma_addr)
+ 			continue;
+ 
+-		if (slot->dma_addr)
+-			dma_unmap_single(dma_dev, slot->dma_addr,
+-					 BGMAC_RX_BUF_SIZE,
+-					 DMA_FROM_DEVICE);
++		dma_unmap_single(dma_dev, slot->dma_addr,
++				 BGMAC_RX_BUF_SIZE,
++				 DMA_FROM_DEVICE);
+ 		put_page(virt_to_head_page(slot->buf));
++		slot->dma_addr = 0;
+ 	}
+ }
+ 
diff --git a/target/linux/generic/patches-4.0/072-08-bgmac-add-check-for-oversized-packets.patch b/target/linux/generic/patches-4.0/072-08-bgmac-add-check-for-oversized-packets.patch
new file mode 100644
index 0000000000..705aa2d2a4
--- /dev/null
+++ b/target/linux/generic/patches-4.0/072-08-bgmac-add-check-for-oversized-packets.patch
@@ -0,0 +1,27 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 12 Apr 2015 22:28:20 +0200
+Subject: [PATCH] bgmac: add check for oversized packets
+
+In very rare cases, the MAC can catch an internal buffer that is bigger
+than it's supposed to be. Instead of crashing the kernel, simply pass
+the buffer back to the hardware
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -445,6 +445,13 @@ static int bgmac_dma_rx_read(struct bgma
+ 				break;
+ 			}
+ 
++			if (len > BGMAC_RX_ALLOC_SIZE) {
++				bgmac_err(bgmac, "Found oversized packet at slot %d, DMA issue!\n",
++					  ring->start);
++				put_page(virt_to_head_page(buf));
++				break;
++			}
++
+ 			/* Omit CRC. */
+ 			len -= ETH_FCS_LEN;
+ 
diff --git a/target/linux/generic/patches-4.0/072-09-bgmac-increase-rx-ring-size-from-511-to-512.patch b/target/linux/generic/patches-4.0/072-09-bgmac-increase-rx-ring-size-from-511-to-512.patch
new file mode 100644
index 0000000000..4888cc32af
--- /dev/null
+++ b/target/linux/generic/patches-4.0/072-09-bgmac-increase-rx-ring-size-from-511-to-512.patch
@@ -0,0 +1,23 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 12 Apr 2015 22:36:16 +0200
+Subject: [PATCH] bgmac: increase rx ring size from 511 to 512
+
+Limiting it to 511 looks like a failed attempt at leaving one descriptor
+empty to allow the hardware to stop processing a buffer that has not
+been prepared yet. However, this doesn't work because this affects the
+total ring size as well
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/ethernet/broadcom/bgmac.h
++++ b/drivers/net/ethernet/broadcom/bgmac.h
+@@ -356,7 +356,7 @@
+ #define BGMAC_MAX_RX_RINGS			1
+ 
+ #define BGMAC_TX_RING_SLOTS			128
+-#define BGMAC_RX_RING_SLOTS			512 - 1		/* Why -1? Well, Broadcom does that... */
++#define BGMAC_RX_RING_SLOTS			512
+ 
+ #define BGMAC_RX_HEADER_LEN			28		/* Last 24 bytes are unused. Well... */
+ #define BGMAC_RX_FRAME_OFFSET			30		/* There are 2 unused bytes between header and real data */
diff --git a/target/linux/generic/patches-4.0/072-10-bgmac-simplify-dma-init-cleanup.patch b/target/linux/generic/patches-4.0/072-10-bgmac-simplify-dma-init-cleanup.patch
new file mode 100644
index 0000000000..f8d7921159
--- /dev/null
+++ b/target/linux/generic/patches-4.0/072-10-bgmac-simplify-dma-init-cleanup.patch
@@ -0,0 +1,184 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 12 Apr 2015 23:19:32 +0200
+Subject: [PATCH] bgmac: simplify dma init/cleanup
+
+Instead of allocating buffers at device init time and initializing
+descriptors at device open, do both at the same time (during open).
+Free all buffers when closing the device.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -562,18 +562,26 @@ static void bgmac_dma_ring_desc_free(str
+ 			  ring->dma_base);
+ }
+ 
+-static void bgmac_dma_free(struct bgmac *bgmac)
++static void bgmac_dma_cleanup(struct bgmac *bgmac)
+ {
+ 	int i;
+ 
+-	for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) {
++	for (i = 0; i < BGMAC_MAX_TX_RINGS; i++)
+ 		bgmac_dma_tx_ring_free(bgmac, &bgmac->tx_ring[i]);
+-		bgmac_dma_ring_desc_free(bgmac, &bgmac->tx_ring[i]);
+-	}
+-	for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) {
++
++	for (i = 0; i < BGMAC_MAX_RX_RINGS; i++)
+ 		bgmac_dma_rx_ring_free(bgmac, &bgmac->rx_ring[i]);
++}
++
++static void bgmac_dma_free(struct bgmac *bgmac)
++{
++	int i;
++
++	for (i = 0; i < BGMAC_MAX_TX_RINGS; i++)
++		bgmac_dma_ring_desc_free(bgmac, &bgmac->tx_ring[i]);
++
++	for (i = 0; i < BGMAC_MAX_RX_RINGS; i++)
+ 		bgmac_dma_ring_desc_free(bgmac, &bgmac->rx_ring[i]);
+-	}
+ }
+ 
+ static int bgmac_dma_alloc(struct bgmac *bgmac)
+@@ -621,8 +629,6 @@ static int bgmac_dma_alloc(struct bgmac
+ 	}
+ 
+ 	for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) {
+-		int j;
+-
+ 		ring = &bgmac->rx_ring[i];
+ 		ring->num_slots = BGMAC_RX_RING_SLOTS;
+ 		ring->mmio_base = ring_base[i];
+@@ -645,15 +651,6 @@ static int bgmac_dma_alloc(struct bgmac
+ 			ring->index_base = lower_32_bits(ring->dma_base);
+ 		else
+ 			ring->index_base = 0;
+-
+-		/* Alloc RX slots */
+-		for (j = 0; j < ring->num_slots; j++) {
+-			err = bgmac_dma_rx_skb_for_slot(bgmac, &ring->slots[j]);
+-			if (err) {
+-				bgmac_err(bgmac, "Can't allocate skb for slot in RX ring\n");
+-				goto err_dma_free;
+-			}
+-		}
+ 	}
+ 
+ 	return 0;
+@@ -663,10 +660,10 @@ err_dma_free:
+ 	return -ENOMEM;
+ }
+ 
+-static void bgmac_dma_init(struct bgmac *bgmac)
++static int bgmac_dma_init(struct bgmac *bgmac)
+ {
+ 	struct bgmac_dma_ring *ring;
+-	int i;
++	int i, err;
+ 
+ 	for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) {
+ 		ring = &bgmac->tx_ring[i];
+@@ -698,8 +695,13 @@ static void bgmac_dma_init(struct bgmac
+ 		if (ring->unaligned)
+ 			bgmac_dma_rx_enable(bgmac, ring);
+ 
+-		for (j = 0; j < ring->num_slots; j++)
++		for (j = 0; j < ring->num_slots; j++) {
++			err = bgmac_dma_rx_skb_for_slot(bgmac, &ring->slots[j]);
++			if (err)
++				goto error;
++
+ 			bgmac_dma_rx_setup_desc(bgmac, ring, j);
++		}
+ 
+ 		bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_INDEX,
+ 			    ring->index_base +
+@@ -708,6 +710,12 @@ static void bgmac_dma_init(struct bgmac
+ 		ring->start = 0;
+ 		ring->end = 0;
+ 	}
++
++	return 0;
++
++error:
++	bgmac_dma_cleanup(bgmac);
++	return err;
+ }
+ 
+ /**************************************************
+@@ -1183,11 +1191,8 @@ static void bgmac_enable(struct bgmac *b
+ }
+ 
+ /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipinit */
+-static void bgmac_chip_init(struct bgmac *bgmac, bool full_init)
++static void bgmac_chip_init(struct bgmac *bgmac)
+ {
+-	struct bgmac_dma_ring *ring;
+-	int i;
+-
+ 	/* 1 interrupt per received frame */
+ 	bgmac_write(bgmac, BGMAC_INT_RECV_LAZY, 1 << BGMAC_IRL_FC_SHIFT);
+ 
+@@ -1205,16 +1210,7 @@ static void bgmac_chip_init(struct bgmac
+ 
+ 	bgmac_write(bgmac, BGMAC_RXMAX_LENGTH, 32 + ETHER_MAX_LEN);
+ 
+-	if (full_init) {
+-		bgmac_dma_init(bgmac);
+-		if (1) /* FIXME: is there any case we don't want IRQs? */
+-			bgmac_chip_intrs_on(bgmac);
+-	} else {
+-		for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) {
+-			ring = &bgmac->rx_ring[i];
+-			bgmac_dma_rx_enable(bgmac, ring);
+-		}
+-	}
++	bgmac_chip_intrs_on(bgmac);
+ 
+ 	bgmac_enable(bgmac);
+ }
+@@ -1274,23 +1270,27 @@ static int bgmac_open(struct net_device
+ 	int err = 0;
+ 
+ 	bgmac_chip_reset(bgmac);
++
++	err = bgmac_dma_init(bgmac);
++	if (err)
++		return err;
++
+ 	/* Specs say about reclaiming rings here, but we do that in DMA init */
+-	bgmac_chip_init(bgmac, true);
++	bgmac_chip_init(bgmac);
+ 
+ 	err = request_irq(bgmac->core->irq, bgmac_interrupt, IRQF_SHARED,
+ 			  KBUILD_MODNAME, net_dev);
+ 	if (err < 0) {
+ 		bgmac_err(bgmac, "IRQ request error: %d!\n", err);
+-		goto err_out;
++		bgmac_dma_cleanup(bgmac);
++		return err;
+ 	}
+ 	napi_enable(&bgmac->napi);
+ 
+ 	phy_start(bgmac->phy_dev);
+ 
+ 	netif_carrier_on(net_dev);
+-
+-err_out:
+-	return err;
++	return 0;
+ }
+ 
+ static int bgmac_stop(struct net_device *net_dev)
+@@ -1306,6 +1306,7 @@ static int bgmac_stop(struct net_device
+ 	free_irq(bgmac->core->irq, net_dev);
+ 
+ 	bgmac_chip_reset(bgmac);
++	bgmac_dma_cleanup(bgmac);
+ 
+ 	return 0;
+ }
diff --git a/target/linux/generic/patches-4.0/072-11-bgmac-fix-DMA-rx-corruption.patch b/target/linux/generic/patches-4.0/072-11-bgmac-fix-DMA-rx-corruption.patch
new file mode 100644
index 0000000000..2e670d89b0
--- /dev/null
+++ b/target/linux/generic/patches-4.0/072-11-bgmac-fix-DMA-rx-corruption.patch
@@ -0,0 +1,88 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 12 Apr 2015 11:59:47 +0200
+Subject: [PATCH] bgmac: fix DMA rx corruption
+
+The driver needs to inform the hardware about the first invalid (not yet
+filled) rx slot, by writing its DMA descriptor pointer offset to the
+BGMAC_DMA_RX_INDEX register.
+
+This register was set to a value exceeding the rx ring size, effectively
+allowing the hardware constant access to the full ring, regardless of
+which slots are initialized.
+
+To fix this issue, always mark the last filled rx slot as invalid.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -366,6 +366,16 @@ static int bgmac_dma_rx_skb_for_slot(str
+ 	return 0;
+ }
+ 
++static void bgmac_dma_rx_update_index(struct bgmac *bgmac,
++				      struct bgmac_dma_ring *ring)
++{
++	wmb();
++
++	bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_INDEX,
++		    ring->index_base +
++		    ring->end * sizeof(struct bgmac_dma_desc));
++}
++
+ static void bgmac_dma_rx_setup_desc(struct bgmac *bgmac,
+ 				    struct bgmac_dma_ring *ring, int desc_idx)
+ {
+@@ -384,6 +394,8 @@ static void bgmac_dma_rx_setup_desc(stru
+ 	dma_desc->addr_high = cpu_to_le32(upper_32_bits(ring->slots[desc_idx].dma_addr));
+ 	dma_desc->ctl0 = cpu_to_le32(ctl0);
+ 	dma_desc->ctl1 = cpu_to_le32(ctl1);
++
++	ring->end = desc_idx;
+ }
+ 
+ static void bgmac_dma_rx_poison_buf(struct device *dma_dev,
+@@ -411,9 +423,7 @@ static int bgmac_dma_rx_read(struct bgma
+ 	end_slot &= BGMAC_DMA_RX_STATDPTR;
+ 	end_slot /= sizeof(struct bgmac_dma_desc);
+ 
+-	ring->end = end_slot;
+-
+-	while (ring->start != ring->end) {
++	while (ring->start != end_slot) {
+ 		struct device *dma_dev = bgmac->core->dma_dev;
+ 		struct bgmac_slot_info *slot = &ring->slots[ring->start];
+ 		struct bgmac_rx_header *rx = slot->buf + BGMAC_RX_BUF_OFFSET;
+@@ -476,6 +486,8 @@ static int bgmac_dma_rx_read(struct bgma
+ 			break;
+ 	}
+ 
++	bgmac_dma_rx_update_index(bgmac, ring);
++
+ 	return handled;
+ }
+ 
+@@ -695,6 +707,8 @@ static int bgmac_dma_init(struct bgmac *
+ 		if (ring->unaligned)
+ 			bgmac_dma_rx_enable(bgmac, ring);
+ 
++		ring->start = 0;
++		ring->end = 0;
+ 		for (j = 0; j < ring->num_slots; j++) {
+ 			err = bgmac_dma_rx_skb_for_slot(bgmac, &ring->slots[j]);
+ 			if (err)
+@@ -703,12 +717,7 @@ static int bgmac_dma_init(struct bgmac *
+ 			bgmac_dma_rx_setup_desc(bgmac, ring, j);
+ 		}
+ 
+-		bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_INDEX,
+-			    ring->index_base +
+-			    ring->num_slots * sizeof(struct bgmac_dma_desc));
+-
+-		ring->start = 0;
+-		ring->end = 0;
++		bgmac_dma_rx_update_index(bgmac, ring);
+ 	}
+ 
+ 	return 0;
diff --git a/target/linux/generic/patches-4.0/072-12-bgmac-drop-ring-num_slots.patch b/target/linux/generic/patches-4.0/072-12-bgmac-drop-ring-num_slots.patch
new file mode 100644
index 0000000000..4cd2e3f80c
--- /dev/null
+++ b/target/linux/generic/patches-4.0/072-12-bgmac-drop-ring-num_slots.patch
@@ -0,0 +1,132 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 12 Apr 2015 23:28:38 +0200
+Subject: [PATCH] bgmac: drop ring->num_slots
+
+The ring size is always known at compile time, so make the code a bit
+more efficient
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -123,7 +123,7 @@ bgmac_dma_tx_add_buf(struct bgmac *bgmac
+ 	struct bgmac_dma_desc *dma_desc;
+ 	u32 ctl1;
+ 
+-	if (i == ring->num_slots - 1)
++	if (i == BGMAC_TX_RING_SLOTS - 1)
+ 		ctl0 |= BGMAC_DESC_CTL0_EOT;
+ 
+ 	ctl1 = len & BGMAC_DESC_CTL1_LEN;
+@@ -382,7 +382,7 @@ static void bgmac_dma_rx_setup_desc(stru
+ 	struct bgmac_dma_desc *dma_desc = ring->cpu_base + desc_idx;
+ 	u32 ctl0 = 0, ctl1 = 0;
+ 
+-	if (desc_idx == ring->num_slots - 1)
++	if (desc_idx == BGMAC_RX_RING_SLOTS - 1)
+ 		ctl0 |= BGMAC_DESC_CTL0_EOT;
+ 	ctl1 |= BGMAC_RX_BUF_SIZE & BGMAC_DESC_CTL1_LEN;
+ 	/* Is there any BGMAC device that requires extension? */
+@@ -521,7 +521,7 @@ static void bgmac_dma_tx_ring_free(struc
+ 	struct bgmac_slot_info *slot;
+ 	int i;
+ 
+-	for (i = 0; i < ring->num_slots; i++) {
++	for (i = 0; i < BGMAC_TX_RING_SLOTS; i++) {
+ 		int len = dma_desc[i].ctl1 & BGMAC_DESC_CTL1_LEN;
+ 
+ 		slot = &ring->slots[i];
+@@ -546,7 +546,7 @@ static void bgmac_dma_rx_ring_free(struc
+ 	struct bgmac_slot_info *slot;
+ 	int i;
+ 
+-	for (i = 0; i < ring->num_slots; i++) {
++	for (i = 0; i < BGMAC_RX_RING_SLOTS; i++) {
+ 		slot = &ring->slots[i];
+ 		if (!slot->dma_addr)
+ 			continue;
+@@ -560,7 +560,8 @@ static void bgmac_dma_rx_ring_free(struc
+ }
+ 
+ static void bgmac_dma_ring_desc_free(struct bgmac *bgmac,
+-				     struct bgmac_dma_ring *ring)
++				     struct bgmac_dma_ring *ring,
++				     int num_slots)
+ {
+ 	struct device *dma_dev = bgmac->core->dma_dev;
+ 	int size;
+@@ -569,7 +570,7 @@ static void bgmac_dma_ring_desc_free(str
+ 	    return;
+ 
+ 	/* Free ring of descriptors */
+-	size = ring->num_slots * sizeof(struct bgmac_dma_desc);
++	size = num_slots * sizeof(struct bgmac_dma_desc);
+ 	dma_free_coherent(dma_dev, size, ring->cpu_base,
+ 			  ring->dma_base);
+ }
+@@ -590,10 +591,12 @@ static void bgmac_dma_free(struct bgmac
+ 	int i;
+ 
+ 	for (i = 0; i < BGMAC_MAX_TX_RINGS; i++)
+-		bgmac_dma_ring_desc_free(bgmac, &bgmac->tx_ring[i]);
++		bgmac_dma_ring_desc_free(bgmac, &bgmac->tx_ring[i],
++					 BGMAC_TX_RING_SLOTS);
+ 
+ 	for (i = 0; i < BGMAC_MAX_RX_RINGS; i++)
+-		bgmac_dma_ring_desc_free(bgmac, &bgmac->rx_ring[i]);
++		bgmac_dma_ring_desc_free(bgmac, &bgmac->rx_ring[i],
++					 BGMAC_RX_RING_SLOTS);
+ }
+ 
+ static int bgmac_dma_alloc(struct bgmac *bgmac)
+@@ -616,11 +619,10 @@ static int bgmac_dma_alloc(struct bgmac
+ 
+ 	for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) {
+ 		ring = &bgmac->tx_ring[i];
+-		ring->num_slots = BGMAC_TX_RING_SLOTS;
+ 		ring->mmio_base = ring_base[i];
+ 
+ 		/* Alloc ring of descriptors */
+-		size = ring->num_slots * sizeof(struct bgmac_dma_desc);
++		size = BGMAC_TX_RING_SLOTS * sizeof(struct bgmac_dma_desc);
+ 		ring->cpu_base = dma_zalloc_coherent(dma_dev, size,
+ 						     &ring->dma_base,
+ 						     GFP_KERNEL);
+@@ -642,11 +644,10 @@ static int bgmac_dma_alloc(struct bgmac
+ 
+ 	for (i = 0; i < BGMAC_MAX_RX_RINGS; i++) {
+ 		ring = &bgmac->rx_ring[i];
+-		ring->num_slots = BGMAC_RX_RING_SLOTS;
+ 		ring->mmio_base = ring_base[i];
+ 
+ 		/* Alloc ring of descriptors */
+-		size = ring->num_slots * sizeof(struct bgmac_dma_desc);
++		size = BGMAC_RX_RING_SLOTS * sizeof(struct bgmac_dma_desc);
+ 		ring->cpu_base = dma_zalloc_coherent(dma_dev, size,
+ 						     &ring->dma_base,
+ 						     GFP_KERNEL);
+@@ -709,7 +710,7 @@ static int bgmac_dma_init(struct bgmac *
+ 
+ 		ring->start = 0;
+ 		ring->end = 0;
+-		for (j = 0; j < ring->num_slots; j++) {
++		for (j = 0; j < BGMAC_RX_RING_SLOTS; j++) {
+ 			err = bgmac_dma_rx_skb_for_slot(bgmac, &ring->slots[j]);
+ 			if (err)
+ 				goto error;
+--- a/drivers/net/ethernet/broadcom/bgmac.h
++++ b/drivers/net/ethernet/broadcom/bgmac.h
+@@ -419,11 +419,10 @@ struct bgmac_dma_ring {
+ 	u32 start;
+ 	u32 end;
+ 
+-	u16 num_slots;
+-	u16 mmio_base;
+ 	struct bgmac_dma_desc *cpu_base;
+ 	dma_addr_t dma_base;
+ 	u32 index_base; /* Used for unaligned rings only, otherwise 0 */
++	u16 mmio_base;
+ 	bool unaligned;
+ 
+ 	struct bgmac_slot_info slots[BGMAC_RX_RING_SLOTS];
diff --git a/target/linux/generic/patches-4.0/072-13-bgmac-fix-MAC-soft-reset-bit-for-corerev-4.patch b/target/linux/generic/patches-4.0/072-13-bgmac-fix-MAC-soft-reset-bit-for-corerev-4.patch
new file mode 100644
index 0000000000..a3d9fd6cd9
--- /dev/null
+++ b/target/linux/generic/patches-4.0/072-13-bgmac-fix-MAC-soft-reset-bit-for-corerev-4.patch
@@ -0,0 +1,24 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Mon, 13 Apr 2015 15:54:04 +0200
+Subject: [PATCH] bgmac: fix MAC soft-reset bit for corerev > 4
+
+Only core revisions older than 4 use BGMAC_CMDCFG_SR_REV0
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/ethernet/broadcom/bgmac.h
++++ b/drivers/net/ethernet/broadcom/bgmac.h
+@@ -198,9 +198,9 @@
+ #define  BGMAC_CMDCFG_TAI			0x00000200
+ #define  BGMAC_CMDCFG_HD			0x00000400	/* Set if in half duplex mode */
+ #define  BGMAC_CMDCFG_HD_SHIFT			10
+-#define  BGMAC_CMDCFG_SR_REV0			0x00000800	/* Set to reset mode, for other revs */
+-#define  BGMAC_CMDCFG_SR_REV4			0x00002000	/* Set to reset mode, only for core rev 4 */
+-#define  BGMAC_CMDCFG_SR(rev)  ((rev == 4) ? BGMAC_CMDCFG_SR_REV4 : BGMAC_CMDCFG_SR_REV0)
++#define  BGMAC_CMDCFG_SR_REV0			0x00000800	/* Set to reset mode, for core rev 0-3 */
++#define  BGMAC_CMDCFG_SR_REV4			0x00002000	/* Set to reset mode, for core rev >= 4 */
++#define  BGMAC_CMDCFG_SR(rev)  ((rev >= 4) ? BGMAC_CMDCFG_SR_REV4 : BGMAC_CMDCFG_SR_REV0)
+ #define  BGMAC_CMDCFG_ML			0x00008000	/* Set to activate mac loopback mode */
+ #define  BGMAC_CMDCFG_AE			0x00400000
+ #define  BGMAC_CMDCFG_CFE			0x00800000
diff --git a/target/linux/generic/patches-4.0/072-14-bgmac-reset-all-4-GMAC-cores-on-init.patch b/target/linux/generic/patches-4.0/072-14-bgmac-reset-all-4-GMAC-cores-on-init.patch
new file mode 100644
index 0000000000..2a913a60e7
--- /dev/null
+++ b/target/linux/generic/patches-4.0/072-14-bgmac-reset-all-4-GMAC-cores-on-init.patch
@@ -0,0 +1,28 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Mon, 13 Apr 2015 15:56:26 +0200
+Subject: [PATCH] bgmac: reset all 4 GMAC cores on init
+
+On a BCM4709 based device, I found that GMAC cores may be enabled at
+probe time, but only become usable after a full reset.
+Disable cores before re-enabling them to ensure that they are properly
+reset.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -1623,8 +1623,11 @@ static int bgmac_probe(struct bcma_devic
+ 			ns_core = bcma_find_core_unit(core->bus,
+ 						      BCMA_CORE_MAC_GBIT,
+ 						      ns_gmac);
+-			if (ns_core && !bcma_core_is_enabled(ns_core))
+-				bcma_core_enable(ns_core, 0);
++			if (!ns_core)
++				continue;
++
++			bcma_core_disable(ns_core, 0);
++			bcma_core_enable(ns_core, 0);
+ 		}
+ 	}
+ 
diff --git a/target/linux/generic/patches-4.0/072-15-bgmac-fix-requests-for-extra-polling-calls-from-NAPI.patch b/target/linux/generic/patches-4.0/072-15-bgmac-fix-requests-for-extra-polling-calls-from-NAPI.patch
new file mode 100644
index 0000000000..39cf048b9c
--- /dev/null
+++ b/target/linux/generic/patches-4.0/072-15-bgmac-fix-requests-for-extra-polling-calls-from-NAPI.patch
@@ -0,0 +1,35 @@
+From 047f89922c6381432501f248d08226ff9adc4ee3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Date: Thu, 23 Apr 2015 20:45:25 +0200
+Subject: [PATCH][FIX 4.1] bgmac: fix requests for extra polling calls from
+ NAPI
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+After d75b1ade567f ("net: less interrupt masking in NAPI") polling
+function has to return whole budget when it wants NAPI to call it again.
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+Cc: Felix Fietkau <nbd@openwrt.org>
+Fixes: eb64e2923a886 ("bgmac: leave interrupts disabled as long as there is work to do")
+---
+ drivers/net/ethernet/broadcom/bgmac.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
+index de77d3a..21e3c38 100644
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -1260,7 +1260,7 @@ static int bgmac_poll(struct napi_struct *napi, int weight)
+ 
+ 	/* Poll again if more events arrived in the meantime */
+ 	if (bgmac_read(bgmac, BGMAC_INT_STATUS) & (BGMAC_IS_TX0 | BGMAC_IS_RX))
+-		return handled;
++		return weight;
+ 
+ 	if (handled < weight) {
+ 		napi_complete(napi);
+-- 
+1.8.4.5
+
-- 
2.30.2