diff options
Diffstat (limited to 'drivers/phy/phy-core.c')
| -rw-r--r-- | drivers/phy/phy-core.c | 50 | 
1 files changed, 44 insertions, 6 deletions
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index e7e574dc667a..b72e9a3b6429 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -141,7 +141,7 @@ static struct phy_provider *of_phy_provider_lookup(struct device_node *node)  		if (phy_provider->dev->of_node == node)  			return phy_provider; -		for_each_child_of_node(phy_provider->dev->of_node, child) +		for_each_child_of_node(phy_provider->children, child)  			if (child == node)  				return phy_provider;  	} @@ -811,24 +811,59 @@ EXPORT_SYMBOL_GPL(devm_phy_destroy);  /**   * __of_phy_provider_register() - create/register phy provider with the framework   * @dev: struct device of the phy provider + * @children: device node containing children (if different from dev->of_node)   * @owner: the module owner containing of_xlate   * @of_xlate: function pointer to obtain phy instance from phy provider   *   * Creates struct phy_provider from dev and of_xlate function pointer.   * This is used in the case of dt boot for finding the phy instance from   * phy provider. + * + * If the PHY provider doesn't nest children directly but uses a separate + * child node to contain the individual children, the @children parameter + * can be used to override the default. If NULL, the default (dev->of_node) + * will be used. If non-NULL, the device node must be a child (or further + * descendant) of dev->of_node. Otherwise an ERR_PTR()-encoded -EINVAL + * error code is returned.   */  struct phy_provider *__of_phy_provider_register(struct device *dev, -	struct module *owner, struct phy * (*of_xlate)(struct device *dev, -	struct of_phandle_args *args)) +	struct device_node *children, struct module *owner, +	struct phy * (*of_xlate)(struct device *dev, +				 struct of_phandle_args *args))  {  	struct phy_provider *phy_provider; +	/* +	 * If specified, the device node containing the children must itself +	 * be the provider's device node or a child (or further descendant) +	 * thereof. +	 */ +	if (children) { +		struct device_node *parent = of_node_get(children), *next; + +		while (parent) { +			if (parent == dev->of_node) +				break; + +			next = of_get_parent(parent); +			of_node_put(parent); +			parent = next; +		} + +		if (!parent) +			return ERR_PTR(-EINVAL); + +		of_node_put(parent); +	} else { +		children = dev->of_node; +	} +  	phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);  	if (!phy_provider)  		return ERR_PTR(-ENOMEM);  	phy_provider->dev = dev; +	phy_provider->children = of_node_get(children);  	phy_provider->owner = owner;  	phy_provider->of_xlate = of_xlate; @@ -854,8 +889,9 @@ EXPORT_SYMBOL_GPL(__of_phy_provider_register);   * on the devres data, then, devres data is freed.   */  struct phy_provider *__devm_of_phy_provider_register(struct device *dev, -	struct module *owner, struct phy * (*of_xlate)(struct device *dev, -	struct of_phandle_args *args)) +	struct device_node *children, struct module *owner, +	struct phy * (*of_xlate)(struct device *dev, +				 struct of_phandle_args *args))  {  	struct phy_provider **ptr, *phy_provider; @@ -863,7 +899,8 @@ struct phy_provider *__devm_of_phy_provider_register(struct device *dev,  	if (!ptr)  		return ERR_PTR(-ENOMEM); -	phy_provider = __of_phy_provider_register(dev, owner, of_xlate); +	phy_provider = __of_phy_provider_register(dev, children, owner, +						  of_xlate);  	if (!IS_ERR(phy_provider)) {  		*ptr = phy_provider;  		devres_add(dev, ptr); @@ -888,6 +925,7 @@ void of_phy_provider_unregister(struct phy_provider *phy_provider)  	mutex_lock(&phy_provider_mutex);  	list_del(&phy_provider->list); +	of_node_put(phy_provider->children);  	kfree(phy_provider);  	mutex_unlock(&phy_provider_mutex);  }  |