kernel: backport rest of bgmac patches to 4.0
Signed-off-by: Rafał Miłecki <zajec5@gmail.com> SVN-Revision: 45573
This commit is contained in:
parent
62e7f07615
commit
e175b199f8
11 changed files with 824 additions and 0 deletions
|
@ -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;
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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];
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
Loading…
Reference in a new issue