[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#950578: linux-image-4.19.67-2-arm64: Add ACPI network interface support for RPi4 (patch attached)



Package: src:linux
Version: 4.19.67-2
Severity: important
Tags: patch


Dear Maintainers,

While it is currently possible to boot and install Debian 10.x on the Raspberry Pi 4 using the official vanilla ARM64 ISO images (through the use of the latest EDK2 RPi4 firmware such as the one provided at [1]), one of the major drawbacks that exists is that, because the EDK2 firmware currently only supports Linux boot in ACPI mode and also because the native Debian kernel does not include the bcmgenet module at all (module is currently disabled altogether), it is not possible to perform a networked installation of Debian on the Raspberry Pi 4.

Due to the popularity of the platform, we therefore suggest that bcmgenet should be enabled for the next Debian ARM64 10.x release as a matter of priority, after the the attached patch has been applied to the default kernel as some modifications are required for the network interface to work in ACPI mode in coordination with the EDK2 firmware.

With bcmgenet is currently being disabled as a module, and with the effort we made to ensure that the modifications applied have the least impact possible on existing code (the existing Device Tree code paths are left virtually untouched), we assert that the inclusion of this patch should be low risk and therefore very desirable to have for the potentially large number of RPi4 users, who should then be able to perform netinst installation of Debian 10.x on their platform using the vanilla ARM64 images.

Thanks and regards,

/Pete

[1] https://github.com/pftf/RPi4

-- System Information:
Debian Release: 10.2
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable')
Architecture: arm64 (aarch64)

Kernel: Linux 4.19.87 (SMP w/4 CPU cores)
Kernel taint flags: TAINT_UNSIGNED_MODULE
Locale: LANG=en_IE.UTF-8, LC_CTYPE=en_IE.UTF-8 (charmap=UTF-8), LANGUAGE=en_IE:en (charmap=UTF-8)
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled
From d2fecdc2828dc0256df7c10e02fc6e7aaf99ce9a Mon Sep 17 00:00:00 2001
From: Jeremy Linton <lintonrjeremy@gmail.com>
Date: Mon, 3 Feb 2020 17:39:14 +0000
Subject: [PATCH] net: bcmgenet: add ACPI bindings

This patch is needed to support the Raspberry Pi 4 network interface on
native Debian 4.19 kernels in ACPI mode. With this, network installation
of Debian 10.x, from vanilla ARM64 ISO images, should become possible
when using the official EDK2 UEFI firmware for the Raspberry Pi 4, which
currently requires the use of ACPI when booting Linux.

The changes covered by this patch can be summarized as follows:
* Since the 4.19 Genet driver private data structures are lacking this
  attribute compared to the 5.x version, and this is the least intrusive
  way of compensating for that, the max DMA burst length must be allowed
  to be overridden, when it is specified as an ACPI DSD parameter (which
  the UEFI firmware will provide accordingly).
* The MAC address must be allowed to be set from the hardware's UMAC
  registers (which the UEFI firmware will program accordingly).
  Note that we grouped the MAC initialization further down the code for
  readability and added random allocation on invalid MAC, rather than
  report an error, as this is what the current 5.x driver does.
* The relevant ACPI initialization structures must exist in the driver.

We also take this opportunity to add an entry for brcm,bcm2711-genet-v5
in the Device Tree list, which is how the Raspberry Pi Foundation
declares the network interface (though using it as such in a 4.19 kernel
will require the provision of the "brcm,max-dma-burst-size" attribute).

Signed-off-by: Pete Batard <pete@akeo.ie>
---
 .../net/ethernet/broadcom/genet/bcmgenet.c    | 74 ++++++++++++++-----
 drivers/net/ethernet/broadcom/genet/bcmmii.c  | 33 ++++++++-
 drivers/net/phy/mdio-bcm-unimac.c             | 26 ++++++-
 3 files changed, 111 insertions(+), 22 deletions(-)

diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 338d22380434..2b4b2906a9b9 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -10,6 +10,7 @@
 
 #define pr_fmt(fmt)				"bcmgenet: " fmt
 
+#include <linux/acpi.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/sched.h>
@@ -2553,6 +2554,7 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv)
 	int ret;
 	unsigned int i;
 	struct enet_cb *cb;
+	u32 dma_max_burst_length;
 
 	netif_dbg(priv, hw, priv->dev, "%s\n", __func__);
 
@@ -2584,8 +2586,13 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv)
 		cb->bd_addr = priv->tx_bds + i * DMA_DESC_SIZE;
 	}
 
+	/* Set the max DMA burst length if specified. Else, use default */
+	if (!priv->pdev || device_property_read_u32(&priv->pdev->dev,
+	    "brcm,max-dma-burst-size", &dma_max_burst_length) != 0)
+		dma_max_burst_length = DMA_MAX_BURST_LENGTH;
+
 	/* Init rDma */
-	bcmgenet_rdma_writel(priv, DMA_MAX_BURST_LENGTH, DMA_SCB_BURST_SIZE);
+	bcmgenet_rdma_writel(priv, dma_max_burst_length, DMA_SCB_BURST_SIZE);
 
 	/* Initialize Rx queues */
 	ret = bcmgenet_init_rx_queues(priv->dev);
@@ -2598,7 +2605,7 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv)
 	}
 
 	/* Init tDma */
-	bcmgenet_tdma_writel(priv, DMA_MAX_BURST_LENGTH, DMA_SCB_BURST_SIZE);
+	bcmgenet_tdma_writel(priv, dma_max_burst_length, DMA_SCB_BURST_SIZE);
 
 	/* Initialize Tx queues */
 	bcmgenet_init_tx_queues(priv->dev);
@@ -2783,6 +2790,21 @@ static void bcmgenet_set_hw_addr(struct bcmgenet_priv *priv,
 	bcmgenet_umac_writel(priv, (addr[4] << 8) | addr[5], UMAC_MAC1);
 }
 
+static void bcmgenet_get_hw_addr(struct bcmgenet_priv *priv,
+				 unsigned char *addr)
+{
+	u32 addr_tmp;
+
+	addr_tmp = bcmgenet_umac_readl(priv, UMAC_MAC0);
+	addr[0] = addr_tmp >> 24;
+	addr[1] = (addr_tmp >> 16) & 0xff;
+	addr[2] = (addr_tmp >>	8) & 0xff;
+	addr[3] = addr_tmp & 0xff;
+	addr_tmp = bcmgenet_umac_readl(priv, UMAC_MAC1);
+	addr[4] = (addr_tmp >> 8) & 0xff;
+	addr[5] = addr_tmp & 0xff;
+}
+
 /* Returns a reusable dma control register value */
 static u32 bcmgenet_dma_disable(struct bcmgenet_priv *priv)
 {
@@ -3433,10 +3455,17 @@ static const struct of_device_id bcmgenet_match[] = {
 	{ .compatible = "brcm,genet-v3", .data = (void *)GENET_V3 },
 	{ .compatible = "brcm,genet-v4", .data = (void *)GENET_V4 },
 	{ .compatible = "brcm,genet-v5", .data = (void *)GENET_V5 },
+	{ .compatible = "brcm,bcm2711-genet-v5", .data = (void *)GENET_V5 },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, bcmgenet_match);
 
+static const struct acpi_device_id genet_acpi_match[] = {
+	{ "BCM6E4E", (kernel_ulong_t)GENET_V5 },
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, genet_acpi_match);
+
 static int bcmgenet_probe(struct platform_device *pdev)
 {
 	struct bcmgenet_platform_data *pd = pdev->dev.platform_data;
@@ -3444,7 +3473,7 @@ static int bcmgenet_probe(struct platform_device *pdev)
 	const struct of_device_id *of_id = NULL;
 	struct bcmgenet_priv *priv;
 	struct net_device *dev;
-	const void *macaddr;
+	const void *macaddr = NULL;
 	struct resource *r;
 	unsigned int i;
 	int err = -EIO;
@@ -3474,17 +3503,6 @@ static int bcmgenet_probe(struct platform_device *pdev)
 		goto err;
 	}
 
-	if (dn) {
-		macaddr = of_get_mac_address(dn);
-		if (!macaddr) {
-			dev_err(&pdev->dev, "can't find MAC address\n");
-			err = -EINVAL;
-			goto err;
-		}
-	} else {
-		macaddr = pd->mac_address;
-	}
-
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	priv->base = devm_ioremap_resource(&pdev->dev, r);
 	if (IS_ERR(priv->base)) {
@@ -3496,7 +3514,6 @@ static int bcmgenet_probe(struct platform_device *pdev)
 
 	SET_NETDEV_DEV(dev, &pdev->dev);
 	dev_set_drvdata(&pdev->dev, dev);
-	ether_addr_copy(dev->dev_addr, macaddr);
 	dev->watchdog_timeo = 2 * HZ;
 	dev->ethtool_ops = &bcmgenet_ethtool_ops;
 	dev->netdev_ops = &bcmgenet_netdev_ops;
@@ -3525,8 +3542,11 @@ static int bcmgenet_probe(struct platform_device *pdev)
 	priv->pdev = pdev;
 	if (of_id)
 		priv->version = (enum bcmgenet_version)of_id->data;
-	else
+	else if (pd)
 		priv->version = pd->genet_version;
+	else
+		priv->version = (enum bcmgenet_version)
+			device_get_match_data(&pdev->dev);
 
 	priv->clk = devm_clk_get(&priv->pdev->dev, "enet");
 	if (IS_ERR(priv->clk)) {
@@ -3559,10 +3579,27 @@ static int bcmgenet_probe(struct platform_device *pdev)
 	/* If this is an internal GPHY, power it on now, before UniMAC is
 	 * brought out of reset as absolutely no UniMAC activity is allowed
 	 */
-	if (dn && !of_property_read_string(dn, "phy-mode", &phy_mode_str) &&
-	    !strcasecmp(phy_mode_str, "internal"))
+	if (device_property_read_string(&pdev->dev, "phy-mode",
+	    &phy_mode_str) == 0 && !strcasecmp(phy_mode_str, "internal"))
 		bcmgenet_power_up(priv, GENET_POWER_PASSIVE);
 
+	if (dn)
+		macaddr = of_get_mac_address(dn);
+	else if (pd)
+		macaddr = pd->mac_address;
+
+	if (IS_ERR_OR_NULL(macaddr) || !is_valid_ether_addr(macaddr)) {
+		if (has_acpi_companion(&pdev->dev))
+			bcmgenet_get_hw_addr(priv, dev->dev_addr);
+
+		if (!is_valid_ether_addr(dev->dev_addr)) {
+			dev_warn(&pdev->dev, "using random Ethernet MAC\n");
+			eth_hw_addr_random(dev);
+		}
+	} else {
+		ether_addr_copy(dev->dev_addr, macaddr);
+	}
+
 	reset_umac(priv);
 
 	err = bcmgenet_mii_init(dev);
@@ -3730,6 +3767,7 @@ static struct platform_driver bcmgenet_driver = {
 		.name	= "bcmgenet",
 		.of_match_table = bcmgenet_match,
 		.pm	= &bcmgenet_pm_ops,
+		.acpi_match_table = ACPI_PTR(genet_acpi_match),
 	},
 };
 module_platform_driver(bcmgenet_driver);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index b0592fd4135b..3b3d412b0e4a 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -8,7 +8,7 @@
  * published by the Free Software Foundation.
  */
 
-
+#include <linux/acpi.h>
 #include <linux/types.h>
 #include <linux/delay.h>
 #include <linux/wait.h>
@@ -277,7 +277,8 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
 int bcmgenet_mii_probe(struct net_device *dev)
 {
 	struct bcmgenet_priv *priv = netdev_priv(dev);
-	struct device_node *dn = priv->pdev->dev.of_node;
+	struct device *kdev = &priv->pdev->dev;
+	struct device_node *dn = kdev->of_node;
 	struct phy_device *phydev;
 	u32 phy_flags = 0;
 	int ret;
@@ -299,6 +300,20 @@ int bcmgenet_mii_probe(struct net_device *dev)
 			pr_err("could not attach to PHY\n");
 			return -ENODEV;
 		}
+	} else if (has_acpi_companion(kdev)) {
+		char phy_name[MII_BUS_ID_SIZE + 3];
+		char mdio_bus_id[MII_BUS_ID_SIZE];
+
+		snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
+			 UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
+		snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, 1);
+
+		phydev = phy_connect(dev, phy_name, bcmgenet_mii_setup,
+				     priv->phy_interface);
+		if (!IS_ERR(phydev))
+			phydev->dev_flags = phy_flags;
+		else
+			return -ENODEV;
 	} else {
 		phydev = dev->phydev;
 		phydev->dev_flags = phy_flags;
@@ -545,12 +560,24 @@ static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
 	return 0;
 }
 
+static int bcmgenet_mii_acpi_init(struct bcmgenet_priv *priv)
+{
+	struct device *kdev = &priv->pdev->dev;
+	priv->phy_interface = device_get_phy_mode(kdev);
+	if (priv->phy_interface < 0)
+		priv->phy_interface = PHY_INTERFACE_MODE_RGMII;
+	return 0;
+}
+
 static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv)
 {
-	struct device_node *dn = priv->pdev->dev.of_node;
+	struct device *kdev = &priv->pdev->dev;
+	struct device_node *dn = kdev->of_node;
 
 	if (dn)
 		return bcmgenet_mii_of_init(priv);
+	else if (has_acpi_companion(kdev))
+		return bcmgenet_mii_acpi_init(priv);
 	else
 		return bcmgenet_mii_pd_init(priv);
 }
diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c
index df75efa96a7d..b71fbc42e324 100644
--- a/drivers/net/phy/mdio-bcm-unimac.c
+++ b/drivers/net/phy/mdio-bcm-unimac.c
@@ -294,9 +294,33 @@ static int unimac_mdio_probe(struct platform_device *pdev)
 		goto out_mdio_free;
 	}
 
+	if (!np) {
+		struct phy_device *phy_dev;
+		int phy_addr_check;
+
+		for (phy_addr_check = 0; phy_addr_check < PHY_MAX_ADDR; phy_addr_check++) {
+			phy_dev = get_phy_device(bus, phy_addr_check ,false);
+			if (IS_ERR(phy_dev)) {
+				if (phy_addr_check == PHY_MAX_ADDR - 1) {
+					dev_err(&pdev->dev, "MDIO phy search failed\n");
+					goto out_mdio_free;
+				}
+			} else {
+				dev_dbg(&pdev->dev, "MDIO phy success @ %d\n", phy_addr_check);
+				break;
+			}
+		}
+
+		if (phy_device_register(phy_dev)) {
+			dev_err(&pdev->dev, "MDIO phy register failed\n");
+			phy_device_free(phy_dev);
+			goto out_mdio_free;
+		}
+	}
+
 	platform_set_drvdata(pdev, priv);
 
-	dev_info(&pdev->dev, "Broadcom UniMAC MDIO bus at 0x%p\n", priv->base);
+	dev_info(&pdev->dev, "Broadcom UniMAC MDIO bus id=%s\n", bus->id);
 
 	return 0;
 
-- 
2.21.0.windows.1


Reply to: