mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
This moves definition of mdio_bus class and bus_type to the provider
side, what allows to make them private to libphy.
As a prerequisite MDIO statistics handling is moved to the
provider side as well.
Note: This patch causes a checkpatch error "Macros with complex values
should be enclosed in parentheses" for
MDIO_BUS_STATS_ADDR_ATTR_GROUP. I consider this a false positive
here, in addition the patch just moves existing code.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Link: https://patch.msgid.link/47b85676-b349-4aa0-a5ef-cd37769a4c69@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
628 lines
16 KiB
C
628 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/* MDIO Bus interface
|
|
*
|
|
* Author: Andy Fleming
|
|
*
|
|
* Copyright (c) 2004 Freescale Semiconductor, Inc.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/ethtool.h>
|
|
#include <linux/init.h>
|
|
#include <linux/io.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mii.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/module.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/string.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/unistd.h>
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/mdio.h>
|
|
|
|
static struct mdio_device *mdiobus_find_device(struct mii_bus *bus, int addr)
|
|
{
|
|
bool addr_valid = addr >= 0 && addr < ARRAY_SIZE(bus->mdio_map);
|
|
|
|
if (WARN_ONCE(!addr_valid, "addr %d out of range\n", addr))
|
|
return NULL;
|
|
|
|
return bus->mdio_map[addr];
|
|
}
|
|
|
|
struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr)
|
|
{
|
|
struct mdio_device *mdiodev;
|
|
|
|
mdiodev = mdiobus_find_device(bus, addr);
|
|
if (!mdiodev)
|
|
return NULL;
|
|
|
|
if (!(mdiodev->flags & MDIO_DEVICE_FLAG_PHY))
|
|
return NULL;
|
|
|
|
return container_of(mdiodev, struct phy_device, mdio);
|
|
}
|
|
EXPORT_SYMBOL(mdiobus_get_phy);
|
|
|
|
bool mdiobus_is_registered_device(struct mii_bus *bus, int addr)
|
|
{
|
|
return mdiobus_find_device(bus, addr) != NULL;
|
|
}
|
|
EXPORT_SYMBOL(mdiobus_is_registered_device);
|
|
|
|
static void mdiobus_stats_acct(struct mdio_bus_stats *stats, bool op, int ret)
|
|
{
|
|
preempt_disable();
|
|
u64_stats_update_begin(&stats->syncp);
|
|
|
|
u64_stats_inc(&stats->transfers);
|
|
if (ret < 0) {
|
|
u64_stats_inc(&stats->errors);
|
|
goto out;
|
|
}
|
|
|
|
if (op)
|
|
u64_stats_inc(&stats->reads);
|
|
else
|
|
u64_stats_inc(&stats->writes);
|
|
out:
|
|
u64_stats_update_end(&stats->syncp);
|
|
preempt_enable();
|
|
}
|
|
|
|
/**
|
|
* __mdiobus_read - Unlocked version of the mdiobus_read function
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @regnum: register number to read
|
|
*
|
|
* Return: The register value if successful, negative error code on failure
|
|
*
|
|
* Read a MDIO bus register. Caller must hold the mdio bus lock.
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context.
|
|
*/
|
|
int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
|
|
{
|
|
int retval;
|
|
|
|
lockdep_assert_held_once(&bus->mdio_lock);
|
|
|
|
if (addr >= PHY_MAX_ADDR)
|
|
return -ENXIO;
|
|
|
|
if (bus->read)
|
|
retval = bus->read(bus, addr, regnum);
|
|
else
|
|
retval = -EOPNOTSUPP;
|
|
|
|
trace_mdio_access(bus, 1, addr, regnum, retval, retval);
|
|
mdiobus_stats_acct(&bus->stats[addr], true, retval);
|
|
|
|
return retval;
|
|
}
|
|
EXPORT_SYMBOL(__mdiobus_read);
|
|
|
|
/**
|
|
* __mdiobus_write - Unlocked version of the mdiobus_write function
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @regnum: register number to write
|
|
* @val: value to write to @regnum
|
|
*
|
|
* Return: Zero if successful, negative error code on failure
|
|
*
|
|
* Write a MDIO bus register. Caller must hold the mdio bus lock.
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context.
|
|
*/
|
|
int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
|
|
{
|
|
int err;
|
|
|
|
lockdep_assert_held_once(&bus->mdio_lock);
|
|
|
|
if (addr >= PHY_MAX_ADDR)
|
|
return -ENXIO;
|
|
|
|
if (bus->write)
|
|
err = bus->write(bus, addr, regnum, val);
|
|
else
|
|
err = -EOPNOTSUPP;
|
|
|
|
trace_mdio_access(bus, 0, addr, regnum, val, err);
|
|
mdiobus_stats_acct(&bus->stats[addr], false, err);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(__mdiobus_write);
|
|
|
|
/**
|
|
* __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @regnum: register number to modify
|
|
* @mask: bit mask of bits to clear
|
|
* @set: bit mask of bits to set
|
|
*
|
|
* Return: 1 if the register was modified, 0 if no change was needed,
|
|
* negative on any error condition
|
|
*
|
|
* Read, modify, and if any change, write the register value back to the
|
|
* device.
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context.
|
|
*/
|
|
int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
|
|
u16 mask, u16 set)
|
|
{
|
|
int new, ret;
|
|
|
|
ret = __mdiobus_read(bus, addr, regnum);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
new = (ret & ~mask) | set;
|
|
if (new == ret)
|
|
return 0;
|
|
|
|
ret = __mdiobus_write(bus, addr, regnum, new);
|
|
|
|
return ret < 0 ? ret : 1;
|
|
}
|
|
EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
|
|
|
|
/**
|
|
* __mdiobus_c45_read - Unlocked version of the mdiobus_c45_read function
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @devad: device address to read
|
|
* @regnum: register number to read
|
|
*
|
|
* Return: The register value if successful, negative error code on failure
|
|
*
|
|
* Read a MDIO bus register. Caller must hold the mdio bus lock.
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context.
|
|
*/
|
|
int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
|
|
{
|
|
int retval;
|
|
|
|
lockdep_assert_held_once(&bus->mdio_lock);
|
|
|
|
if (addr >= PHY_MAX_ADDR)
|
|
return -ENXIO;
|
|
|
|
if (bus->read_c45)
|
|
retval = bus->read_c45(bus, addr, devad, regnum);
|
|
else
|
|
retval = -EOPNOTSUPP;
|
|
|
|
trace_mdio_access(bus, 1, addr, regnum, retval, retval);
|
|
mdiobus_stats_acct(&bus->stats[addr], true, retval);
|
|
|
|
return retval;
|
|
}
|
|
EXPORT_SYMBOL(__mdiobus_c45_read);
|
|
|
|
/**
|
|
* __mdiobus_c45_write - Unlocked version of the mdiobus_write function
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @devad: device address to read
|
|
* @regnum: register number to write
|
|
* @val: value to write to @regnum
|
|
*
|
|
* Return: Zero if successful, negative error code on failure
|
|
*
|
|
* Write a MDIO bus register. Caller must hold the mdio bus lock.
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context.
|
|
*/
|
|
int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
|
u16 val)
|
|
{
|
|
int err;
|
|
|
|
lockdep_assert_held_once(&bus->mdio_lock);
|
|
|
|
if (addr >= PHY_MAX_ADDR)
|
|
return -ENXIO;
|
|
|
|
if (bus->write_c45)
|
|
err = bus->write_c45(bus, addr, devad, regnum, val);
|
|
else
|
|
err = -EOPNOTSUPP;
|
|
|
|
trace_mdio_access(bus, 0, addr, regnum, val, err);
|
|
mdiobus_stats_acct(&bus->stats[addr], false, err);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(__mdiobus_c45_write);
|
|
|
|
/**
|
|
* __mdiobus_c45_modify_changed - Unlocked version of the mdiobus_modify function
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @devad: device address to read
|
|
* @regnum: register number to modify
|
|
* @mask: bit mask of bits to clear
|
|
* @set: bit mask of bits to set
|
|
*
|
|
* Return: 1 if the register was modified, 0 if no change was needed,
|
|
* negative on any error condition
|
|
*
|
|
* Read, modify, and if any change, write the register value back to the
|
|
* device. Any error returns a negative number.
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context.
|
|
*/
|
|
static int __mdiobus_c45_modify_changed(struct mii_bus *bus, int addr,
|
|
int devad, u32 regnum, u16 mask,
|
|
u16 set)
|
|
{
|
|
int new, ret;
|
|
|
|
ret = __mdiobus_c45_read(bus, addr, devad, regnum);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
new = (ret & ~mask) | set;
|
|
if (new == ret)
|
|
return 0;
|
|
|
|
ret = __mdiobus_c45_write(bus, addr, devad, regnum, new);
|
|
|
|
return ret < 0 ? ret : 1;
|
|
}
|
|
|
|
/**
|
|
* mdiobus_read_nested - Nested version of the mdiobus_read function
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @regnum: register number to read
|
|
*
|
|
* Return: The register value if successful, negative error code on failure
|
|
*
|
|
* In case of nested MDIO bus access avoid lockdep false positives by
|
|
* using mutex_lock_nested().
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context,
|
|
* because the bus read/write functions may wait for an interrupt
|
|
* to conclude the operation.
|
|
*/
|
|
int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum)
|
|
{
|
|
int retval;
|
|
|
|
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
|
|
retval = __mdiobus_read(bus, addr, regnum);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return retval;
|
|
}
|
|
EXPORT_SYMBOL(mdiobus_read_nested);
|
|
|
|
/**
|
|
* mdiobus_read - Convenience function for reading a given MII mgmt register
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @regnum: register number to read
|
|
*
|
|
* Return: The register value if successful, negative error code on failure
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context,
|
|
* because the bus read/write functions may wait for an interrupt
|
|
* to conclude the operation.
|
|
*/
|
|
int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
|
|
{
|
|
int retval;
|
|
|
|
mutex_lock(&bus->mdio_lock);
|
|
retval = __mdiobus_read(bus, addr, regnum);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return retval;
|
|
}
|
|
EXPORT_SYMBOL(mdiobus_read);
|
|
|
|
/**
|
|
* mdiobus_c45_read - Convenience function for reading a given MII mgmt register
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @devad: device address to read
|
|
* @regnum: register number to read
|
|
*
|
|
* Return: The register value if successful, negative error code on failure
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context,
|
|
* because the bus read/write functions may wait for an interrupt
|
|
* to conclude the operation.
|
|
*/
|
|
int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
|
|
{
|
|
int retval;
|
|
|
|
mutex_lock(&bus->mdio_lock);
|
|
retval = __mdiobus_c45_read(bus, addr, devad, regnum);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return retval;
|
|
}
|
|
EXPORT_SYMBOL(mdiobus_c45_read);
|
|
|
|
/**
|
|
* mdiobus_c45_read_nested - Nested version of the mdiobus_c45_read function
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @devad: device address to read
|
|
* @regnum: register number to read
|
|
*
|
|
* Return: The register value if successful, negative error code on failure
|
|
*
|
|
* In case of nested MDIO bus access avoid lockdep false positives by
|
|
* using mutex_lock_nested().
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context,
|
|
* because the bus read/write functions may wait for an interrupt
|
|
* to conclude the operation.
|
|
*/
|
|
int mdiobus_c45_read_nested(struct mii_bus *bus, int addr, int devad,
|
|
u32 regnum)
|
|
{
|
|
int retval;
|
|
|
|
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
|
|
retval = __mdiobus_c45_read(bus, addr, devad, regnum);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return retval;
|
|
}
|
|
EXPORT_SYMBOL(mdiobus_c45_read_nested);
|
|
|
|
/**
|
|
* mdiobus_write_nested - Nested version of the mdiobus_write function
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @regnum: register number to write
|
|
* @val: value to write to @regnum
|
|
*
|
|
* Return: Zero if successful, negative error code on failure
|
|
*
|
|
* In case of nested MDIO bus access avoid lockdep false positives by
|
|
* using mutex_lock_nested().
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context,
|
|
* because the bus read/write functions may wait for an interrupt
|
|
* to conclude the operation.
|
|
*/
|
|
int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val)
|
|
{
|
|
int err;
|
|
|
|
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
|
|
err = __mdiobus_write(bus, addr, regnum, val);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(mdiobus_write_nested);
|
|
|
|
/**
|
|
* mdiobus_write - Convenience function for writing a given MII mgmt register
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @regnum: register number to write
|
|
* @val: value to write to @regnum
|
|
*
|
|
* Return: Zero if successful, negative error code on failure
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context,
|
|
* because the bus read/write functions may wait for an interrupt
|
|
* to conclude the operation.
|
|
*/
|
|
int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
|
|
{
|
|
int err;
|
|
|
|
mutex_lock(&bus->mdio_lock);
|
|
err = __mdiobus_write(bus, addr, regnum, val);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(mdiobus_write);
|
|
|
|
/**
|
|
* mdiobus_c45_write - Convenience function for writing a given MII mgmt register
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @devad: device address to read
|
|
* @regnum: register number to write
|
|
* @val: value to write to @regnum
|
|
*
|
|
* Return: Zero if successful, negative error code on failure
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context,
|
|
* because the bus read/write functions may wait for an interrupt
|
|
* to conclude the operation.
|
|
*/
|
|
int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
|
u16 val)
|
|
{
|
|
int err;
|
|
|
|
mutex_lock(&bus->mdio_lock);
|
|
err = __mdiobus_c45_write(bus, addr, devad, regnum, val);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(mdiobus_c45_write);
|
|
|
|
/**
|
|
* mdiobus_c45_write_nested - Nested version of the mdiobus_c45_write function
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @devad: device address to read
|
|
* @regnum: register number to write
|
|
* @val: value to write to @regnum
|
|
*
|
|
* Return: Zero if successful, negative error code on failure
|
|
*
|
|
* In case of nested MDIO bus access avoid lockdep false positives by
|
|
* using mutex_lock_nested().
|
|
*
|
|
* NOTE: MUST NOT be called from interrupt context,
|
|
* because the bus read/write functions may wait for an interrupt
|
|
* to conclude the operation.
|
|
*/
|
|
int mdiobus_c45_write_nested(struct mii_bus *bus, int addr, int devad,
|
|
u32 regnum, u16 val)
|
|
{
|
|
int err;
|
|
|
|
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
|
|
err = __mdiobus_c45_write(bus, addr, devad, regnum, val);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(mdiobus_c45_write_nested);
|
|
|
|
/*
|
|
* __mdiobus_modify - Convenience function for modifying a given mdio device
|
|
* register
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @regnum: register number to write
|
|
* @mask: bit mask of bits to clear
|
|
* @set: bit mask of bits to set
|
|
*
|
|
* Return: 0 on success, negative on any error condition
|
|
*/
|
|
int __mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask,
|
|
u16 set)
|
|
{
|
|
int err;
|
|
|
|
err = __mdiobus_modify_changed(bus, addr, regnum, mask, set);
|
|
|
|
return err < 0 ? err : 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(__mdiobus_modify);
|
|
|
|
/**
|
|
* mdiobus_modify - Convenience function for modifying a given mdio device
|
|
* register
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @regnum: register number to write
|
|
* @mask: bit mask of bits to clear
|
|
* @set: bit mask of bits to set
|
|
*
|
|
* Return: 0 on success, negative on any error condition
|
|
*/
|
|
int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set)
|
|
{
|
|
int err;
|
|
|
|
mutex_lock(&bus->mdio_lock);
|
|
err = __mdiobus_modify(bus, addr, regnum, mask, set);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mdiobus_modify);
|
|
|
|
/**
|
|
* mdiobus_c45_modify - Convenience function for modifying a given mdio device
|
|
* register
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @devad: device address to read
|
|
* @regnum: register number to write
|
|
* @mask: bit mask of bits to clear
|
|
* @set: bit mask of bits to set
|
|
*
|
|
* Return: 0 on success, negative on any error condition
|
|
*/
|
|
int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
|
u16 mask, u16 set)
|
|
{
|
|
int err;
|
|
|
|
mutex_lock(&bus->mdio_lock);
|
|
err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum,
|
|
mask, set);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return err < 0 ? err : 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mdiobus_c45_modify);
|
|
|
|
/**
|
|
* mdiobus_modify_changed - Convenience function for modifying a given mdio
|
|
* device register and returning if it changed
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @regnum: register number to write
|
|
* @mask: bit mask of bits to clear
|
|
* @set: bit mask of bits to set
|
|
*
|
|
* Return: 1 if the register was modified, 0 if no change was needed,
|
|
* negative on any error condition
|
|
*/
|
|
int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
|
|
u16 mask, u16 set)
|
|
{
|
|
int err;
|
|
|
|
mutex_lock(&bus->mdio_lock);
|
|
err = __mdiobus_modify_changed(bus, addr, regnum, mask, set);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mdiobus_modify_changed);
|
|
|
|
/**
|
|
* mdiobus_c45_modify_changed - Convenience function for modifying a given mdio
|
|
* device register and returning if it changed
|
|
* @bus: the mii_bus struct
|
|
* @addr: the phy address
|
|
* @devad: device address to read
|
|
* @regnum: register number to write
|
|
* @mask: bit mask of bits to clear
|
|
* @set: bit mask of bits to set
|
|
*
|
|
* Return: 1 if the register was modified, 0 if no change was needed,
|
|
* negative on any error condition
|
|
*/
|
|
int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad,
|
|
u32 regnum, u16 mask, u16 set)
|
|
{
|
|
int err;
|
|
|
|
mutex_lock(&bus->mdio_lock);
|
|
err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, mask, set);
|
|
mutex_unlock(&bus->mdio_lock);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mdiobus_c45_modify_changed);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("MDIO bus/device layer");
|