kernel: backport rest of bgmac patches to 4.0
authorrmilecki <rmilecki@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Thu, 23 Apr 2015 19:11:11 +0000 (19:11 +0000)
committerrmilecki <rmilecki@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Thu, 23 Apr 2015 19:11:11 +0000 (19:11 +0000)
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@45573 3c298f89-4303-0410-b956-a3cf2f4a3e73

target/linux/generic/patches-4.0/072-05-bgmac-leave-interrupts-disabled-as-long-as-there-is-.patch [new file with mode: 0644]
target/linux/generic/patches-4.0/072-06-bgmac-set-received-skb-headroom-to-NET_SKB_PAD.patch [new file with mode: 0644]
target/linux/generic/patches-4.0/072-07-bgmac-simplify-rx-DMA-error-handling.patch [new file with mode: 0644]
target/linux/generic/patches-4.0/072-08-bgmac-add-check-for-oversized-packets.patch [new file with mode: 0644]
target/linux/generic/patches-4.0/072-09-bgmac-increase-rx-ring-size-from-511-to-512.patch [new file with mode: 0644]
target/linux/generic/patches-4.0/072-10-bgmac-simplify-dma-init-cleanup.patch [new file with mode: 0644]
target/linux/generic/patches-4.0/072-11-bgmac-fix-DMA-rx-corruption.patch [new file with mode: 0644]
target/linux/generic/patches-4.0/072-12-bgmac-drop-ring-num_slots.patch [new file with mode: 0644]
target/linux/generic/patches-4.0/072-13-bgmac-fix-MAC-soft-reset-bit-for-corerev-4.patch [new file with mode: 0644]
target/linux/generic/patches-4.0/072-14-bgmac-reset-all-4-GMAC-cores-on-init.patch [new file with mode: 0644]
target/linux/generic/patches-4.0/072-15-bgmac-fix-requests-for-extra-polling-calls-from-NAPI.patch [new file with mode: 0644]

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 (file)
index 0000000..3bbe9ea
--- /dev/null
@@ -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 (file)
index 0000000..2c490ef
--- /dev/null
@@ -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 (file)
index 0000000..2ca9d10
--- /dev/null
@@ -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 (file)
index 0000000..705aa2d
--- /dev/null
@@ -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 (file)
index 0000000..4888cc3
--- /dev/null
@@ -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 (file)
index 0000000..f8d7921
--- /dev/null
@@ -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 (file)
index 0000000..2e670d8
--- /dev/null
@@ -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 (file)
index 0000000..4cd2e3f
--- /dev/null
@@ -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 (file)
index 0000000..a3d9fd6
--- /dev/null
@@ -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 (file)
index 0000000..2a913a6
--- /dev/null
@@ -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 (file)
index 0000000..39cf048
--- /dev/null
@@ -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
+