// SPDX-License-Identifier: GPL-2.0+ /* MDIO Bus interface * * Author: Andy Fleming * * Copyright (c) 2004 Freescale Semiconductor, Inc. */ #define pr_fmt(fmt) … #include <linux/delay.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> #include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/micrel_phy.h> #include <linux/mii.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/of_device.h> #include <linux/of_mdio.h> #include <linux/phy.h> #include <linux/reset.h> #include <linux/skbuff.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> #include "mdio-boardinfo.h" static int mdiobus_register_gpiod(struct mdio_device *mdiodev) { … } static int mdiobus_register_reset(struct mdio_device *mdiodev) { … } int mdiobus_register_device(struct mdio_device *mdiodev) { … } EXPORT_SYMBOL(…); int mdiobus_unregister_device(struct mdio_device *mdiodev) { … } EXPORT_SYMBOL(…); static struct mdio_device *mdiobus_find_device(struct mii_bus *bus, int addr) { … } struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr) { … } EXPORT_SYMBOL(…); bool mdiobus_is_registered_device(struct mii_bus *bus, int addr) { … } EXPORT_SYMBOL(…); /** * mdiobus_alloc_size - allocate a mii_bus structure * @size: extra amount of memory to allocate for private storage. * If non-zero, then bus->priv is points to that memory. * * Description: called by a bus driver to allocate an mii_bus * structure to fill in. */ struct mii_bus *mdiobus_alloc_size(size_t size) { … } EXPORT_SYMBOL(…); /** * mdiobus_release - mii_bus device release callback * @d: the target struct device that contains the mii_bus * * Description: called when the last reference to an mii_bus is * dropped, to free the underlying memory. */ static void mdiobus_release(struct device *d) { … } struct mdio_bus_stat_attr { … }; static u64 mdio_bus_get_stat(struct mdio_bus_stats *s, unsigned int offset) { … } static u64 mdio_bus_get_global_stat(struct mii_bus *bus, unsigned int offset) { … } static ssize_t mdio_bus_stat_field_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t mdio_bus_device_stat_field_show(struct device *dev, struct device_attribute *attr, char *buf) { … } #define MDIO_BUS_STATS_ATTR_DECL(field, file) … #define MDIO_BUS_STATS_ATTR(field) … MDIO_BUS_STATS_ATTR(transfers); MDIO_BUS_STATS_ATTR(errors); MDIO_BUS_STATS_ATTR(writes); MDIO_BUS_STATS_ATTR(reads); #define MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr, file) … #define MDIO_BUS_STATS_ADDR_ATTR(field, addr) … #define MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(addr) … \ MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(…); #define MDIO_BUS_STATS_ADDR_ATTR_GROUP(addr) … \ static struct attribute *mdio_bus_statistics_attrs[] = …; static const struct attribute_group mdio_bus_statistics_group = …; static const struct attribute_group *mdio_bus_groups[] = …; static struct class mdio_bus_class = …; /** * mdio_find_bus - Given the name of a mdiobus, find the mii_bus. * @mdio_name: The name of a mdiobus. * * 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) { … } EXPORT_SYMBOL(…); #if IS_ENABLED(CONFIG_OF_MDIO) /** * of_mdio_find_bus - Given an mii_bus node, 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 once the bus is finished with. * * Because the association of a device_node and mii_bus is made via * of_mdiobus_register(), the mii_bus cannot be found before it is * registered with of_mdiobus_register(). * */ struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np) { … } EXPORT_SYMBOL(…); /* Walk the list of subnodes of a mdio bus and look for a node that * matches the mdio device's address with its 'reg' property. If * found, set the of_node pointer for the mdio device. This allows * auto-probed phy devices to be supplied with information passed in * via DT. * If a PHY package is found, PHY is searched also there. */ static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev, struct device_node *np) { … } static void of_mdiobus_link_mdiodev(struct mii_bus *bus, struct mdio_device *mdiodev) { … } #else /* !IS_ENABLED(CONFIG_OF_MDIO) */ static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio, struct mdio_device *mdiodev) { } #endif /** * mdiobus_create_device - create a full MDIO device given * a mdio_board_info structure * @bus: MDIO bus to create the devices on * @bi: mdio_board_info structure describing the devices * * Returns 0 on success or < 0 on error. */ static int mdiobus_create_device(struct mii_bus *bus, struct mdio_board_info *bi) { … } static struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr, bool c45) { … } /** * mdiobus_scan_c22 - scan one address on a bus for C22 MDIO devices. * @bus: mii_bus to scan * @addr: address on bus to scan * * This function scans one address on the MDIO bus, looking for * devices which can be identified using a vendor/product ID in * registers 2 and 3. Not all MDIO devices have such registers, but * PHY devices typically do. Hence this function assumes anything * found is a PHY, or can be treated as a PHY. Other MDIO devices, * such as switches, will probably not be found during the scan. */ struct phy_device *mdiobus_scan_c22(struct mii_bus *bus, int addr) { … } EXPORT_SYMBOL(…); /** * mdiobus_scan_c45 - scan one address on a bus for C45 MDIO devices. * @bus: mii_bus to scan * @addr: address on bus to scan * * This function scans one address on the MDIO bus, looking for * devices which can be identified using a vendor/product ID in * registers 2 and 3. Not all MDIO devices have such registers, but * PHY devices typically do. Hence this function assumes anything * found is a PHY, or can be treated as a PHY. Other MDIO devices, * such as switches, will probably not be found during the scan. */ static struct phy_device *mdiobus_scan_c45(struct mii_bus *bus, int addr) { … } static int mdiobus_scan_bus_c22(struct mii_bus *bus) { … } static int mdiobus_scan_bus_c45(struct mii_bus *bus) { … } /* There are some C22 PHYs which do bad things when where is a C45 * transaction on the bus, like accepting a read themselves, and * stomping over the true devices reply, to performing a write to * themselves which was intended for another device. Now that C22 * devices have been found, see if any of them are bad for C45, and if we * should skip the C45 scan. */ static bool mdiobus_prevent_c45_scan(struct mii_bus *bus) { … } /** * __mdiobus_register - bring up all the PHYs on a given bus and attach them to bus * @bus: target mii_bus * @owner: module containing bus accessor functions * * Description: Called by a bus driver to bring up all the PHYs * on a given bus, and attach them to the bus. Drivers should use * mdiobus_register() rather than __mdiobus_register() unless they * need to pass a specific owner module. MDIO devices which are not * PHYs will not be brought up by this function. They are expected * to be explicitly listed in DT and instantiated by of_mdiobus_register(). * * Returns 0 on success or < 0 on error. */ int __mdiobus_register(struct mii_bus *bus, struct module *owner) { … } EXPORT_SYMBOL(…); void mdiobus_unregister(struct mii_bus *bus) { … } EXPORT_SYMBOL(…); /** * mdiobus_free - free a struct mii_bus * @bus: mii_bus to free * * This function releases the reference to the underlying device * object in the mii_bus. If this is the last reference, the mii_bus * will be freed. */ void mdiobus_free(struct mii_bus *bus) { … } EXPORT_SYMBOL(…); static void mdiobus_stats_acct(struct mdio_bus_stats *stats, bool op, int ret) { … } /** * __mdiobus_read - Unlocked version of the mdiobus_read function * @bus: the mii_bus struct * @addr: the phy address * @regnum: register number to read * * 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) { … } EXPORT_SYMBOL(…); /** * __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 * * 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) { … } EXPORT_SYMBOL(…); /** * __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 * * 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. */ int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set) { … } EXPORT_SYMBOL_GPL(…); /** * __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 * * 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) { … } EXPORT_SYMBOL(…); /** * __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 * * 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) { … } EXPORT_SYMBOL(…); /** * __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 * * 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) { … } /** * mdiobus_read_nested - Nested version of the mdiobus_read function * @bus: the mii_bus struct * @addr: the phy address * @regnum: register number to read * * 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) { … } EXPORT_SYMBOL(…); /** * 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 * * 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) { … } EXPORT_SYMBOL(…); /** * 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 * * 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) { … } EXPORT_SYMBOL(…); /** * 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 * * 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) { … } EXPORT_SYMBOL(…); /** * 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 * * 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) { … } EXPORT_SYMBOL(…); /** * 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 * * 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) { … } EXPORT_SYMBOL(…); /** * 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 * * 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) { … } EXPORT_SYMBOL(…); /** * 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 * * 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) { … } EXPORT_SYMBOL(…); /* * __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 */ int __mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set) { … } EXPORT_SYMBOL_GPL(…); /** * 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 */ int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set) { … } EXPORT_SYMBOL_GPL(…); /** * 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 */ int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum, u16 mask, u16 set) { … } EXPORT_SYMBOL_GPL(…); /** * 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 */ int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set) { … } EXPORT_SYMBOL_GPL(…); /** * 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 */ int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad, u32 regnum, u16 mask, u16 set) { … } EXPORT_SYMBOL_GPL(…); /** * mdio_bus_match - determine if given MDIO driver supports the given * MDIO device * @dev: target MDIO device * @drv: given MDIO driver * * Description: Given a MDIO device, and a MDIO driver, return 1 if * the driver supports the device. Otherwise, return 0. This may * require calling the devices own match function, since different classes * of MDIO devices have different match criteria. */ static int mdio_bus_match(struct device *dev, const struct device_driver *drv) { … } static int mdio_uevent(const struct device *dev, struct kobj_uevent_env *env) { … } static struct attribute *mdio_bus_device_statistics_attrs[] = …; static const struct attribute_group mdio_bus_device_statistics_group = …; static const struct attribute_group *mdio_bus_dev_groups[] = …; const struct bus_type mdio_bus_type = …; EXPORT_SYMBOL(…); int __init mdio_bus_init(void) { … } #if IS_ENABLED(CONFIG_PHYLIB) void mdio_bus_exit(void) { … } EXPORT_SYMBOL_GPL(…); #else module_init(mdio_bus_init); /* no module_exit, intentional */ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MDIO bus/device layer"); #endif