Skip to content

Commit

Permalink
Merge branch 'Add-ACPI-bindings-to-the-genet'
Browse files Browse the repository at this point in the history
Jeremy Linton says:

====================
Add ACPI bindings to the genet

This patch series allows the BCM GENET, as used on the RPi4,
to attach when booted in an ACPI environment. The DSDT entry to
trigger this is seen below. Of note, the first patch adds a
small extension to the mdio layer which allows drivers to find
the mii_bus without firmware assistance. The fifth patch in
the set retrieves the MAC address from the umac registers
rather than carrying it directly in the DSDT. This of course
requires the firmware to pre-program it, so we continue to fall
back on a random one if it appears to be garbage.

v1 -> v2:
     fail on missing phy-mode property
     replace phy-mode internal property read string with
     	     device_get_phy_mode() equivalent
     rework mac address detection logic so that it merges
     	    the acpi/DT case into device_get_mac_address()
	    allowing _DSD mac address properties.
     some commit messages justifying why phy_find_first()
     	    isn't the worst choice for this driver.

+    Device (ETH0)
+    {
+      Name (_HID, "BCM6E4E")
+      Name (_UID, 0)
+      Name (_CCA, 0x0)
+      Method (_STA)
+      {
+        Return (0xf)
+      }
+      Method (_CRS, 0x0, Serialized)
+      {
+        Name (RBUF, ResourceTemplate ()
+        {
+          Memory32Fixed (ReadWrite, 0xFd580000, 0x10000, )
+          Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive) { 0xBD }
+          Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive) { 0xBE }
+        })
+        Return (RBUF)
+      }
+      Name (_DSD, Package () {
+        ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+          Package () {
+          Package () { "phy-mode", "rgmii-rxid" },
+        }
+      })
+    }
====================

Tested-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Feb 24, 2020
2 parents cc5b48b + ae200c2 commit e2763bc
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 42 deletions.
62 changes: 42 additions & 20 deletions drivers/net/ethernet/broadcom/genet/bcmgenet.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#define pr_fmt(fmt) "bcmgenet: " fmt

#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
Expand Down Expand Up @@ -2771,6 +2772,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)
{
Expand Down Expand Up @@ -3466,10 +3482,8 @@ static int bcmgenet_probe(struct platform_device *pdev)
const struct bcmgenet_plat_data *pdata;
struct bcmgenet_priv *priv;
struct net_device *dev;
const void *macaddr;
unsigned int i;
int err = -EIO;
const char *phy_mode_str;

/* Up to GENET_MAX_MQ_CNT + 1 TX queues and RX queues */
dev = alloc_etherdev_mqs(sizeof(*priv), GENET_MAX_MQ_CNT + 1,
Expand Down Expand Up @@ -3498,11 +3512,6 @@ static int bcmgenet_probe(struct platform_device *pdev)
}
priv->wol_irq = platform_get_irq_optional(pdev, 2);

if (dn)
macaddr = of_get_mac_address(dn);
else
macaddr = pd->mac_address;

priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base)) {
err = PTR_ERR(priv->base);
Expand All @@ -3513,12 +3522,6 @@ static int bcmgenet_probe(struct platform_device *pdev)

SET_NETDEV_DEV(dev, &pdev->dev);
dev_set_drvdata(&pdev->dev, dev);
if (IS_ERR_OR_NULL(macaddr) || !is_valid_ether_addr(macaddr)) {
dev_warn(&pdev->dev, "using random Ethernet MAC\n");
eth_hw_addr_random(dev);
} else {
ether_addr_copy(dev->dev_addr, macaddr);
}
dev->watchdog_timeo = 2 * HZ;
dev->ethtool_ops = &bcmgenet_ethtool_ops;
dev->netdev_ops = &bcmgenet_netdev_ops;
Expand Down Expand Up @@ -3547,8 +3550,9 @@ static int bcmgenet_probe(struct platform_device *pdev)

priv->dev = dev;
priv->pdev = pdev;
if (of_id) {
pdata = of_id->data;

pdata = device_get_match_data(&pdev->dev);
if (pdata) {
priv->version = pdata->version;
priv->dma_max_burst_length = pdata->dma_max_burst_length;
} else {
Expand All @@ -3558,7 +3562,7 @@ static int bcmgenet_probe(struct platform_device *pdev)

priv->clk = devm_clk_get(&priv->pdev->dev, "enet");
if (IS_ERR(priv->clk)) {
dev_warn(&priv->pdev->dev, "failed to get enet clock\n");
dev_dbg(&priv->pdev->dev, "failed to get enet clock\n");
priv->clk = NULL;
}

Expand All @@ -3582,23 +3586,34 @@ static int bcmgenet_probe(struct platform_device *pdev)

priv->clk_wol = devm_clk_get(&priv->pdev->dev, "enet-wol");
if (IS_ERR(priv->clk_wol)) {
dev_warn(&priv->pdev->dev, "failed to get enet-wol clock\n");
dev_dbg(&priv->pdev->dev, "failed to get enet-wol clock\n");
priv->clk_wol = NULL;
}

priv->clk_eee = devm_clk_get(&priv->pdev->dev, "enet-eee");
if (IS_ERR(priv->clk_eee)) {
dev_warn(&priv->pdev->dev, "failed to get enet-eee clock\n");
dev_dbg(&priv->pdev->dev, "failed to get enet-eee clock\n");
priv->clk_eee = NULL;
}

/* 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_get_phy_mode(&pdev->dev) == PHY_INTERFACE_MODE_INTERNAL)
bcmgenet_power_up(priv, GENET_POWER_PASSIVE);

if ((pd) && (!IS_ERR_OR_NULL(pd->mac_address)))
ether_addr_copy(dev->dev_addr, pd->mac_address);
else
if (!device_get_mac_address(&pdev->dev, dev->dev_addr, ETH_ALEN))
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);
}

reset_umac(priv);

err = bcmgenet_mii_init(dev);
Expand Down Expand Up @@ -3771,6 +3786,12 @@ static int bcmgenet_suspend(struct device *d)

static SIMPLE_DEV_PM_OPS(bcmgenet_pm_ops, bcmgenet_suspend, bcmgenet_resume);

static const struct acpi_device_id genet_acpi_match[] = {
{ "BCM6E4E", (kernel_ulong_t)&bcm2711_plat_data },
{ },
};
MODULE_DEVICE_TABLE(acpi, genet_acpi_match);

static struct platform_driver bcmgenet_driver = {
.probe = bcmgenet_probe,
.remove = bcmgenet_remove,
Expand All @@ -3779,6 +3800,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);
Expand Down
81 changes: 59 additions & 22 deletions drivers/net/ethernet/broadcom/genet/bcmmii.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Copyright (c) 2014-2017 Broadcom
*/


#include <linux/acpi.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/wait.h>
Expand Down Expand Up @@ -311,7 +311,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;
Expand All @@ -334,7 +335,27 @@ int bcmgenet_mii_probe(struct net_device *dev)
return -ENODEV;
}
} else {
phydev = dev->phydev;
if (has_acpi_companion(kdev)) {
char mdio_bus_id[MII_BUS_ID_SIZE];
struct mii_bus *unimacbus;

snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
UNIMAC_MDIO_DRV_NAME, priv->pdev->id);

unimacbus = mdio_find_bus(mdio_bus_id);
if (!unimacbus) {
pr_err("Unable to find mii\n");
return -ENODEV;
}
phydev = phy_find_first(unimacbus);
put_device(&unimacbus->dev);
if (!phydev) {
pr_err("Unable to find PHY\n");
return -ENODEV;
}
} else {
phydev = dev->phydev;
}
phydev->dev_flags = phy_flags;

ret = phy_connect_direct(dev, phydev, bcmgenet_mii_setup,
Expand Down Expand Up @@ -455,9 +476,12 @@ static int bcmgenet_mii_register(struct bcmgenet_priv *priv)
/* Retain this platform_device pointer for later cleanup */
priv->mii_pdev = ppdev;
ppdev->dev.parent = &pdev->dev;
ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv);
if (pdata)
if (dn)
ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv);
else if (pdata)
bcmgenet_mii_pdata_init(priv, &ppd);
else
ppd.phy_mask = ~0;

ret = platform_device_add_resources(ppdev, &res, 1);
if (ret)
Expand All @@ -477,12 +501,33 @@ static int bcmgenet_mii_register(struct bcmgenet_priv *priv)
return ret;
}

static int bcmgenet_phy_interface_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
int phy_mode = device_get_phy_mode(kdev);

if (phy_mode < 0) {
dev_err(kdev, "invalid PHY mode property\n");
return phy_mode;
}

priv->phy_interface = phy_mode;

/* We need to specifically look up whether this PHY interface is
* internal or not *before* we even try to probe the PHY driver
* over MDIO as we may have shut down the internal PHY for power
* saving purposes.
*/
if (priv->phy_interface == PHY_INTERFACE_MODE_INTERNAL)
priv->internal_phy = true;

return 0;
}

static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv)
{
struct device_node *dn = priv->pdev->dev.of_node;
struct device *kdev = &priv->pdev->dev;
struct phy_device *phydev;
phy_interface_t phy_mode;
int ret;

/* Fetch the PHY phandle */
Expand All @@ -500,23 +545,12 @@ static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv)
}

/* Get the link mode */
ret = of_get_phy_mode(dn, &phy_mode);
if (ret) {
dev_err(kdev, "invalid PHY mode property\n");
ret = bcmgenet_phy_interface_init(priv);
if (ret)
return ret;
}

priv->phy_interface = phy_mode;

/* We need to specifically look up whether this PHY interface is internal
* or not *before* we even try to probe the PHY driver over MDIO as we
* may have shut down the internal PHY for power saving purposes.
*/
if (priv->phy_interface == PHY_INTERFACE_MODE_INTERNAL)
priv->internal_phy = true;

/* Make sure we initialize MoCA PHYs with a link down */
if (phy_mode == PHY_INTERFACE_MODE_MOCA) {
if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) {
phydev = of_phy_find_device(dn);
if (phydev) {
phydev->link = 0;
Expand Down Expand Up @@ -581,10 +615,13 @@ static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)

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_phy_interface_init(priv);
else
return bcmgenet_mii_pd_init(priv);
}
Expand Down
17 changes: 17 additions & 0 deletions drivers/net/phy/mdio_bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,23 @@ static struct class mdio_bus_class = {
.dev_groups = mdio_bus_groups,
};

/**
* mdio_find_bus - Given the name of a mdiobus, find the mii_bus.
* @mdio_bus_np: Pointer to the mii_bus.
*
* Returns a reference to the mii_bus, or NULL if none found. The
* embedded struct device will have its reference count incremented,
* and this must be put_deviced'ed once the bus is finished with.
*/
struct mii_bus *mdio_find_bus(const char *mdio_name)
{
struct device *d;

d = class_find_device_by_name(&mdio_bus_class, mdio_name);
return d ? to_mii_bus(d) : NULL;
}
EXPORT_SYMBOL(mdio_find_bus);

#if IS_ENABLED(CONFIG_OF_MDIO)
/**
* of_mdio_find_bus - Given an mii_bus node, find the mii_bus.
Expand Down
1 change: 1 addition & 0 deletions include/linux/phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ static inline struct mii_bus *devm_mdiobus_alloc(struct device *dev)
return devm_mdiobus_alloc_size(dev, 0);
}

struct mii_bus *mdio_find_bus(const char *mdio_name);
void devm_mdiobus_free(struct device *dev, struct mii_bus *bus);
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);

Expand Down

0 comments on commit e2763bc

Please sign in to comment.