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

Bug#950578: Updated patch



Here's an updated version of the patch, since we switched to using "rgmii-rxid" instead of "rgmii" for phy-mode in ACPI (which is what the current Linux Device Tree also uses and which prevents packet loss and network instability) and the 4.19 bcmmii driver is missing a case for PHY_INTERFACE_MODE_RGMII_RXID.

We also use this opportunity to fix a possible warning in bcmgenet_mii_acpi_init().

Once again, it would be greatly appreciated if this patch could be applied as a matter of priority as it's all that stands between making the next release of Debian net installable on a Raspberry Pi 4.
From 2d780b0bec254002221559e51cc0b6e5038d4f1a 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  | 40 +++++++++-
 drivers/net/phy/mdio-bcm-unimac.c             | 26 ++++++-
 3 files changed, 118 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..b764444474d9 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>
@@ -250,6 +250,11 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
 		bcmgenet_sys_writel(priv,
 				    PORT_MODE_EXT_GPHY, SYS_PORT_CTRL);
 		break;
+	case PHY_INTERFACE_MODE_RGMII_RXID:
+		phy_name = "external RGMII (RX delay)";
+		bcmgenet_sys_writel(priv,
+				    PORT_MODE_EXT_GPHY, SYS_PORT_CTRL);
+		break;
 	default:
 		dev_err(kdev, "unknown phy mode: %d\n", priv->phy_interface);
 		return -EINVAL;
@@ -277,7 +282,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 +305,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 +565,26 @@ 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;
+	int phy_mode = device_get_phy_mode(kdev);
+	if (phy_mode < 0)
+		priv->phy_interface = PHY_INTERFACE_MODE_RGMII;
+	else
+		priv->phy_interface = phy_mode;
+	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: