Commit e2763bc4 authored by David S. Miller's avatar David S. Miller

Merge branch 'Add-ACPI-bindings-to-the-genet'

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: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents cc5b48b5 ae200c26
......@@ -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>
......@@ -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)
{
......@@ -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,
......@@ -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);
......@@ -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;
......@@ -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 {
......@@ -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;
}
......@@ -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);
......@@ -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,
......@@ -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);
......
......@@ -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>
......@@ -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;
......@@ -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,
......@@ -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)
......@@ -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 */
......@@ -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;
......@@ -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);
}
......
......@@ -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.
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment