forked from mirrors/linux
		
	usb: phy: Add interface to get phy give of device_node.
Split the "get phy from device_node" functionality out of "get phy by phandle" so it can be used directly. This is useful when a battery-charger is intimately associated with a particular phy but handled by a separate driver. The charger can find the device_node based on sibling relationships without the need for a redundant declaration in the devicetree description. As a peripheral that gets a phy will often want to register a notifier block, and de-register it later, that functionality is included so the de-registration is automatic. Acked-by: Pavel Machek <pavel@ucw.cz> Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
		
							parent
							
								
									11bece5e06
								
							
						
					
					
						commit
						e842b84c8e
					
				
					 2 changed files with 72 additions and 27 deletions
				
			
		|  | @ -22,6 +22,11 @@ static LIST_HEAD(phy_list); | ||||||
| static LIST_HEAD(phy_bind_list); | static LIST_HEAD(phy_bind_list); | ||||||
| static DEFINE_SPINLOCK(phy_lock); | static DEFINE_SPINLOCK(phy_lock); | ||||||
| 
 | 
 | ||||||
|  | struct phy_devm { | ||||||
|  | 	struct usb_phy *phy; | ||||||
|  | 	struct notifier_block *nb; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| static struct usb_phy *__usb_find_phy(struct list_head *list, | static struct usb_phy *__usb_find_phy(struct list_head *list, | ||||||
| 	enum usb_phy_type type) | 	enum usb_phy_type type) | ||||||
| { | { | ||||||
|  | @ -79,6 +84,15 @@ static void devm_usb_phy_release(struct device *dev, void *res) | ||||||
| 	usb_put_phy(phy); | 	usb_put_phy(phy); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void devm_usb_phy_release2(struct device *dev, void *_res) | ||||||
|  | { | ||||||
|  | 	struct phy_devm *res = _res; | ||||||
|  | 
 | ||||||
|  | 	if (res->nb) | ||||||
|  | 		usb_unregister_notifier(res->phy, res->nb); | ||||||
|  | 	usb_put_phy(res->phy); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) | static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) | ||||||
| { | { | ||||||
| 	struct usb_phy **phy = res; | 	struct usb_phy **phy = res; | ||||||
|  | @ -153,40 +167,30 @@ struct usb_phy *usb_get_phy(enum usb_phy_type type) | ||||||
| EXPORT_SYMBOL_GPL(usb_get_phy); | EXPORT_SYMBOL_GPL(usb_get_phy); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * devm_usb_get_phy_by_phandle - find the USB PHY by phandle |  * devm_usb_get_phy_by_node - find the USB PHY by device_node | ||||||
|  * @dev - device that requests this phy |  * @dev - device that requests this phy | ||||||
|  * @phandle - name of the property holding the phy phandle value |  * @node - the device_node for the phy device. | ||||||
|  * @index - the index of the phy |  * @nb - a notifier_block to register with the phy. | ||||||
|  * |  * | ||||||
|  * Returns the phy driver associated with the given phandle value, |  * Returns the phy driver associated with the given device_node, | ||||||
|  * after getting a refcount to it, -ENODEV if there is no such phy or |  * after getting a refcount to it, -ENODEV if there is no such phy or | ||||||
|  * -EPROBE_DEFER if there is a phandle to the phy, but the device is |  * -EPROBE_DEFER if the device is not yet loaded. While at that, it | ||||||
|  * not yet loaded. While at that, it also associates the device with |  * also associates the device with | ||||||
|  * the phy using devres. On driver detach, release function is invoked |  * the phy using devres. On driver detach, release function is invoked | ||||||
|  * on the devres data, then, devres data is freed. |  * on the devres data, then, devres data is freed. | ||||||
|  * |  * | ||||||
|  * For use by USB host and peripheral drivers. |  * For use by peripheral drivers for devices related to a phy, | ||||||
|  |  * such as a charger. | ||||||
|  */ |  */ | ||||||
| struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, | struct  usb_phy *devm_usb_get_phy_by_node(struct device *dev, | ||||||
| 	const char *phandle, u8 index) | 					  struct device_node *node, | ||||||
|  | 					  struct notifier_block *nb) | ||||||
| { | { | ||||||
| 	struct usb_phy	*phy = ERR_PTR(-ENOMEM), **ptr; | 	struct usb_phy	*phy = ERR_PTR(-ENOMEM); | ||||||
|  | 	struct phy_devm	*ptr; | ||||||
| 	unsigned long	flags; | 	unsigned long	flags; | ||||||
| 	struct device_node *node; |  | ||||||
| 
 | 
 | ||||||
| 	if (!dev->of_node) { | 	ptr = devres_alloc(devm_usb_phy_release2, sizeof(*ptr), GFP_KERNEL); | ||||||
| 		dev_dbg(dev, "device does not have a device node entry\n"); |  | ||||||
| 		return ERR_PTR(-EINVAL); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	node = of_parse_phandle(dev->of_node, phandle, index); |  | ||||||
| 	if (!node) { |  | ||||||
| 		dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, |  | ||||||
| 			dev->of_node->full_name); |  | ||||||
| 		return ERR_PTR(-ENODEV); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); |  | ||||||
| 	if (!ptr) { | 	if (!ptr) { | ||||||
| 		dev_dbg(dev, "failed to allocate memory for devres\n"); | 		dev_dbg(dev, "failed to allocate memory for devres\n"); | ||||||
| 		goto err0; | 		goto err0; | ||||||
|  | @ -205,8 +209,10 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, | ||||||
| 		devres_free(ptr); | 		devres_free(ptr); | ||||||
| 		goto err1; | 		goto err1; | ||||||
| 	} | 	} | ||||||
| 
 | 	if (nb) | ||||||
| 	*ptr = phy; | 		usb_register_notifier(phy, nb); | ||||||
|  | 	ptr->phy = phy; | ||||||
|  | 	ptr->nb = nb; | ||||||
| 	devres_add(dev, ptr); | 	devres_add(dev, ptr); | ||||||
| 
 | 
 | ||||||
| 	get_device(phy->dev); | 	get_device(phy->dev); | ||||||
|  | @ -215,10 +221,47 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, | ||||||
| 	spin_unlock_irqrestore(&phy_lock, flags); | 	spin_unlock_irqrestore(&phy_lock, flags); | ||||||
| 
 | 
 | ||||||
| err0: | err0: | ||||||
| 	of_node_put(node); |  | ||||||
| 
 | 
 | ||||||
| 	return phy; | 	return phy; | ||||||
| } | } | ||||||
|  | EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_node); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * devm_usb_get_phy_by_phandle - find the USB PHY by phandle | ||||||
|  |  * @dev - device that requests this phy | ||||||
|  |  * @phandle - name of the property holding the phy phandle value | ||||||
|  |  * @index - the index of the phy | ||||||
|  |  * | ||||||
|  |  * Returns the phy driver associated with the given phandle value, | ||||||
|  |  * after getting a refcount to it, -ENODEV if there is no such phy or | ||||||
|  |  * -EPROBE_DEFER if there is a phandle to the phy, but the device is | ||||||
|  |  * not yet loaded. While at that, it also associates the device with | ||||||
|  |  * the phy using devres. On driver detach, release function is invoked | ||||||
|  |  * on the devres data, then, devres data is freed. | ||||||
|  |  * | ||||||
|  |  * For use by USB host and peripheral drivers. | ||||||
|  |  */ | ||||||
|  | struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, | ||||||
|  | 	const char *phandle, u8 index) | ||||||
|  | { | ||||||
|  | 	struct device_node *node; | ||||||
|  | 	struct usb_phy	*phy; | ||||||
|  | 
 | ||||||
|  | 	if (!dev->of_node) { | ||||||
|  | 		dev_dbg(dev, "device does not have a device node entry\n"); | ||||||
|  | 		return ERR_PTR(-EINVAL); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	node = of_parse_phandle(dev->of_node, phandle, index); | ||||||
|  | 	if (!node) { | ||||||
|  | 		dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, | ||||||
|  | 			dev->of_node->full_name); | ||||||
|  | 		return ERR_PTR(-ENODEV); | ||||||
|  | 	} | ||||||
|  | 	phy = devm_usb_get_phy_by_node(dev, node, NULL); | ||||||
|  | 	of_node_put(node); | ||||||
|  | 	return phy; | ||||||
|  | } | ||||||
| EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle); | EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
|  | @ -205,6 +205,8 @@ extern struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index); | ||||||
| extern struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index); | extern struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index); | ||||||
| extern struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, | extern struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, | ||||||
| 	const char *phandle, u8 index); | 	const char *phandle, u8 index); | ||||||
|  | extern struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, | ||||||
|  | 	struct device_node *node, struct notifier_block *nb); | ||||||
| extern void usb_put_phy(struct usb_phy *); | extern void usb_put_phy(struct usb_phy *); | ||||||
| extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); | extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); | ||||||
| extern int usb_bind_phy(const char *dev_name, u8 index, | extern int usb_bind_phy(const char *dev_name, u8 index, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 NeilBrown
						NeilBrown