Commit 975f6262 authored by David S. Miller's avatar David S. Miller

Merge branch 'dpaa2-ACPI'

Ioana Ciornei says:

====================
ACPI support for dpaa2 driver

This patch set provides ACPI support to DPAA2 network drivers.

It also introduces new fwnode based APIs to support phylink and phy
layers
    Following functions are defined:
      phylink_fwnode_phy_connect()
      fwnode_mdiobus_register_phy()
      fwnode_get_phy_id()
      fwnode_phy_find_device()
      device_phy_find_device()
      fwnode_get_phy_node()
      fwnode_mdio_find_device()
      acpi_get_local_address()

    First one helps in connecting phy to phylink instance.
    Next three helps in getting phy_id and registering phy to mdiobus
    Next two help in finding a phy on a mdiobus.
    Next one helps in getting phy_node from a fwnode.
    Last one is used to get local address from _ADR object.

    Corresponding OF functions are refactored.

Tested-on: LX2160ARDB

Changes in v9:
 - merged some minimal changes requested in the wording of the commit
   messages
 - fixed some build problems in patch 8/15 by moving the removal of
   of_find_mii_timestamper from patch 8/15 to 9/15.

Changes in v8:
 - fixed some checkpatch warnings/checks
 - included linux/fwnode_mdio.h in fwnode_mdio.c (fixed the build warnings)
 - added fwnode_find_mii_timestamper() and
   fwnode_mdiobus_phy_device_register() in order to get rid of the cycle
   dependency.
 - change to 'depends on (ACPI || OF) || COMPILE_TEST (for FWNODE_MDIO)
 - remove the fwnode_mdiobus_register from fwnode_mdio.c since it
   introduces a cycle of dependencies.

Changes in v7:
- correct fwnode_mdio_find_device() description
- check NULL in unregister_mii_timestamper()
- Call unregister_mii_timestamper() without NULL check
- Create fwnode_mdio.c and move fwnode_mdiobus_register_phy()
- include fwnode_mdio.h
- Include headers directly used in acpi_mdio.c
- Move fwnode_mdiobus_register() to fwnode_mdio.c
- Include fwnode_mdio.h
- Alphabetically sort header inclusions
- remove unnecassary checks

Changes in v6:
- Minor cleanup
- fix warning for function parameter of fwnode_mdio_find_device()
- Initialize mii_ts to NULL
- use GENMASK() and ACPI_COMPANION_SET()
- some cleanup
- remove unwanted header inclusion
- remove OF check for fixed-link
- use dev_fwnode()
- remove useless else
- replace of_device_is_available() to fwnode_device_is_available()

Changes in v5:
- More cleanup
- Replace fwnode_get_id() with acpi_get_local_address()
- add missing MODULE_LICENSE()
- replace fwnode_get_id() with OF and ACPI function calls
- replace fwnode_get_id() with OF and ACPI function calls

Changes in v4:
- More cleanup
- Improve code structure to handle all cases
- Remove redundant else from fwnode_mdiobus_register()
- Cleanup xgmac_mdio_probe()
- call phy_device_free() before returning

Changes in v3:
- Add more info on legacy DT properties "phy" and "phy-device"
- Redefine fwnode_phy_find_device() to follow of_phy_find_device()
- Use traditional comparison pattern
- Use GENMASK
- Modified to retrieve reg property value for ACPI as well
- Resolved compilation issue with CONFIG_ACPI = n
- Added more info into documentation
- Use acpi_mdiobus_register()
- Avoid unnecessary line removal
- Remove unused inclusion of acpi.h

Changes in v2:
- Updated with more description in document
- use reverse christmas tree ordering for local variables
- Refactor OF functions to use fwnode functions
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3e6dc7b6 3264f599
.. SPDX-License-Identifier: GPL-2.0
=========================
MDIO bus and PHYs in ACPI
=========================
The PHYs on an MDIO bus [1] are probed and registered using
fwnode_mdiobus_register_phy().
Later, for connecting these PHYs to their respective MACs, the PHYs registered
on the MDIO bus have to be referenced.
This document introduces two _DSD properties that are to be used
for connecting PHYs on the MDIO bus [3] to the MAC layer.
These properties are defined in accordance with the "Device
Properties UUID For _DSD" [2] document and the
daffd814-6eba-4d8c-8a91-bc9bbf4aa301 UUID must be used in the Device
Data Descriptors containing them.
phy-handle
----------
For each MAC node, a device property "phy-handle" is used to reference
the PHY that is registered on an MDIO bus. This is mandatory for
network interfaces that have PHYs connected to MAC via MDIO bus.
During the MDIO bus driver initialization, PHYs on this bus are probed
using the _ADR object as shown below and are registered on the MDIO bus.
::
Scope(\_SB.MDI0)
{
Device(PHY1) {
Name (_ADR, 0x1)
} // end of PHY1
Device(PHY2) {
Name (_ADR, 0x2)
} // end of PHY2
}
Later, during the MAC driver initialization, the registered PHY devices
have to be retrieved from the MDIO bus. For this, the MAC driver needs
references to the previously registered PHYs which are provided
as device object references (e.g. \_SB.MDI0.PHY1).
phy-mode
--------
The "phy-mode" _DSD property is used to describe the connection to
the PHY. The valid values for "phy-mode" are defined in [4].
The following ASL example illustrates the usage of these properties.
DSDT entry for MDIO node
------------------------
The MDIO bus has an SoC component (MDIO controller) and a platform
component (PHYs on the MDIO bus).
a) Silicon Component
This node describes the MDIO controller, MDI0
---------------------------------------------
::
Scope(_SB)
{
Device(MDI0) {
Name(_HID, "NXP0006")
Name(_CCA, 1)
Name(_UID, 0)
Name(_CRS, ResourceTemplate() {
Memory32Fixed(ReadWrite, MDI0_BASE, MDI_LEN)
Interrupt(ResourceConsumer, Level, ActiveHigh, Shared)
{
MDI0_IT
}
}) // end of _CRS for MDI0
} // end of MDI0
}
b) Platform Component
The PHY1 and PHY2 nodes represent the PHYs connected to MDIO bus MDI0
---------------------------------------------------------------------
::
Scope(\_SB.MDI0)
{
Device(PHY1) {
Name (_ADR, 0x1)
} // end of PHY1
Device(PHY2) {
Name (_ADR, 0x2)
} // end of PHY2
}
DSDT entries representing MAC nodes
-----------------------------------
Below are the MAC nodes where PHY nodes are referenced.
phy-mode and phy-handle are used as explained earlier.
------------------------------------------------------
::
Scope(\_SB.MCE0.PR17)
{
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package (2) {"phy-mode", "rgmii-id"},
Package (2) {"phy-handle", \_SB.MDI0.PHY1}
}
})
}
Scope(\_SB.MCE0.PR18)
{
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package (2) {"phy-mode", "rgmii-id"},
Package (2) {"phy-handle", \_SB.MDI0.PHY2}}
}
})
}
References
==========
[1] Documentation/networking/phy.rst
[2] https://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf
[3] Documentation/firmware-guide/acpi/DSD-properties-rules.rst
[4] Documentation/devicetree/bindings/net/ethernet-controller.yaml
......@@ -6811,6 +6811,8 @@ F: Documentation/devicetree/bindings/net/mdio*
F: Documentation/devicetree/bindings/net/qca,ar803x.yaml
F: Documentation/networking/phy.rst
F: drivers/net/mdio/
F: drivers/net/mdio/acpi_mdio.c
F: drivers/net/mdio/fwnode_mdio.c
F: drivers/net/mdio/of_mdio.c
F: drivers/net/pcs/
F: drivers/net/phy/
......
......@@ -277,6 +277,20 @@ acpi_evaluate_integer(acpi_handle handle,
EXPORT_SYMBOL(acpi_evaluate_integer);
int acpi_get_local_address(acpi_handle handle, u32 *addr)
{
unsigned long long adr;
acpi_status status;
status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
if (ACPI_FAILURE(status))
return -ENODATA;
*addr = (u32)adr;
return 0;
}
EXPORT_SYMBOL(acpi_get_local_address);
acpi_status
acpi_evaluate_reference(acpi_handle handle,
acpi_string pathname,
......
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2019 NXP */
#include <linux/acpi.h>
#include <linux/property.h>
#include "dpaa2-eth.h"
#include "dpaa2-mac.h"
......@@ -34,39 +37,51 @@ static int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode)
return 0;
}
/* Caller must call of_node_put on the returned value */
static struct device_node *dpaa2_mac_get_node(u16 dpmac_id)
static struct fwnode_handle *dpaa2_mac_get_node(struct device *dev,
u16 dpmac_id)
{
struct device_node *dpmacs, *dpmac = NULL;
u32 id;
struct fwnode_handle *fwnode, *parent, *child = NULL;
struct device_node *dpmacs = NULL;
int err;
u32 id;
dpmacs = of_find_node_by_name(NULL, "dpmacs");
if (!dpmacs)
return NULL;
fwnode = dev_fwnode(dev->parent);
if (is_of_node(fwnode)) {
dpmacs = of_find_node_by_name(NULL, "dpmacs");
if (!dpmacs)
return NULL;
parent = of_fwnode_handle(dpmacs);
} else if (is_acpi_node(fwnode)) {
parent = fwnode;
}
while ((dpmac = of_get_next_child(dpmacs, dpmac)) != NULL) {
err = of_property_read_u32(dpmac, "reg", &id);
fwnode_for_each_child_node(parent, child) {
err = -EINVAL;
if (is_acpi_device_node(child))
err = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &id);
else if (is_of_node(child))
err = of_property_read_u32(to_of_node(child), "reg", &id);
if (err)
continue;
if (id == dpmac_id)
break;
}
if (id == dpmac_id) {
of_node_put(dpmacs);
return child;
}
}
of_node_put(dpmacs);
return dpmac;
return NULL;
}
static int dpaa2_mac_get_if_mode(struct device_node *node,
static int dpaa2_mac_get_if_mode(struct fwnode_handle *dpmac_node,
struct dpmac_attr attr)
{
phy_interface_t if_mode;
int err;
err = of_get_phy_mode(node, &if_mode);
if (!err)
return if_mode;
err = fwnode_get_phy_mode(dpmac_node);
if (err > 0)
return err;
err = phy_mode(attr.eth_if, &if_mode);
if (!err)
......@@ -235,26 +250,27 @@ static const struct phylink_mac_ops dpaa2_mac_phylink_ops = {
};
static int dpaa2_pcs_create(struct dpaa2_mac *mac,
struct device_node *dpmac_node, int id)
struct fwnode_handle *dpmac_node,
int id)
{
struct mdio_device *mdiodev;
struct device_node *node;
struct fwnode_handle *node;
node = of_parse_phandle(dpmac_node, "pcs-handle", 0);
if (!node) {
node = fwnode_find_reference(dpmac_node, "pcs-handle", 0);
if (IS_ERR(node)) {
/* do not error out on old DTS files */
netdev_warn(mac->net_dev, "pcs-handle node not found\n");
return 0;
}
if (!of_device_is_available(node)) {
if (!fwnode_device_is_available(node)) {
netdev_err(mac->net_dev, "pcs-handle node not available\n");
of_node_put(node);
fwnode_handle_put(node);
return -ENODEV;
}
mdiodev = of_mdio_find_device(node);
of_node_put(node);
mdiodev = fwnode_mdio_find_device(node);
fwnode_handle_put(node);
if (!mdiodev)
return -EPROBE_DEFER;
......@@ -283,13 +299,13 @@ static void dpaa2_pcs_destroy(struct dpaa2_mac *mac)
int dpaa2_mac_connect(struct dpaa2_mac *mac)
{
struct net_device *net_dev = mac->net_dev;
struct device_node *dpmac_node;
struct fwnode_handle *dpmac_node;
struct phylink *phylink;
int err;
mac->if_link_type = mac->attr.link_type;
dpmac_node = mac->of_node;
dpmac_node = mac->fw_node;
if (!dpmac_node) {
netdev_err(net_dev, "No dpmac@%d node found.\n", mac->attr.id);
return -ENODEV;
......@@ -304,7 +320,7 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac)
* error out if the interface mode requests them and there is no PHY
* to act upon them
*/
if (of_phy_is_fixed_link(dpmac_node) &&
if (of_phy_is_fixed_link(to_of_node(dpmac_node)) &&
(mac->if_mode == PHY_INTERFACE_MODE_RGMII_ID ||
mac->if_mode == PHY_INTERFACE_MODE_RGMII_RXID ||
mac->if_mode == PHY_INTERFACE_MODE_RGMII_TXID)) {
......@@ -324,7 +340,7 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac)
mac->phylink_config.type = PHYLINK_NETDEV;
phylink = phylink_create(&mac->phylink_config,
of_fwnode_handle(dpmac_node), mac->if_mode,
dpmac_node, mac->if_mode,
&dpaa2_mac_phylink_ops);
if (IS_ERR(phylink)) {
err = PTR_ERR(phylink);
......@@ -335,9 +351,9 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac)
if (mac->pcs)
phylink_set_pcs(mac->phylink, &mac->pcs->pcs);
err = phylink_of_phy_connect(mac->phylink, dpmac_node, 0);
err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0);
if (err) {
netdev_err(net_dev, "phylink_of_phy_connect() = %d\n", err);
netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err);
goto err_phylink_destroy;
}
......@@ -384,8 +400,8 @@ int dpaa2_mac_open(struct dpaa2_mac *mac)
/* Find the device node representing the MAC device and link the device
* behind the associated netdev to it.
*/
mac->of_node = dpaa2_mac_get_node(mac->attr.id);
net_dev->dev.of_node = mac->of_node;
mac->fw_node = dpaa2_mac_get_node(&mac->mc_dev->dev, mac->attr.id);
net_dev->dev.of_node = to_of_node(mac->fw_node);
return 0;
......@@ -399,8 +415,8 @@ void dpaa2_mac_close(struct dpaa2_mac *mac)
struct fsl_mc_device *dpmac_dev = mac->mc_dev;
dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle);
if (mac->of_node)
of_node_put(mac->of_node);
if (mac->fw_node)
fwnode_handle_put(mac->fw_node);
}
static char dpaa2_mac_ethtool_stats[][ETH_GSTRING_LEN] = {
......
......@@ -24,7 +24,7 @@ struct dpaa2_mac {
phy_interface_t if_mode;
enum dpmac_link_type if_link_type;
struct lynx_pcs *pcs;
struct device_node *of_node;
struct fwnode_handle *fw_node;
};
bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,
......
......@@ -2,6 +2,7 @@
* QorIQ 10G MDIO Controller
*
* Copyright 2012 Freescale Semiconductor, Inc.
* Copyright 2021 NXP
*
* Authors: Andy Fleming <afleming@freescale.com>
* Timur Tabi <timur@freescale.com>
......@@ -11,15 +12,17 @@
* kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/acpi_mdio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/phy.h>
#include <linux/kernel.h>
#include <linux/mdio.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/of_mdio.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/slab.h>
/* Number of microseconds to wait for a register to respond */
#define TIMEOUT 1000
......@@ -243,10 +246,10 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
static int xgmac_mdio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct mii_bus *bus;
struct resource *res;
struct fwnode_handle *fwnode;
struct mdio_fsl_priv *priv;
struct resource *res;
struct mii_bus *bus;
int ret;
/* In DPAA-1, MDIO is one of the many FMan sub-devices. The FMan
......@@ -279,13 +282,22 @@ static int xgmac_mdio_probe(struct platform_device *pdev)
goto err_ioremap;
}
/* For both ACPI and DT cases, endianness of MDIO controller
* needs to be specified using "little-endian" property.
*/
priv->is_little_endian = device_property_read_bool(&pdev->dev,
"little-endian");
priv->has_a011043 = device_property_read_bool(&pdev->dev,
"fsl,erratum-a011043");
ret = of_mdiobus_register(bus, np);
fwnode = pdev->dev.fwnode;
if (is_of_node(fwnode))
ret = of_mdiobus_register(bus, to_of_node(fwnode));
else if (is_acpi_node(fwnode))
ret = acpi_mdiobus_register(bus, fwnode);
else
ret = -EINVAL;
if (ret) {
dev_err(&pdev->dev, "cannot register MDIO bus\n");
goto err_registration;
......
......@@ -19,6 +19,13 @@ config MDIO_BUS
reflects whether the mdio_bus/mdio_device code is built as a
loadable module or built-in.
config FWNODE_MDIO
def_tristate PHYLIB
depends on (ACPI || OF) || COMPILE_TEST
select FIXED_PHY
help
FWNODE MDIO bus (Ethernet PHY) accessors
config OF_MDIO
def_tristate PHYLIB
depends on OF
......@@ -27,6 +34,13 @@ config OF_MDIO
help
OpenFirmware MDIO bus (Ethernet PHY) accessors
config ACPI_MDIO
def_tristate PHYLIB
depends on ACPI
depends on PHYLIB
help
ACPI MDIO bus (Ethernet PHY) accessors
if MDIO_BUS
config MDIO_DEVRES
......
# SPDX-License-Identifier: GPL-2.0
# Makefile for Linux MDIO bus drivers
obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_ACPI_MDIO) += acpi_mdio.o
obj-$(CONFIG_FWNODE_MDIO) += fwnode_mdio.o
obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_MDIO_ASPEED) += mdio-aspeed.o
obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* ACPI helpers for the MDIO (Ethernet PHY) API
*
* This file provides helper functions for extracting PHY device information
* out of the ACPI ASL and using it to populate an mii_bus.
*/
#include <linux/acpi.h>
#include <linux/acpi_mdio.h>
#include <linux/bits.h>
#include <linux/dev_printk.h>
#include <linux/fwnode_mdio.h>
#include <linux/module.h>
#include <linux/types.h>
MODULE_AUTHOR("Calvin Johnson <calvin.johnson@oss.nxp.com>");
MODULE_LICENSE("GPL");
/**
* acpi_mdiobus_register - Register mii_bus and create PHYs from the ACPI ASL.
* @mdio: pointer to mii_bus structure
* @fwnode: pointer to fwnode of MDIO bus. This fwnode is expected to represent
* an ACPI device object corresponding to the MDIO bus and its children are
* expected to correspond to the PHY devices on that bus.
*
* This function registers the mii_bus structure and registers a phy_device
* for each child node of @fwnode.
*/
int acpi_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode)
{
struct fwnode_handle *child;
u32 addr;
int ret;
/* Mask out all PHYs from auto probing. */
mdio->phy_mask = GENMASK(31, 0);
ret = mdiobus_register(mdio);
if (ret)
return ret;
ACPI_COMPANION_SET(&mdio->dev, to_acpi_device_node(fwnode));
/* Loop over the child nodes and register a phy_device for each PHY */
fwnode_for_each_child_node(fwnode, child) {
ret = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &addr);
if (ret || addr >= PHY_MAX_ADDR)
continue;
ret = fwnode_mdiobus_register_phy(mdio, child, addr);
if (ret == -ENODEV)
dev_err(&mdio->dev,
"MDIO device at address %d is missing.\n",
addr);
}
return 0;
}
EXPORT_SYMBOL(acpi_mdiobus_register);
// SPDX-License-Identifier: GPL-2.0-only
/*
* fwnode helpers for the MDIO (Ethernet PHY) API
*
* This file provides helper functions for extracting PHY device information
* out of the fwnode and using it to populate an mii_bus.
*/
#include <linux/acpi.h>
#include <linux/fwnode_mdio.h>
#include <linux/of.h>
#include <linux/phy.h>
MODULE_AUTHOR("Calvin Johnson <calvin.johnson@oss.nxp.com>");
MODULE_LICENSE("GPL");
static struct mii_timestamper *
fwnode_find_mii_timestamper(struct fwnode_handle *fwnode)
{
struct of_phandle_args arg;
int err;
if (is_acpi_node(fwnode))
return NULL;
err = of_parse_phandle_with_fixed_args(to_of_node(fwnode),
"timestamper", 1, 0, &arg);
if (err == -ENOENT)
return NULL;
else if (err)
return ERR_PTR(err);
if (arg.args_count != 1)
return ERR_PTR(-EINVAL);
return register_mii_timestamper(arg.np, arg.args[0]);
}
int fwnode_mdiobus_phy_device_register(struct mii_bus *mdio,
struct phy_device *phy,
struct fwnode_handle *child, u32 addr)
{
int rc;
rc = fwnode_irq_get(child, 0);
if (rc == -EPROBE_DEFER)
return rc;
if (rc > 0) {
phy->irq = rc;
mdio->irq[addr] = rc;
} else {
phy->irq = mdio->irq[addr];
}
if (fwnode_property_read_bool(child, "broken-turn-around"))
mdio->phy_ignore_ta_mask |= 1 << addr;
fwnode_property_read_u32(child, "reset-assert-us",
&phy->mdio.reset_assert_delay);
fwnode_property_read_u32(child, "reset-deassert-us",
&phy->mdio.reset_deassert_delay);
/* Associate the fwnode with the device structure so it
* can be looked up later
*/
fwnode_handle_get(child);
phy->mdio.dev.fwnode = child;
/* All data is now stored in the phy struct;
* register it
*/
rc = phy_device_register(phy);
if (rc) {
fwnode_handle_put(child);
return rc;
}
dev_dbg(&mdio->dev, "registered phy %p fwnode at address %i\n",
child, addr);
return 0;
}
EXPORT_SYMBOL(fwnode_mdiobus_phy_device_register);
int fwnode_mdiobus_register_phy(struct mii_bus *bus,
struct fwnode_handle *child, u32 addr)
{
struct mii_timestamper *mii_ts = NULL;
struct phy_device *phy;
bool is_c45 = false;
u32 phy_id;
int rc;
mii_ts = fwnode_find_mii_timestamper(child);
if (IS_ERR(mii_ts))
return PTR_ERR(mii_ts);
rc = fwnode_property_match_string(child, "compatible",
"ethernet-phy-ieee802.3-c45");
if (rc >= 0)
is_c45 = true;
if (is_c45 || fwnode_get_phy_id(child, &phy_id))
phy = get_phy_device(bus, addr, is_c45);
else
phy = phy_device_create(bus, addr, phy_id, 0, NULL);
if (IS_ERR(phy)) {
unregister_mii_timestamper(mii_ts);
return PTR_ERR(phy);
}
if (is_acpi_node(child)) {
phy->irq = bus->irq[addr];
/* Associate the fwnode with the device structure so it
* can be looked up later.
*/
phy->mdio.dev.fwnode = child;
/* All data is now stored in the phy struct, so register it */
rc = phy_device_register(phy);
if (rc) {
phy_device_free(phy);
fwnode_handle_put(phy->mdio.dev.fwnode);
return rc;
}
} else if (is_of_node(child)) {
rc = fwnode_mdiobus_phy_device_register(bus, phy, child, addr);
if (rc) {
unregister_mii_timestamper(mii_ts);
phy_device_free(phy);
return rc;
}
}
/* phy->mii_ts may already be defined by the PHY driver. A
* mii_timestamper probed via the device tree will still have
* precedence.
*/
if (mii_ts)
phy->mii_ts = mii_ts;
return 0;
}
EXPORT_SYMBOL(fwnode_mdiobus_register_phy);
......@@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/fwnode_mdio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
......@@ -29,123 +30,22 @@ MODULE_LICENSE("GPL");
* ethernet-phy-idAAAA.BBBB */
static int of_get_phy_id(struct device_node *device, u32 *phy_id)
{
struct property *prop;
const char *cp;
unsigned int upper, lower;
of_property_for_each_string(device, "compatible", prop, cp) {
if (sscanf(cp, "ethernet-phy-id%4x.%4x", &upper, &lower) == 2) {
*phy_id = ((upper & 0xFFFF) << 16) | (lower & 0xFFFF);
return 0;
}
}
return -EINVAL;
}
static struct mii_timestamper *of_find_mii_timestamper(struct device_node *node)
{
struct of_phandle_args arg;
int err;
err = of_parse_phandle_with_fixed_args(node, "timestamper", 1, 0, &arg);
if (err == -ENOENT)
return NULL;
else if (err)
return ERR_PTR(err);
if (arg.args_count != 1)
return ERR_PTR(-EINVAL);
return register_mii_timestamper(arg.np, arg.args[0]);
return fwnode_get_phy_id(of_fwnode_handle(device), phy_id);
}
int of_mdiobus_phy_device_register(struct mii_bus *mdio, struct phy_device *phy,
struct device_node *child, u32 addr)
struct device_node *child, u32 addr)
{
int rc;
rc = of_irq_get(child, 0);
if (rc == -EPROBE_DEFER)
return rc;
if (rc > 0) {
phy->irq = rc;
mdio->irq[addr] = rc;
} else {
phy->irq = mdio->irq[addr];
}
if (of_property_read_bool(child, "broken-turn-around"))
mdio->phy_ignore_ta_mask |= 1 << addr;
of_property_read_u32(child, "reset-assert-us",
&phy->mdio.reset_assert_delay);
of_property_read_u32(child, "reset-deassert-us",
&phy->mdio.reset_deassert_delay);
/* Associate the OF node with the device structure so it
* can be looked up later */
of_node_get(child);
phy->mdio.dev.of_node = child;
phy->mdio.dev.fwnode = of_fwnode_handle(child);
/* All data is now stored in the phy struct;
* register it */
rc = phy_device_register(phy);
if (rc) {
of_node_put(child);
return rc;
}
dev_dbg(&mdio->dev, "registered phy %pOFn at address %i\n",
child, addr);
return 0;
return fwnode_mdiobus_phy_device_register(mdio, phy,
of_fwnode_handle(child),
addr);
}
EXPORT_SYMBOL(of_mdiobus_phy_device_register);
static int of_mdiobus_register_phy(struct mii_bus *mdio,
struct device_node *child, u32 addr)
{
struct mii_timestamper *mii_ts;
struct phy_device *phy;
bool is_c45;
int rc;
u32 phy_id;
mii_ts = of_find_mii_timestamper(child);
if (IS_ERR(mii_ts))
return PTR_ERR(mii_ts);
is_c45 = of_device_is_compatible(child,
"ethernet-phy-ieee802.3-c45");
if (!is_c45 && !of_get_phy_id(child, &phy_id))
phy = phy_device_create(mdio, addr, phy_id, 0, NULL);
else
phy = get_phy_device(mdio, addr, is_c45);
if (IS_ERR(phy)) {
if (mii_ts)
unregister_mii_timestamper(mii_ts);
return PTR_ERR(phy);
}
rc = of_mdiobus_phy_device_register(mdio, phy, child, addr);
if (rc) {
if (mii_ts)
unregister_mii_timestamper(mii_ts);
phy_device_free(phy);
return rc;
}
/* phy->mii_ts may already be defined by the PHY driver. A
* mii_timestamper probed via the device tree will still have
* precedence.
*/
if (mii_ts)
phy->mii_ts = mii_ts;
return 0;
return fwnode_mdiobus_register_phy(mdio, of_fwnode_handle(child), addr);
}
static int of_mdiobus_register_device(struct mii_bus *mdio,
......@@ -347,16 +247,7 @@ EXPORT_SYMBOL(of_mdiobus_register);
*/
struct mdio_device *of_mdio_find_device(struct device_node *np)
{
struct device *d;
if (!np)
return NULL;
d = bus_find_device_by_of_node(&mdio_bus_type, np);
if (!d)
return NULL;
return to_mdio_device(d);
return fwnode_mdio_find_device(of_fwnode_handle(np));
}
EXPORT_SYMBOL(of_mdio_find_device);
......@@ -369,18 +260,7 @@ EXPORT_SYMBOL(of_mdio_find_device);
*/
struct phy_device *of_phy_find_device(struct device_node *phy_np)
{
struct mdio_device *mdiodev;
mdiodev = of_mdio_find_device(phy_np);
if (!mdiodev)
return NULL;
if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY)
return to_phy_device(&mdiodev->dev);
put_device(&mdiodev->dev);
return NULL;
return fwnode_phy_find_device(of_fwnode_handle(phy_np));
}
EXPORT_SYMBOL(of_phy_find_device);
......
......@@ -111,6 +111,9 @@ void unregister_mii_timestamper(struct mii_timestamper *mii_ts)
struct mii_timestamping_desc *desc;
struct list_head *this;
if (!mii_ts)
return;
/* mii_timestamper statically registered by the PHY driver won't use the
* register_mii_timestamper() and thus don't have ->device set. Don't
* try to unregister these.
......
......@@ -9,6 +9,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/errno.h>
......@@ -833,6 +834,27 @@ static int get_phy_c22_id(struct mii_bus *bus, int addr, u32 *phy_id)
return 0;
}
/* Extract the phy ID from the compatible string of the form
* ethernet-phy-idAAAA.BBBB.
*/
int fwnode_get_phy_id(struct fwnode_handle *fwnode, u32 *phy_id)
{
unsigned int upper, lower;
const char *cp;
int ret;
ret = fwnode_property_read_string(fwnode, "compatible", &cp);
if (ret)
return ret;
if (sscanf(cp, "ethernet-phy-id%4x.%4x", &upper, &lower) != 2)
return -EINVAL;
*phy_id = ((upper & GENMASK(15, 0)) << 16) | (lower & GENMASK(15, 0));
return 0;
}
EXPORT_SYMBOL(fwnode_get_phy_id);
/**
* get_phy_device - reads the specified PHY device and returns its @phy_device
* struct
......@@ -935,8 +957,7 @@ EXPORT_SYMBOL(phy_device_register);
*/
void phy_device_remove(struct phy_device *phydev)
{
if (phydev->mii_ts)
unregister_mii_timestamper(phydev->mii_ts);
unregister_mii_timestamper(phydev->mii_ts);
device_del(&phydev->mdio.dev);
......@@ -2875,6 +2896,90 @@ static bool phy_drv_supports_irq(struct phy_driver *phydrv)
return phydrv->config_intr && phydrv->handle_interrupt;
}
/**
* fwnode_mdio_find_device - Given a fwnode, find the mdio_device
* @fwnode: pointer to the mdio_device's fwnode
*
* If successful, returns a pointer to the mdio_device with the embedded
* struct device refcount incremented by one, or NULL on failure.
* The caller should call put_device() on the mdio_device after its use.
*/
struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode)
{
struct device *d;
if (!fwnode)
return NULL;
d = bus_find_device_by_fwnode(&mdio_bus_type, fwnode);
if (!d)
return NULL;
return to_mdio_device(d);
}
EXPORT_SYMBOL(fwnode_mdio_find_device);
/**
* fwnode_phy_find_device - For provided phy_fwnode, find phy_device.
*
* @phy_fwnode: Pointer to the phy's fwnode.
*
* If successful, returns a pointer to the phy_device with the embedded
* struct device refcount incremented by one, or NULL on failure.
*/
struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode)
{
struct mdio_device *mdiodev;
mdiodev = fwnode_mdio_find_device(phy_fwnode);
if (!mdiodev)
return NULL;
if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY)
return to_phy_device(&mdiodev->dev);
put_device(&mdiodev->dev);
return NULL;
}
EXPORT_SYMBOL(fwnode_phy_find_device);
/**
* device_phy_find_device - For the given device, get the phy_device
* @dev: Pointer to the given device
*
* Refer return conditions of fwnode_phy_find_device().
*/
struct phy_device *device_phy_find_device(struct device *dev)
{
return fwnode_phy_find_device(dev_fwnode(dev));
}
EXPORT_SYMBOL_GPL(device_phy_find_device);
/**
* fwnode_get_phy_node - Get the phy_node using the named reference.
* @fwnode: Pointer to fwnode from which phy_node has to be obtained.
*
* Refer return conditions of fwnode_find_reference().
* For ACPI, only "phy-handle" is supported. Legacy DT properties "phy"
* and "phy-device" are not supported in ACPI. DT supports all the three
* named references to the phy node.
*/
struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode)
{
struct fwnode_handle *phy_node;
/* Only phy-handle is used for ACPI */
phy_node = fwnode_find_reference(fwnode, "phy-handle", 0);
if (is_acpi_node(fwnode) || !IS_ERR(phy_node))
return phy_node;
phy_node = fwnode_find_reference(fwnode, "phy", 0);
if (IS_ERR(phy_node))
phy_node = fwnode_find_reference(fwnode, "phy-device", 0);
return phy_node;
}
EXPORT_SYMBOL_GPL(fwnode_get_phy_node);
/**
* phy_probe - probe and init a PHY device
* @dev: device to probe and init
......
......@@ -5,6 +5,7 @@
*
* Copyright (C) 2015 Russell King
*/
#include <linux/acpi.h>
#include <linux/ethtool.h>
#include <linux/export.h>
#include <linux/gpio/consumer.h>
......@@ -1084,7 +1085,26 @@ EXPORT_SYMBOL_GPL(phylink_connect_phy);
int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn,
u32 flags)
{
struct device_node *phy_node;
return phylink_fwnode_phy_connect(pl, of_fwnode_handle(dn), flags);
}
EXPORT_SYMBOL_GPL(phylink_of_phy_connect);
/**
* phylink_fwnode_phy_connect() - connect the PHY specified in the fwnode.
* @pl: a pointer to a &struct phylink returned from phylink_create()
* @fwnode: a pointer to a &struct fwnode_handle.
* @flags: PHY-specific flags to communicate to the PHY device driver
*
* Connect the phy specified @fwnode to the phylink instance specified
* by @pl.
*
* Returns 0 on success or a negative errno.
*/
int phylink_fwnode_phy_connect(struct phylink *pl,
struct fwnode_handle *fwnode,
u32 flags)
{
struct fwnode_handle *phy_fwnode;
struct phy_device *phy_dev;
int ret;
......@@ -1094,28 +1114,25 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn,
phy_interface_mode_is_8023z(pl->link_interface)))
return 0;
phy_node = of_parse_phandle(dn, "phy-handle", 0);
if (!phy_node)
phy_node = of_parse_phandle(dn, "phy", 0);
if (!phy_node)
phy_node = of_parse_phandle(dn, "phy-device", 0);
if (!phy_node) {
phy_fwnode = fwnode_get_phy_node(fwnode);
if (IS_ERR(phy_fwnode)) {
if (pl->cfg_link_an_mode == MLO_AN_PHY)
return -ENODEV;
return 0;
}
phy_dev = of_phy_find_device(phy_node);
phy_dev = fwnode_phy_find_device(phy_fwnode);
/* We're done with the phy_node handle */
of_node_put(phy_node);
fwnode_handle_put(phy_fwnode);
if (!phy_dev)
return -ENODEV;
ret = phy_attach_direct(pl->netdev, phy_dev, flags,
pl->link_interface);
if (ret)
if (ret) {
phy_device_free(phy_dev);
return ret;
}
ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface);
if (ret)
......@@ -1123,7 +1140,7 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn,
return ret;
}
EXPORT_SYMBOL_GPL(phylink_of_phy_connect);
EXPORT_SYMBOL_GPL(phylink_fwnode_phy_connect);
/**
* phylink_disconnect_phy() - disconnect any PHY attached to the phylink
......
......@@ -710,6 +710,8 @@ static inline u64 acpi_arch_get_root_pointer(void)
}
#endif
int acpi_get_local_address(acpi_handle handle, u32 *addr);
#else /* !CONFIG_ACPI */
#define acpi_disabled 1
......@@ -965,6 +967,11 @@ static inline struct acpi_device *acpi_resource_consumer(struct resource *res)
return NULL;
}
static inline int acpi_get_local_address(acpi_handle handle, u32 *addr)
{
return -ENODEV;
}
#endif /* !CONFIG_ACPI */
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
......
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ACPI helper for the MDIO (Ethernet PHY) API
*/
#ifndef __LINUX_ACPI_MDIO_H
#define __LINUX_ACPI_MDIO_H
#include <linux/phy.h>
#if IS_ENABLED(CONFIG_ACPI_MDIO)
int acpi_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode);
#else /* CONFIG_ACPI_MDIO */
static inline int
acpi_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode)
{
/*
* Fall back to mdiobus_register() function to register a bus.
* This way, we don't have to keep compat bits around in drivers.
*/
return mdiobus_register(mdio);
}
#endif
#endif /* __LINUX_ACPI_MDIO_H */
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* FWNODE helper for the MDIO (Ethernet PHY) API
*/
#ifndef __LINUX_FWNODE_MDIO_H
#define __LINUX_FWNODE_MDIO_H
#include <linux/phy.h>
#if IS_ENABLED(CONFIG_FWNODE_MDIO)
int fwnode_mdiobus_phy_device_register(struct mii_bus *mdio,
struct phy_device *phy,
struct fwnode_handle *child, u32 addr);
int fwnode_mdiobus_register_phy(struct mii_bus *bus,
struct fwnode_handle *child, u32 addr);
#else /* CONFIG_FWNODE_MDIO */
int fwnode_mdiobus_phy_device_register(struct mii_bus *mdio,
struct phy_device *phy,
struct fwnode_handle *child, u32 addr)
{
return -EINVAL;
}
static inline int fwnode_mdiobus_register_phy(struct mii_bus *bus,
struct fwnode_handle *child,
u32 addr)
{
return -EINVAL;
}
#endif
#endif /* __LINUX_FWNODE_MDIO_H */
......@@ -1377,10 +1377,42 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
bool is_c45,
struct phy_c45_device_ids *c45_ids);
#if IS_ENABLED(CONFIG_PHYLIB)
int fwnode_get_phy_id(struct fwnode_handle *fwnode, u32 *phy_id);
struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode);
struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode);
struct phy_device *device_phy_find_device(struct device *dev);
struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode);
struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45);
int phy_device_register(struct phy_device *phy);
void phy_device_free(struct phy_device *phydev);
#else
static inline int fwnode_get_phy_id(struct fwnode_handle *fwnode, u32 *phy_id)
{
return 0;
}
static inline
struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode)
{
return 0;
}
static inline
struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode)
{
return NULL;
}
static inline struct phy_device *device_phy_find_device(struct device *dev)
{
return NULL;
}
static inline
struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode)
{
return NULL;
}
static inline
struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
{
......
......@@ -441,6 +441,9 @@ void phylink_destroy(struct phylink *);
int phylink_connect_phy(struct phylink *, struct phy_device *);
int phylink_of_phy_connect(struct phylink *, struct device_node *, u32 flags);
int phylink_fwnode_phy_connect(struct phylink *pl,
struct fwnode_handle *fwnode,
u32 flags);
void phylink_disconnect_phy(struct phylink *);
void phylink_mac_change(struct phylink *, bool up);
......
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