Contributors: 21
Author Tokens Token Proportion Commits Commit Proportion
Andy Fleming 405 23.21% 2 4.17%
Andrew Lunn 360 20.63% 9 18.75%
Russell King 297 17.02% 8 16.67%
Lennert Buytenhek 248 14.21% 4 8.33%
Florian Fainelli 133 7.62% 2 4.17%
Heiner Kallweit 81 4.64% 8 16.67%
Uwe Kleine-König 57 3.27% 1 2.08%
Neil Armstrong 42 2.41% 1 2.08%
Jakub Raczynski 40 2.29% 1 2.08%
Buday Csaba 19 1.09% 1 2.08%
Jason Gunthorpe 15 0.86% 1 2.08%
Bartosz Golaszewski 14 0.80% 1 2.08%
Joe Perches 7 0.40% 1 2.08%
Roger Quadros 6 0.34% 1 2.08%
Daniel Mack 5 0.29% 1 2.08%
Nikita Yushchenko 4 0.23% 1 2.08%
Nate Case 4 0.23% 1 2.08%
Jiawen Wu 3 0.17% 1 2.08%
David Daney 2 0.11% 1 2.08%
Mark Brown 2 0.11% 1 2.08%
Vitaly Bordug 1 0.06% 1 2.08%
Total 1745 48


// 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");