bcm4908: update Ethernet driver

Use the latest version sent upsteram.

Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
This commit is contained in:
Rafał Miłecki 2021-02-10 11:27:44 +01:00
parent 670bfc86c8
commit c382fe857d
4 changed files with 179 additions and 178 deletions

View File

@ -40,7 +40,7 @@ CONFIG_B53=y
# CONFIG_B53_MMAP_DRIVER is not set
# CONFIG_B53_SERDES is not set
# CONFIG_B53_SRAB_DRIVER is not set
CONFIG_BCM4908ENET=y
CONFIG_BCM4908_ENET=y
CONFIG_BCM7XXX_PHY=y
CONFIG_BCM_NET_PHYLIB=y
CONFIG_BCM_PMB=y

View File

@ -1,22 +1,25 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
Date: Fri, 5 Feb 2021 21:57:41 +0100
Subject: [PATCH 1/2] dt-bindings: net: document BCM4908 Ethernet controller
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
BCM4908 is a family of SoCs with integrated Ethernet controller.
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
---
.../bindings/net/brcm,bcm4908enet.yaml | 45 +++++++++++++++++++
1 file changed, 45 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/brcm,bcm4908enet.yaml
.../bindings/net/brcm,bcm4908-enet.yaml | 43 +++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/brcm,bcm4908-enet.yaml
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/brcm,bcm4908enet.yaml
@@ -0,0 +1,45 @@
+++ b/Documentation/devicetree/bindings/net/brcm,bcm4908-enet.yaml
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/brcm,bcm4908enet.yaml#
+$id: http://devicetree.org/schemas/net/brcm,bcm4908-enet.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Broadcom BCM4908 Ethernet controller
@ -26,9 +29,12 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+maintainers:
+ - Rafał Miłecki <rafal@milecki.pl>
+
+allOf:
+ - $ref: ethernet-controller.yaml#
+
+properties:
+ compatible:
+ const: brcm,bcm4908enet
+ const: brcm,bcm4908-enet
+
+ reg:
+ maxItems: 1
@ -36,13 +42,9 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ interrupts:
+ description: RX interrupt
+
+ interrupt-names:
+ const: rx
+
+required:
+ - reg
+ - interrupts
+ - interrupt-names
+
+additionalProperties: false
+
@ -52,9 +54,8 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ ethernet@80002000 {
+ compatible = "brcm,bcm4908enet";
+ compatible = "brcm,bcm4908-enet";
+ reg = <0x80002000 0x1000>;
+
+ interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "rx";
+ };

View File

@ -1,21 +1,28 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
Date: Fri, 5 Feb 2021 21:59:51 +0100
Subject: [PATCH 2/2] net: broadcom: bcm4908enet: add BCM4908 controller driver
Subject: [PATCH 2/2] net: broadcom: bcm4908_enet: add BCM4908 controller
driver
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
BCM4908 SoCs family uses Ethernel controller that includes UniMAC but
uses different DMA engine (than other controllers) and requires
different programming.
BCM4908 SoCs family has integrated Ethernel controller that includes
UniMAC but uses different DMA engine (than other controllers) and
requires different programming.
Ethernet controller in BCM4908 is always connected to the internal SF2
switch's port and uses fixed link.
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
---
MAINTAINERS | 9 +
drivers/net/ethernet/broadcom/Kconfig | 8 +
drivers/net/ethernet/broadcom/Makefile | 1 +
drivers/net/ethernet/broadcom/bcm4908enet.c | 676 ++++++++++++++++++++
drivers/net/ethernet/broadcom/bcm4908enet.h | 96 +++
5 files changed, 790 insertions(+)
create mode 100644 drivers/net/ethernet/broadcom/bcm4908enet.c
create mode 100644 drivers/net/ethernet/broadcom/bcm4908enet.h
MAINTAINERS | 9 +
drivers/net/ethernet/broadcom/Kconfig | 8 +
drivers/net/ethernet/broadcom/Makefile | 1 +
drivers/net/ethernet/broadcom/bcm4908_enet.c | 671 +++++++++++++++++++
drivers/net/ethernet/broadcom/bcm4908_enet.h | 96 +++
5 files changed, 785 insertions(+)
create mode 100644 drivers/net/ethernet/broadcom/bcm4908_enet.c
create mode 100644 drivers/net/ethernet/broadcom/bcm4908_enet.h
--- a/MAINTAINERS
+++ b/MAINTAINERS
@ -28,8 +35,8 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+M: bcm-kernel-feedback-list@broadcom.com
+L: netdev@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/net/brcm,bcm4908enet.yaml
+F: drivers/net/ethernet/broadcom/bcm4908enet.*
+F: Documentation/devicetree/bindings/net/brcm,bcm4908-enet.yaml
+F: drivers/net/ethernet/broadcom/bcm4908_enet.*
+F: drivers/net/ethernet/broadcom/unimac.h
+
BROADCOM BCM5301X ARM ARCHITECTURE
@ -41,10 +48,10 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
depends on B44_PCI_AUTOSELECT && B44_PCICORE_AUTOSELECT
default y
+config BCM4908ENET
+config BCM4908_ENET
+ tristate "Broadcom BCM4908 internal mac support"
+ depends on ARCH_BCM4908 || COMPILE_TEST
+ default y
+ default ARCH_BCM4908
+ help
+ This driver supports Ethernet controller integrated into Broadcom
+ BCM4908 family SoCs.
@ -58,13 +65,13 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
#
obj-$(CONFIG_B44) += b44.o
+obj-$(CONFIG_BCM4908ENET) += bcm4908enet.o
+obj-$(CONFIG_BCM4908_ENET) += bcm4908_enet.o
obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o
obj-$(CONFIG_BCMGENET) += genet/
obj-$(CONFIG_BNX2) += bnx2.o
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bcm4908enet.c
@@ -0,0 +1,676 @@
+++ b/drivers/net/ethernet/broadcom/bcm4908_enet.c
@@ -0,0 +1,671 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
@ -79,7 +86,7 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "bcm4908enet.h"
+#include "bcm4908_enet.h"
+#include "unimac.h"
+
+#define ENET_DMA_CH_RX_CFG ENET_DMA_CH0_CFG
@ -100,18 +107,18 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+#define ENET_MTU_MAX 1500 /* Is it possible to support 2044? */
+#define ENET_MTU_MAX_EXTRA_SIZE 32 /* L2 */
+
+struct bcm4908enet_dma_ring_bd {
+struct bcm4908_enet_dma_ring_bd {
+ __le32 ctl;
+ __le32 addr;
+} __packed;
+
+struct bcm4908enet_dma_ring_slot {
+struct bcm4908_enet_dma_ring_slot {
+ struct sk_buff *skb;
+ unsigned int len;
+ dma_addr_t dma_addr;
+};
+
+struct bcm4908enet_dma_ring {
+struct bcm4908_enet_dma_ring {
+ int is_tx;
+ int read_idx;
+ int write_idx;
@ -121,38 +128,38 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+
+ union {
+ void *cpu_addr;
+ struct bcm4908enet_dma_ring_bd *buf_desc;
+ struct bcm4908_enet_dma_ring_bd *buf_desc;
+ };
+ dma_addr_t dma_addr;
+
+ struct bcm4908enet_dma_ring_slot *slots;
+ struct bcm4908_enet_dma_ring_slot *slots;
+};
+
+struct bcm4908enet {
+struct bcm4908_enet {
+ struct device *dev;
+ struct net_device *netdev;
+ struct napi_struct napi;
+ void __iomem *base;
+
+ struct bcm4908enet_dma_ring tx_ring;
+ struct bcm4908enet_dma_ring rx_ring;
+ struct bcm4908_enet_dma_ring tx_ring;
+ struct bcm4908_enet_dma_ring rx_ring;
+};
+
+/***
+ * R/W ops
+ */
+
+static inline u32 enet_read(struct bcm4908enet *enet, u16 offset)
+static u32 enet_read(struct bcm4908_enet *enet, u16 offset)
+{
+ return readl(enet->base + offset);
+}
+
+static inline void enet_write(struct bcm4908enet *enet, u16 offset, u32 value)
+static void enet_write(struct bcm4908_enet *enet, u16 offset, u32 value)
+{
+ writel(value, enet->base + offset);
+}
+
+static inline void enet_maskset(struct bcm4908enet *enet, u16 offset, u32 mask, u32 set)
+static void enet_maskset(struct bcm4908_enet *enet, u16 offset, u32 mask, u32 set)
+{
+ u32 val;
+
@ -163,27 +170,22 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ enet_write(enet, offset, val);
+}
+
+static inline void enet_set(struct bcm4908enet *enet, u16 offset, u32 set)
+static void enet_set(struct bcm4908_enet *enet, u16 offset, u32 set)
+{
+ enet_maskset(enet, offset, set, set);
+}
+
+static inline u32 enet_umac_read(struct bcm4908enet *enet, u16 offset)
+static u32 enet_umac_read(struct bcm4908_enet *enet, u16 offset)
+{
+ return enet_read(enet, ENET_UNIMAC + offset);
+}
+
+static inline void enet_umac_write(struct bcm4908enet *enet, u16 offset, u32 value)
+static void enet_umac_write(struct bcm4908_enet *enet, u16 offset, u32 value)
+{
+ enet_write(enet, ENET_UNIMAC + offset, value);
+}
+
+static inline void enet_umac_maskset(struct bcm4908enet *enet, u16 offset, u32 mask, u32 set)
+{
+ enet_maskset(enet, ENET_UNIMAC + offset, mask, set);
+}
+
+static inline void enet_umac_set(struct bcm4908enet *enet, u16 offset, u32 set)
+static void enet_umac_set(struct bcm4908_enet *enet, u16 offset, u32 set)
+{
+ enet_set(enet, ENET_UNIMAC + offset, set);
+}
@ -192,17 +194,17 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ * Helpers
+ */
+
+static void bcm4908enet_intrs_on(struct bcm4908enet *enet)
+static void bcm4908_enet_intrs_on(struct bcm4908_enet *enet)
+{
+ enet_write(enet, ENET_DMA_CH_RX_CFG + ENET_DMA_CH_CFG_INT_MASK, ENET_DMA_INT_DEFAULTS);
+}
+
+static void bcm4908enet_intrs_off(struct bcm4908enet *enet)
+static void bcm4908_enet_intrs_off(struct bcm4908_enet *enet)
+{
+ enet_write(enet, ENET_DMA_CH_RX_CFG + ENET_DMA_CH_CFG_INT_MASK, 0);
+}
+
+static void bcm4908enet_intrs_ack(struct bcm4908enet *enet)
+static void bcm4908_enet_intrs_ack(struct bcm4908_enet *enet)
+{
+ enet_write(enet, ENET_DMA_CH_RX_CFG + ENET_DMA_CH_CFG_INT_STAT, ENET_DMA_INT_DEFAULTS);
+}
@ -211,9 +213,10 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ * DMA
+ */
+
+static int bcm4908_dma_alloc_buf_descs(struct bcm4908enet *enet, struct bcm4908enet_dma_ring *ring)
+static int bcm4908_dma_alloc_buf_descs(struct bcm4908_enet *enet,
+ struct bcm4908_enet_dma_ring *ring)
+{
+ int size = ring->length * sizeof(struct bcm4908enet_dma_ring_bd);
+ int size = ring->length * sizeof(struct bcm4908_enet_dma_ring_bd);
+ struct device *dev = enet->dev;
+
+ ring->cpu_addr = dma_alloc_coherent(dev, size, &ring->dma_addr, GFP_KERNEL);
@ -229,8 +232,6 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ if (!ring->slots)
+ goto err_free_buf_descs;
+
+ memset(ring->cpu_addr, 0, size);
+
+ ring->read_idx = 0;
+ ring->write_idx = 0;
+
@ -241,28 +242,28 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ return -ENOMEM;
+}
+
+static void bcm4908enet_dma_free(struct bcm4908enet *enet)
+static void bcm4908_enet_dma_free(struct bcm4908_enet *enet)
+{
+ struct bcm4908enet_dma_ring *tx_ring = &enet->tx_ring;
+ struct bcm4908enet_dma_ring *rx_ring = &enet->rx_ring;
+ struct bcm4908_enet_dma_ring *tx_ring = &enet->tx_ring;
+ struct bcm4908_enet_dma_ring *rx_ring = &enet->rx_ring;
+ struct device *dev = enet->dev;
+ int size;
+
+ size = rx_ring->length * sizeof(struct bcm4908enet_dma_ring_bd);
+ size = rx_ring->length * sizeof(struct bcm4908_enet_dma_ring_bd);
+ if (rx_ring->cpu_addr)
+ dma_free_coherent(dev, size, rx_ring->cpu_addr, rx_ring->dma_addr);
+ kfree(rx_ring->slots);
+
+ size = tx_ring->length * sizeof(struct bcm4908enet_dma_ring_bd);
+ size = tx_ring->length * sizeof(struct bcm4908_enet_dma_ring_bd);
+ if (tx_ring->cpu_addr)
+ dma_free_coherent(dev, size, tx_ring->cpu_addr, tx_ring->dma_addr);
+ kfree(tx_ring->slots);
+}
+
+static int bcm4908enet_dma_alloc(struct bcm4908enet *enet)
+static int bcm4908_enet_dma_alloc(struct bcm4908_enet *enet)
+{
+ struct bcm4908enet_dma_ring *tx_ring = &enet->tx_ring;
+ struct bcm4908enet_dma_ring *rx_ring = &enet->rx_ring;
+ struct bcm4908_enet_dma_ring *tx_ring = &enet->tx_ring;
+ struct bcm4908_enet_dma_ring *rx_ring = &enet->rx_ring;
+ struct device *dev = enet->dev;
+ int err;
+
@ -283,16 +284,16 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ err = bcm4908_dma_alloc_buf_descs(enet, rx_ring);
+ if (err) {
+ dev_err(dev, "Failed to alloc RX buf descriptors: %d\n", err);
+ bcm4908enet_dma_free(enet);
+ bcm4908_enet_dma_free(enet);
+ return err;
+ }
+
+ return 0;
+}
+
+static void bcm4908enet_dma_reset(struct bcm4908enet *enet)
+static void bcm4908_enet_dma_reset(struct bcm4908_enet *enet)
+{
+ struct bcm4908enet_dma_ring *rings[] = { &enet->rx_ring, &enet->tx_ring };
+ struct bcm4908_enet_dma_ring *rings[] = { &enet->rx_ring, &enet->tx_ring };
+ int i;
+
+ /* Disable the DMA controller and channel */
@ -302,7 +303,7 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+
+ /* Reset channels state */
+ for (i = 0; i < ARRAY_SIZE(rings); i++) {
+ struct bcm4908enet_dma_ring *ring = rings[i];
+ struct bcm4908_enet_dma_ring *ring = rings[i];
+
+ enet_write(enet, ring->st_ram_block + ENET_DMA_CH_STATE_RAM_BASE_DESC_PTR, 0);
+ enet_write(enet, ring->st_ram_block + ENET_DMA_CH_STATE_RAM_STATE_DATA, 0);
@ -311,10 +312,10 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ }
+}
+
+static int bcm4908enet_dma_alloc_rx_buf(struct bcm4908enet *enet, unsigned int idx)
+static int bcm4908_enet_dma_alloc_rx_buf(struct bcm4908_enet *enet, unsigned int idx)
+{
+ struct bcm4908enet_dma_ring_bd *buf_desc = &enet->rx_ring.buf_desc[idx];
+ struct bcm4908enet_dma_ring_slot *slot = &enet->rx_ring.slots[idx];
+ struct bcm4908_enet_dma_ring_bd *buf_desc = &enet->rx_ring.buf_desc[idx];
+ struct bcm4908_enet_dma_ring_slot *slot = &enet->rx_ring.slots[idx];
+ struct device *dev = enet->dev;
+ u32 tmp;
+ int err;
@ -344,8 +345,8 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ return 0;
+}
+
+static void bcm4908enet_dma_ring_init(struct bcm4908enet *enet,
+ struct bcm4908enet_dma_ring *ring)
+static void bcm4908_enet_dma_ring_init(struct bcm4908_enet *enet,
+ struct bcm4908_enet_dma_ring *ring)
+{
+ int reset_channel = 0; /* We support only 1 main channel (with TX and RX) */
+ int reset_subch = ring->is_tx ? 1 : 0;
@ -362,10 +363,10 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ (uint32_t)ring->dma_addr);
+}
+
+static void bcm4908enet_dma_uninit(struct bcm4908enet *enet)
+static void bcm4908_enet_dma_uninit(struct bcm4908_enet *enet)
+{
+ struct bcm4908enet_dma_ring *rx_ring = &enet->rx_ring;
+ struct bcm4908enet_dma_ring_slot *slot;
+ struct bcm4908_enet_dma_ring *rx_ring = &enet->rx_ring;
+ struct bcm4908_enet_dma_ring_slot *slot;
+ struct device *dev = enet->dev;
+ int i;
+
@ -379,48 +380,48 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ }
+}
+
+static int bcm4908enet_dma_init(struct bcm4908enet *enet)
+static int bcm4908_enet_dma_init(struct bcm4908_enet *enet)
+{
+ struct bcm4908enet_dma_ring *rx_ring = &enet->rx_ring;
+ struct bcm4908_enet_dma_ring *rx_ring = &enet->rx_ring;
+ struct device *dev = enet->dev;
+ int err;
+ int i;
+
+ for (i = 0; i < rx_ring->length; i++) {
+ err = bcm4908enet_dma_alloc_rx_buf(enet, i);
+ err = bcm4908_enet_dma_alloc_rx_buf(enet, i);
+ if (err) {
+ dev_err(dev, "Failed to alloc RX buffer: %d\n", err);
+ bcm4908enet_dma_uninit(enet);
+ bcm4908_enet_dma_uninit(enet);
+ return err;
+ }
+ }
+
+ bcm4908enet_dma_ring_init(enet, &enet->tx_ring);
+ bcm4908enet_dma_ring_init(enet, &enet->rx_ring);
+ bcm4908_enet_dma_ring_init(enet, &enet->tx_ring);
+ bcm4908_enet_dma_ring_init(enet, &enet->rx_ring);
+
+ return 0;
+}
+
+static void bcm4908enet_dma_tx_ring_ensable(struct bcm4908enet *enet,
+ struct bcm4908enet_dma_ring *ring)
+static void bcm4908_enet_dma_tx_ring_enable(struct bcm4908_enet *enet,
+ struct bcm4908_enet_dma_ring *ring)
+{
+ enet_write(enet, ring->cfg_block + ENET_DMA_CH_CFG, ENET_DMA_CH_CFG_ENABLE);
+}
+
+static void bcm4908enet_dma_tx_ring_disable(struct bcm4908enet *enet,
+ struct bcm4908enet_dma_ring *ring)
+static void bcm4908_enet_dma_tx_ring_disable(struct bcm4908_enet *enet,
+ struct bcm4908_enet_dma_ring *ring)
+{
+ enet_write(enet, ring->cfg_block + ENET_DMA_CH_CFG, 0);
+}
+
+static void bcm4908enet_dma_rx_ring_enable(struct bcm4908enet *enet,
+ struct bcm4908enet_dma_ring *ring)
+static void bcm4908_enet_dma_rx_ring_enable(struct bcm4908_enet *enet,
+ struct bcm4908_enet_dma_ring *ring)
+{
+ enet_set(enet, ring->cfg_block + ENET_DMA_CH_CFG, ENET_DMA_CH_CFG_ENABLE);
+}
+
+static void bcm4908enet_dma_rx_ring_disable(struct bcm4908enet *enet,
+ struct bcm4908enet_dma_ring *ring)
+static void bcm4908_enet_dma_rx_ring_disable(struct bcm4908_enet *enet,
+ struct bcm4908_enet_dma_ring *ring)
+{
+ unsigned long deadline;
+ u32 tmp;
@ -443,7 +444,7 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ * Ethernet driver
+ */
+
+static void bcm4908enet_gmac_init(struct bcm4908enet *enet)
+static void bcm4908_enet_gmac_init(struct bcm4908_enet *enet)
+{
+ u32 cmd;
+
@ -474,82 +475,82 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ ENET_GMAC_STATUS_LINK_UP);
+}
+
+static irqreturn_t bcm4908enet_irq_handler(int irq, void *dev_id)
+static irqreturn_t bcm4908_enet_irq_handler(int irq, void *dev_id)
+{
+ struct bcm4908enet *enet = dev_id;
+ struct bcm4908_enet *enet = dev_id;
+
+ bcm4908enet_intrs_off(enet);
+ bcm4908enet_intrs_ack(enet);
+ bcm4908_enet_intrs_off(enet);
+ bcm4908_enet_intrs_ack(enet);
+
+ napi_schedule(&enet->napi);
+
+ return IRQ_HANDLED;
+}
+
+static int bcm4908enet_open(struct net_device *netdev)
+static int bcm4908_enet_open(struct net_device *netdev)
+{
+ struct bcm4908enet *enet = netdev_priv(netdev);
+ struct bcm4908_enet *enet = netdev_priv(netdev);
+ struct device *dev = enet->dev;
+ int err;
+
+ err = request_irq(netdev->irq, bcm4908enet_irq_handler, 0, "enet", enet);
+ err = request_irq(netdev->irq, bcm4908_enet_irq_handler, 0, "enet", enet);
+ if (err) {
+ dev_err(dev, "Failed to request IRQ %d: %d\n", netdev->irq, err);
+ return err;
+ }
+
+ bcm4908enet_gmac_init(enet);
+ bcm4908enet_dma_reset(enet);
+ bcm4908enet_dma_init(enet);
+ bcm4908_enet_gmac_init(enet);
+ bcm4908_enet_dma_reset(enet);
+ bcm4908_enet_dma_init(enet);
+
+ enet_umac_set(enet, UMAC_CMD, CMD_TX_EN | CMD_RX_EN);
+
+ enet_set(enet, ENET_DMA_CONTROLLER_CFG, ENET_DMA_CTRL_CFG_MASTER_EN);
+ enet_maskset(enet, ENET_DMA_CONTROLLER_CFG, ENET_DMA_CTRL_CFG_FLOWC_CH1_EN, 0);
+ bcm4908enet_dma_rx_ring_enable(enet, &enet->rx_ring);
+ bcm4908_enet_dma_rx_ring_enable(enet, &enet->rx_ring);
+
+ napi_enable(&enet->napi);
+ netif_carrier_on(netdev);
+ netif_start_queue(netdev);
+
+ bcm4908enet_intrs_ack(enet);
+ bcm4908enet_intrs_on(enet);
+ bcm4908_enet_intrs_ack(enet);
+ bcm4908_enet_intrs_on(enet);
+
+ return 0;
+}
+
+static int bcm4908enet_stop(struct net_device *netdev)
+static int bcm4908_enet_stop(struct net_device *netdev)
+{
+ struct bcm4908enet *enet = netdev_priv(netdev);
+ struct bcm4908_enet *enet = netdev_priv(netdev);
+
+ netif_stop_queue(netdev);
+ netif_carrier_off(netdev);
+ napi_disable(&enet->napi);
+
+ bcm4908enet_dma_rx_ring_disable(enet, &enet->rx_ring);
+ bcm4908enet_dma_tx_ring_disable(enet, &enet->tx_ring);
+ bcm4908_enet_dma_rx_ring_disable(enet, &enet->rx_ring);
+ bcm4908_enet_dma_tx_ring_disable(enet, &enet->tx_ring);
+
+ bcm4908enet_dma_uninit(enet);
+ bcm4908_enet_dma_uninit(enet);
+
+ free_irq(enet->netdev->irq, enet);
+
+ return 0;
+}
+
+static int bcm4908enet_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+static int bcm4908_enet_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct bcm4908enet *enet = netdev_priv(netdev);
+ struct bcm4908enet_dma_ring *ring = &enet->tx_ring;
+ struct bcm4908enet_dma_ring_slot *slot;
+ struct bcm4908_enet *enet = netdev_priv(netdev);
+ struct bcm4908_enet_dma_ring *ring = &enet->tx_ring;
+ struct bcm4908_enet_dma_ring_slot *slot;
+ struct device *dev = enet->dev;
+ struct bcm4908enet_dma_ring_bd *buf_desc;
+ struct bcm4908_enet_dma_ring_bd *buf_desc;
+ int free_buf_descs;
+ u32 tmp;
+
+ /* Free transmitted skbs */
+ while (ring->read_idx != ring->write_idx) {
+ buf_desc = &ring->buf_desc[ring->read_idx];
+ if (buf_desc->ctl & DMA_CTL_STATUS_OWN)
+ if (le32_to_cpu(buf_desc->ctl) & DMA_CTL_STATUS_OWN)
+ break;
+ slot = &ring->slots[ring->read_idx];
+
@ -592,7 +593,7 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ buf_desc->addr = cpu_to_le32((uint32_t)slot->dma_addr);
+ buf_desc->ctl = cpu_to_le32(tmp);
+
+ bcm4908enet_dma_tx_ring_ensable(enet, &enet->tx_ring);
+ bcm4908_enet_dma_tx_ring_enable(enet, &enet->tx_ring);
+
+ if (++ring->write_idx == ring->length - 1)
+ ring->write_idx = 0;
@ -602,15 +603,15 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ return NETDEV_TX_OK;
+}
+
+static int bcm4908enet_poll(struct napi_struct *napi, int weight)
+static int bcm4908_enet_poll(struct napi_struct *napi, int weight)
+{
+ struct bcm4908enet *enet = container_of(napi, struct bcm4908enet, napi);
+ struct bcm4908_enet *enet = container_of(napi, struct bcm4908_enet, napi);
+ struct device *dev = enet->dev;
+ int handled = 0;
+
+ while (handled < weight) {
+ struct bcm4908enet_dma_ring_bd *buf_desc;
+ struct bcm4908enet_dma_ring_slot slot;
+ struct bcm4908_enet_dma_ring_bd *buf_desc;
+ struct bcm4908_enet_dma_ring_slot slot;
+ u32 ctl;
+ int len;
+ int err;
@ -623,7 +624,7 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ slot = enet->rx_ring.slots[enet->rx_ring.read_idx];
+
+ /* Provide new buffer before unpinning the old one */
+ err = bcm4908enet_dma_alloc_rx_buf(enet, enet->rx_ring.read_idx);
+ err = bcm4908_enet_dma_alloc_rx_buf(enet, enet->rx_ring.read_idx);
+ if (err)
+ break;
+
@ -634,13 +635,14 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+
+ if (len < ENET_MTU_MIN ||
+ (ctl & (DMA_CTL_STATUS_SOP | DMA_CTL_STATUS_EOP)) != (DMA_CTL_STATUS_SOP | DMA_CTL_STATUS_EOP)) {
+ kfree(slot.skb);
+ enet->netdev->stats.rx_dropped++;
+ break;
+ }
+
+ dma_unmap_single(dev, slot.dma_addr, slot.len, DMA_FROM_DEVICE);
+
+ skb_put(slot.skb, len - 4 + 2);
+ skb_put(slot.skb, len - ETH_FCS_LEN);
+ slot.skb->protocol = eth_type_trans(slot.skb, enet->netdev);
+ netif_receive_skb(slot.skb);
+
@ -650,24 +652,24 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+
+ if (handled < weight) {
+ napi_complete_done(napi, handled);
+ bcm4908enet_intrs_on(enet);
+ bcm4908_enet_intrs_on(enet);
+ }
+
+ return handled;
+}
+
+static const struct net_device_ops bcm96xx_netdev_ops = {
+ .ndo_open = bcm4908enet_open,
+ .ndo_stop = bcm4908enet_stop,
+ .ndo_start_xmit = bcm4908enet_start_xmit,
+static const struct net_device_ops bcm4908_enet_netdev_ops = {
+ .ndo_open = bcm4908_enet_open,
+ .ndo_stop = bcm4908_enet_stop,
+ .ndo_start_xmit = bcm4908_enet_start_xmit,
+ .ndo_set_mac_address = eth_mac_addr,
+};
+
+static int bcm4908enet_probe(struct platform_device *pdev)
+static int bcm4908_enet_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct net_device *netdev;
+ struct bcm4908enet *enet;
+ struct bcm4908_enet *enet;
+ int err;
+
+ netdev = devm_alloc_etherdev(dev, sizeof(*enet));
@ -684,27 +686,27 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ return PTR_ERR(enet->base);
+ }
+
+ netdev->irq = platform_get_irq_byname(pdev, "rx");
+ netdev->irq = platform_get_irq(pdev, 0);
+ if (netdev->irq < 0)
+ return netdev->irq;
+
+ dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+
+ err = bcm4908enet_dma_alloc(enet);
+ err = bcm4908_enet_dma_alloc(enet);
+ if (err)
+ return err;
+
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ eth_hw_addr_random(netdev);
+ netdev->netdev_ops = &bcm96xx_netdev_ops;
+ netdev->netdev_ops = &bcm4908_enet_netdev_ops;
+ netdev->min_mtu = ETH_ZLEN;
+ netdev->mtu = ENET_MTU_MAX;
+ netdev->max_mtu = ENET_MTU_MAX;
+ netif_napi_add(netdev, &enet->napi, bcm4908enet_poll, 64);
+ netif_napi_add(netdev, &enet->napi, bcm4908_enet_poll, 64);
+
+ err = register_netdev(netdev);
+ if (err) {
+ bcm4908enet_dma_free(enet);
+ bcm4908_enet_dma_free(enet);
+ return err;
+ }
+
@ -713,40 +715,40 @@ Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+ return 0;
+}
+
+static int bcm4908enet_remove(struct platform_device *pdev)
+static int bcm4908_enet_remove(struct platform_device *pdev)
+{
+ struct bcm4908enet *enet = platform_get_drvdata(pdev);
+ struct bcm4908_enet *enet = platform_get_drvdata(pdev);
+
+ unregister_netdev(enet->netdev);
+ netif_napi_del(&enet->napi);
+ bcm4908enet_dma_free(enet);
+ bcm4908_enet_dma_free(enet);
+
+ return 0;
+}
+
+static const struct of_device_id bcm4908enet_of_match[] = {
+ { .compatible = "brcm,bcm4908enet"},
+static const struct of_device_id bcm4908_enet_of_match[] = {
+ { .compatible = "brcm,bcm4908-enet"},
+ {},
+};
+
+static struct platform_driver bcm4908enet_driver = {
+static struct platform_driver bcm4908_enet_driver = {
+ .driver = {
+ .name = "bcm4908enet",
+ .of_match_table = bcm4908enet_of_match,
+ .name = "bcm4908_enet",
+ .of_match_table = bcm4908_enet_of_match,
+ },
+ .probe = bcm4908enet_probe,
+ .remove = bcm4908enet_remove,
+ .probe = bcm4908_enet_probe,
+ .remove = bcm4908_enet_remove,
+};
+module_platform_driver(bcm4908enet_driver);
+module_platform_driver(bcm4908_enet_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, bcm4908enet_of_match);
+MODULE_DEVICE_TABLE(of, bcm4908_enet_of_match);
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bcm4908enet.h
+++ b/drivers/net/ethernet/broadcom/bcm4908_enet.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __BCM4908ENET_H
+#define __BCM4908ENET_H
+#ifndef __BCM4908_ENET_H
+#define __BCM4908_ENET_H
+
+#define ENET_CONTROL 0x000
+#define ENET_MIB_CTRL 0x004

View File

@ -10,40 +10,38 @@ BCM4908 SoCs have an integrated Ethernet controller.
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
---
--- a/arch/arm64/boot/dts/broadcom/bcm4908/bcm4908-asus-gt-ac5300.dts
+++ b/arch/arm64/boot/dts/broadcom/bcm4908/bcm4908-asus-gt-ac5300.dts
@@ -87,6 +87,17 @@
full-duplex;
};
};
+
+ port@8 {
+ reg = <8>;
+ phy-mode = "internal";
+ brcm,use-bcm-hdr;
+ ethernet = <&gmac>;
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ };
};
&mdio {
--- a/arch/arm64/boot/dts/broadcom/bcm4908/bcm4908.dtsi
+++ b/arch/arm64/boot/dts/broadcom/bcm4908/bcm4908.dtsi
@@ -112,6 +112,14 @@
@@ -112,6 +112,13 @@
#size-cells = <1>;
ranges = <0x00 0x00 0x80000000 0x281000>;
+ gmac: ethernet@2000 {
+ compatible = "brcm,bcm4908enet";
+ enet: ethernet@2000 {
+ compatible = "brcm,bcm4908-enet";
+ reg = <0x2000 0x1000>;
+
+ interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "rx";
+ };
+
usb_phy: usb-phy@c200 {
compatible = "brcm,bcm4908-usb-phy";
reg = <0xc200 0x100>;
@@ -199,6 +206,18 @@
phy-mode = "internal";
phy-handle = <&phy11>;
};
+
+ port@8 {
+ reg = <8>;
+ phy-mode = "internal";
+ brcm,use-bcm-hdr;
+ ethernet = <&enet>;
+
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ };
};
};