diff options
Diffstat (limited to 'drivers/net/phy/mdio_bus.c')
| -rw-r--r-- | drivers/net/phy/mdio_bus.c | 184 | 
1 files changed, 158 insertions, 26 deletions
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index dc920974204..203651ebccb 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -1,7 +1,4 @@ -/* - * drivers/net/phy/mdio_bus.c - * - * MDIO Bus interface +/* MDIO Bus interface   *   * Author: Andy Fleming   * @@ -36,10 +33,10 @@  #include <linux/mii.h>  #include <linux/ethtool.h>  #include <linux/phy.h> +#include <linux/io.h> +#include <linux/uaccess.h> -#include <asm/io.h>  #include <asm/irq.h> -#include <asm/uaccess.h>  /**   * mdiobus_alloc_size - allocate a mii_bus structure @@ -72,6 +69,73 @@ struct mii_bus *mdiobus_alloc_size(size_t size)  }  EXPORT_SYMBOL(mdiobus_alloc_size); +static void _devm_mdiobus_free(struct device *dev, void *res) +{ +	mdiobus_free(*(struct mii_bus **)res); +} + +static int devm_mdiobus_match(struct device *dev, void *res, void *data) +{ +	struct mii_bus **r = res; + +	if (WARN_ON(!r || !*r)) +		return 0; + +	return *r == data; +} + +/** + * devm_mdiobus_alloc_size - Resource-managed mdiobus_alloc_size() + * @dev:		Device to allocate mii_bus for + * @sizeof_priv:	Space to allocate for private structure. + * + * Managed mdiobus_alloc_size. mii_bus allocated with this function is + * automatically freed on driver detach. + * + * If an mii_bus allocated with this function needs to be freed separately, + * devm_mdiobus_free() must be used. + * + * RETURNS: + * Pointer to allocated mii_bus on success, NULL on failure. + */ +struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv) +{ +	struct mii_bus **ptr, *bus; + +	ptr = devres_alloc(_devm_mdiobus_free, sizeof(*ptr), GFP_KERNEL); +	if (!ptr) +		return NULL; + +	/* use raw alloc_dr for kmalloc caller tracing */ +	bus = mdiobus_alloc_size(sizeof_priv); +	if (bus) { +		*ptr = bus; +		devres_add(dev, ptr); +	} else { +		devres_free(ptr); +	} + +	return bus; +} +EXPORT_SYMBOL_GPL(devm_mdiobus_alloc_size); + +/** + * devm_mdiobus_free - Resource-managed mdiobus_free() + * @dev:		Device this mii_bus belongs to + * @bus:		the mii_bus associated with the device + * + * Free mii_bus allocated with devm_mdiobus_alloc_size(). + */ +void devm_mdiobus_free(struct device *dev, struct mii_bus *bus) +{ +	int rc; + +	rc = devres_release(dev, _devm_mdiobus_free, +			    devm_mdiobus_match, bus); +	WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_mdiobus_free); +  /**   * mdiobus_release - mii_bus device release callback   * @d: the target struct device that contains the mii_bus @@ -123,6 +187,50 @@ struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np)  	return d ? to_mii_bus(d) : NULL;  }  EXPORT_SYMBOL(of_mdio_find_bus); + +/* Walk the list of subnodes of a mdio bus and look for a node that matches the + * phy's address with its 'reg' property. If found, set the of_node pointer for + * the phy. This allows auto-probed pyh devices to be supplied with information + * passed in via DT. + */ +static void of_mdiobus_link_phydev(struct mii_bus *mdio, +				   struct phy_device *phydev) +{ +	struct device *dev = &phydev->dev; +	struct device_node *child; + +	if (dev->of_node || !mdio->dev.of_node) +		return; + +	for_each_available_child_of_node(mdio->dev.of_node, child) { +		int addr; +		int ret; + +		ret = of_property_read_u32(child, "reg", &addr); +		if (ret < 0) { +			dev_err(dev, "%s has invalid PHY address\n", +				child->full_name); +			continue; +		} + +		/* A PHY must have a reg property in the range [0-31] */ +		if (addr >= PHY_MAX_ADDR) { +			dev_err(dev, "%s PHY address %i is too large\n", +				child->full_name, addr); +			continue; +		} + +		if (addr == phydev->addr) { +			dev->of_node = child; +			return; +		} +	} +} +#else /* !IS_ENABLED(CONFIG_OF_MDIO) */ +static inline void of_mdiobus_link_phydev(struct mii_bus *mdio, +					  struct phy_device *phydev) +{ +}  #endif  /** @@ -139,8 +247,7 @@ int mdiobus_register(struct mii_bus *bus)  	int i, err;  	if (NULL == bus || NULL == bus->name || -			NULL == bus->read || -			NULL == bus->write) +	    NULL == bus->read || NULL == bus->write)  		return -EINVAL;  	BUG_ON(bus->state != MDIOBUS_ALLOCATED && @@ -148,12 +255,14 @@ int mdiobus_register(struct mii_bus *bus)  	bus->dev.parent = bus->parent;  	bus->dev.class = &mdio_bus_class; +	bus->dev.driver = bus->parent->driver;  	bus->dev.groups = NULL;  	dev_set_name(&bus->dev, "%s", bus->id);  	err = device_register(&bus->dev);  	if (err) {  		pr_err("mii_bus %s failed to register\n", bus->id); +		put_device(&bus->dev);  		return -EINVAL;  	} @@ -214,9 +323,7 @@ EXPORT_SYMBOL(mdiobus_unregister);   */  void mdiobus_free(struct mii_bus *bus)  { -	/* -	 * For compatibility with error handling in drivers. -	 */ +	/* For compatibility with error handling in drivers. */  	if (bus->state == MDIOBUS_ALLOCATED) {  		kfree(bus);  		return; @@ -238,6 +345,12 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)  	if (IS_ERR(phydev) || phydev == NULL)  		return phydev; +	/* +	 * For DT, see if the auto-probed phy has a correspoding child +	 * in the bus node, and set the of_node pointer in this case. +	 */ +	of_mdiobus_link_phydev(bus, phydev); +  	err = phy_device_register(phydev);  	if (err) {  		phy_device_free(phydev); @@ -316,8 +429,8 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv)  	if (phydrv->match_phy_device)  		return phydrv->match_phy_device(phydev); -	return ((phydrv->phy_id & phydrv->phy_id_mask) == -		(phydev->phy_id & phydrv->phy_id_mask)); +	return (phydrv->phy_id & phydrv->phy_id_mask) == +		(phydev->phy_id & phydrv->phy_id_mask);  }  #ifdef CONFIG_PM @@ -335,15 +448,13 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)  	if (!netdev)  		return true; -	/* -	 * Don't suspend PHY if the attched netdev parent may wakeup. +	/* Don't suspend PHY if the attched netdev parent may wakeup.  	 * The parent may point to a PCI device, as in tg3 driver.  	 */  	if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))  		return false; -	/* -	 * Also don't suspend PHY if the netdev itself may wakeup. This +	/* Also don't suspend PHY if the netdev itself may wakeup. This  	 * is the case for devices w/o underlaying pwr. mgmt. aware bus,  	 * e.g. SoC devices.  	 */ @@ -358,8 +469,7 @@ static int mdio_bus_suspend(struct device *dev)  	struct phy_driver *phydrv = to_phy_driver(dev->driver);  	struct phy_device *phydev = to_phy_device(dev); -	/* -	 * We must stop the state machine manually, otherwise it stops out of +	/* We must stop the state machine manually, otherwise it stops out of  	 * control, possibly with the phydev->lock held. Upon resume, netdev  	 * may call phy routines that try to grab the same lock, and that may  	 * lead to a deadlock. @@ -388,7 +498,7 @@ static int mdio_bus_resume(struct device *dev)  no_resume:  	if (phydev->attached_dev && phydev->adjust_link) -		phy_start_machine(phydev, NULL); +		phy_start_machine(phydev);  	return 0;  } @@ -410,12 +520,12 @@ static int mdio_bus_restore(struct device *dev)  	phydev->link = 0;  	phydev->state = PHY_UP; -	phy_start_machine(phydev, NULL); +	phy_start_machine(phydev);  	return 0;  } -static struct dev_pm_ops mdio_bus_pm_ops = { +static const struct dev_pm_ops mdio_bus_pm_ops = {  	.suspend = mdio_bus_suspend,  	.resume = mdio_bus_resume,  	.freeze = mdio_bus_suspend, @@ -438,17 +548,39 @@ phy_id_show(struct device *dev, struct device_attribute *attr, char *buf)  	return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id);  } +static DEVICE_ATTR_RO(phy_id); + +static ssize_t +phy_interface_show(struct device *dev, struct device_attribute *attr, char *buf) +{ +	struct phy_device *phydev = to_phy_device(dev); + +	return sprintf(buf, "%s\n", phy_modes(phydev->interface)); +} +static DEVICE_ATTR_RO(phy_interface); + +static ssize_t +phy_has_fixups_show(struct device *dev, struct device_attribute *attr, char *buf) +{ +	struct phy_device *phydev = to_phy_device(dev); + +	return sprintf(buf, "%d\n", phydev->has_fixups); +} +static DEVICE_ATTR_RO(phy_has_fixups); -static struct device_attribute mdio_dev_attrs[] = { -	__ATTR_RO(phy_id), -	__ATTR_NULL +static struct attribute *mdio_dev_attrs[] = { +	&dev_attr_phy_id.attr, +	&dev_attr_phy_interface.attr, +	&dev_attr_phy_has_fixups.attr, +	NULL,  }; +ATTRIBUTE_GROUPS(mdio_dev);  struct bus_type mdio_bus_type = {  	.name		= "mdio_bus",  	.match		= mdio_bus_match,  	.pm		= MDIO_BUS_PM_OPS, -	.dev_attrs	= mdio_dev_attrs, +	.dev_groups	= mdio_dev_groups,  };  EXPORT_SYMBOL(mdio_bus_type);  | 
