diff options
Diffstat (limited to 'drivers/of/of_mdio.c')
| -rw-r--r-- | drivers/of/of_mdio.c | 274 | 
1 files changed, 171 insertions, 103 deletions
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index d5a57a9e329..401b2453da4 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -14,6 +14,7 @@  #include <linux/netdevice.h>  #include <linux/err.h>  #include <linux/phy.h> +#include <linux/phy_fixed.h>  #include <linux/of.h>  #include <linux/of_irq.h>  #include <linux/of_mdio.h> @@ -22,6 +23,92 @@  MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");  MODULE_LICENSE("GPL"); +/* Extract the clause 22 phy ID from the compatible string of the form + * 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 int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *child, +				   u32 addr) +{ +	struct phy_device *phy; +	bool is_c45; +	int rc; +	u32 phy_id; + +	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 (!phy || IS_ERR(phy)) +		return 1; + +	rc = irq_of_parse_and_map(child, 0); +	if (rc > 0) { +		phy->irq = rc; +		if (mdio->irq) +			mdio->irq[addr] = rc; +	} else { +		if (mdio->irq) +			phy->irq = mdio->irq[addr]; +	} + +	/* Associate the OF node with the device structure so it +	 * can be looked up later */ +	of_node_get(child); +	phy->dev.of_node = child; + +	/* All data is now stored in the phy struct; +	 * register it */ +	rc = phy_device_register(phy); +	if (rc) { +		phy_device_free(phy); +		of_node_put(child); +		return 1; +	} + +	dev_dbg(&mdio->dev, "registered phy %s at address %i\n", +		child->name, addr); + +	return 0; +} + +static int of_mdio_parse_addr(struct device *dev, const struct device_node *np) +{ +	u32 addr; +	int ret; + +	ret = of_property_read_u32(np, "reg", &addr); +	if (ret < 0) { +		dev_err(dev, "%s has invalid PHY address\n", np->full_name); +		return ret; +	} + +	/* 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", +			np->full_name, addr); +		return -EINVAL; +	} + +	return addr; +} +  /**   * of_mdiobus_register - Register mii_bus and create PHYs from the device tree   * @mdio: pointer to mii_bus structure @@ -32,12 +119,10 @@ MODULE_LICENSE("GPL");   */  int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)  { -	struct phy_device *phy;  	struct device_node *child;  	const __be32 *paddr; -	u32 addr; -	bool is_c45, scanphys = false; -	int rc, i, len; +	bool scanphys = false; +	int addr, rc, i;  	/* Mask out all PHYs from auto probing.  Instead the PHYs listed in  	 * the device tree are populated after the bus has been registered */ @@ -57,54 +142,15 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)  	/* Loop over the child nodes and register a phy_device for each one */  	for_each_available_child_of_node(np, child) { -		/* A PHY must have a reg property in the range [0-31] */ -		paddr = of_get_property(child, "reg", &len); -		if (!paddr || len < sizeof(*paddr)) { +		addr = of_mdio_parse_addr(&mdio->dev, child); +		if (addr < 0) {  			scanphys = true; -			dev_err(&mdio->dev, "%s has invalid PHY address\n", -				child->full_name); -			continue; -		} - -		addr = be32_to_cpup(paddr); -		if (addr >= 32) { -			dev_err(&mdio->dev, "%s PHY address %i is too large\n", -				child->full_name, addr); -			continue; -		} - -		if (mdio->irq) { -			mdio->irq[addr] = irq_of_parse_and_map(child, 0); -			if (!mdio->irq[addr]) -				mdio->irq[addr] = PHY_POLL; -		} - -		is_c45 = of_device_is_compatible(child, -						 "ethernet-phy-ieee802.3-c45"); -		phy = get_phy_device(mdio, addr, is_c45); - -		if (!phy || IS_ERR(phy)) { -			dev_err(&mdio->dev, -				"cannot get PHY at address %i\n", -				addr);  			continue;  		} -		/* Associate the OF node with the device structure so it -		 * can be looked up later */ -		of_node_get(child); -		phy->dev.of_node = child; - -		/* All data is now stored in the phy struct; register it */ -		rc = phy_device_register(phy); -		if (rc) { -			phy_device_free(phy); -			of_node_put(child); +		rc = of_mdiobus_register_phy(mdio, child, addr); +		if (rc)  			continue; -		} - -		dev_dbg(&mdio->dev, "registered phy %s at address %i\n", -			child->name, addr);  	}  	if (!scanphys) @@ -113,13 +159,10 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)  	/* auto scan for PHYs with empty reg property */  	for_each_available_child_of_node(np, child) {  		/* Skip PHYs with reg property set */ -		paddr = of_get_property(child, "reg", &len); +		paddr = of_get_property(child, "reg", NULL);  		if (paddr)  			continue; -		is_c45 = of_device_is_compatible(child, -						 "ethernet-phy-ieee802.3-c45"); -  		for (addr = 0; addr < PHY_MAX_ADDR; addr++) {  			/* skip already registered PHYs */  			if (mdio->phy_map[addr]) @@ -129,34 +172,9 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)  			dev_info(&mdio->dev, "scan phy %s at address %i\n",  				 child->name, addr); -			phy = get_phy_device(mdio, addr, is_c45); -			if (!phy || IS_ERR(phy)) -				continue; - -			if (mdio->irq) { -				mdio->irq[addr] = -					irq_of_parse_and_map(child, 0); -				if (!mdio->irq[addr]) -					mdio->irq[addr] = PHY_POLL; -			} - -			/* Associate the OF node with the device structure so it -			 * can be looked up later */ -			of_node_get(child); -			phy->dev.of_node = child; - -			/* All data is now stored in the phy struct; -			 * register it */ -			rc = phy_device_register(phy); -			if (rc) { -				phy_device_free(phy); -				of_node_put(child); +			rc = of_mdiobus_register_phy(mdio, child, addr); +			if (rc)  				continue; -			} - -			dev_info(&mdio->dev, "registered phy %s at address %i\n", -				 child->name, addr); -			break;  		}  	} @@ -211,39 +229,89 @@ struct phy_device *of_phy_connect(struct net_device *dev,  EXPORT_SYMBOL(of_phy_connect);  /** - * of_phy_connect_fixed_link - Parse fixed-link property and return a dummy phy + * of_phy_attach - Attach to a PHY without starting the state machine   * @dev: pointer to net_device claiming the phy - * @hndlr: Link state callback for the network device + * @phy_np: Node pointer for the PHY + * @flags: flags to pass to the PHY   * @iface: PHY data interface type - * - * This function is a temporary stop-gap and will be removed soon.  It is - * only to support the fs_enet, ucc_geth and gianfar Ethernet drivers.  Do - * not call this function from new drivers.   */ -struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, -					     void (*hndlr)(struct net_device *), -					     phy_interface_t iface) +struct phy_device *of_phy_attach(struct net_device *dev, +				 struct device_node *phy_np, u32 flags, +				 phy_interface_t iface)  { -	struct device_node *net_np; -	char bus_id[MII_BUS_ID_SIZE + 3]; -	struct phy_device *phy; -	const __be32 *phy_id; -	int sz; +	struct phy_device *phy = of_phy_find_device(phy_np); -	if (!dev->dev.parent) +	if (!phy)  		return NULL; -	net_np = dev->dev.parent->of_node; -	if (!net_np) -		return NULL; +	return phy_attach_direct(dev, phy, flags, iface) ? NULL : phy; +} +EXPORT_SYMBOL(of_phy_attach); -	phy_id = of_get_property(net_np, "fixed-link", &sz); -	if (!phy_id || sz < sizeof(*phy_id)) -		return NULL; +#if defined(CONFIG_FIXED_PHY) +/* + * of_phy_is_fixed_link() and of_phy_register_fixed_link() must + * support two DT bindings: + * - the old DT binding, where 'fixed-link' was a property with 5 + *   cells encoding various informations about the fixed PHY + * - the new DT binding, where 'fixed-link' is a sub-node of the + *   Ethernet device. + */ +bool of_phy_is_fixed_link(struct device_node *np) +{ +	struct device_node *dn; +	int len; + +	/* New binding */ +	dn = of_get_child_by_name(np, "fixed-link"); +	if (dn) { +		of_node_put(dn); +		return true; +	} + +	/* Old binding */ +	if (of_get_property(np, "fixed-link", &len) && +	    len == (5 * sizeof(__be32))) +		return true; + +	return false; +} +EXPORT_SYMBOL(of_phy_is_fixed_link); -	sprintf(bus_id, PHY_ID_FMT, "fixed-0", be32_to_cpu(phy_id[0])); +int of_phy_register_fixed_link(struct device_node *np) +{ +	struct fixed_phy_status status = {}; +	struct device_node *fixed_link_node; +	const __be32 *fixed_link_prop; +	int len; + +	/* New binding */ +	fixed_link_node = of_get_child_by_name(np, "fixed-link"); +	if (fixed_link_node) { +		status.link = 1; +		status.duplex = of_property_read_bool(fixed_link_node, +						      "full-duplex"); +		if (of_property_read_u32(fixed_link_node, "speed", &status.speed)) +			return -EINVAL; +		status.pause = of_property_read_bool(fixed_link_node, "pause"); +		status.asym_pause = of_property_read_bool(fixed_link_node, +							  "asym-pause"); +		of_node_put(fixed_link_node); +		return fixed_phy_register(PHY_POLL, &status, np); +	} + +	/* Old binding */ +	fixed_link_prop = of_get_property(np, "fixed-link", &len); +	if (fixed_link_prop && len == (5 * sizeof(__be32))) { +		status.link = 1; +		status.duplex = be32_to_cpu(fixed_link_prop[1]); +		status.speed = be32_to_cpu(fixed_link_prop[2]); +		status.pause = be32_to_cpu(fixed_link_prop[3]); +		status.asym_pause = be32_to_cpu(fixed_link_prop[4]); +		return fixed_phy_register(PHY_POLL, &status, np); +	} -	phy = phy_connect(dev, bus_id, hndlr, iface); -	return IS_ERR(phy) ? NULL : phy; +	return -ENODEV;  } -EXPORT_SYMBOL(of_phy_connect_fixed_link); +EXPORT_SYMBOL(of_phy_register_fixed_link); +#endif  | 
