From: Kishon Vijay Abraham I <kishon@xxxxxx> This patch adds an API to get usb phy by passing a device node phandle value. The new added devm_usb_get_phy_by_phandle() function will return a pointer to the phy on success, -EPROBE_DEFER if there is a device_node for the phandle, but the phy has not been added, or a ERR_PTR() otherwise. Since it's possible to obtain a phy by phandle, the checks in usb_add_phy() for a valid phy type is removed (now it's just a debug message if a user tries to add a phy with undefined type). This also allows to add multiple phys of same type. Cc: Richard Zhao <richard.zhao@xxxxxxxxxxxxx> Cc: Marek Vasut <marex@xxxxxxx> Signed-off-by: Kishon Vijay Abraham I <kishon@xxxxxx> Signed-off-by: Marc Kleine-Budde <mkl@xxxxxxxxxxxxxx> --- drivers/usb/otg/otg.c | 96 ++++++++++++++++++++++++++++++++++++++++------- include/linux/usb/otg.h | 8 ++++ 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 98c430e..23618de 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -15,6 +15,7 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/of.h> #include <linux/usb/otg.h> @@ -36,6 +37,21 @@ static struct usb_phy *__usb_find_phy(struct list_head *list, return ERR_PTR(-ENODEV); } +static struct usb_phy *__of_usb_find_phy(struct list_head *list, + struct device_node *node) +{ + struct usb_phy *phy; + + list_for_each_entry(phy, list, head) { + if (node != phy->dev->of_node) + continue; + + return phy; + } + + return ERR_PTR(-ENODEV); +} + static void devm_usb_phy_release(struct device *dev, void *res) { struct usb_phy *phy = *(struct usb_phy **)res; @@ -112,6 +128,66 @@ err0: EXPORT_SYMBOL(usb_get_phy); /** + * 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 + * + * 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) +{ + struct usb_phy *phy = NULL, **ptr; + unsigned long flags; + struct device_node *node; + + 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, 0); + 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) { + dev_dbg(dev, "failed to allocate memory for devres\n"); + return ERR_PTR(-ENOMEM); + } + + spin_lock_irqsave(&phy_lock, flags); + + phy = __of_usb_find_phy(&phy_list, node); + if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { + phy = ERR_PTR(-EPROBE_DEFER); + devres_free(ptr); + goto err0; + } + + *ptr = phy; + devres_add(dev, ptr); + + get_device(phy->dev); + +err0: + spin_unlock_irqrestore(&phy_lock, flags); + + return phy; +} +EXPORT_SYMBOL(devm_usb_get_phy_by_phandle); + +/** * devm_usb_put_phy - release the USB PHY * @dev - device that wants to release this phy * @phy - the phy returned by devm_usb_get_phy() @@ -158,32 +234,24 @@ EXPORT_SYMBOL(usb_put_phy); */ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) { - int ret = 0; unsigned long flags; struct usb_phy *phy; - if (x && x->type != USB_PHY_TYPE_UNDEFINED) { - dev_err(x->dev, "not accepting initialized PHY %s\n", x->label); - return -EINVAL; - } + if (x && x->type != USB_PHY_TYPE_UNDEFINED) + dev_dbg(x->dev, "add a phy with undefined type %s\n", x->label); spin_lock_irqsave(&phy_lock, flags); - list_for_each_entry(phy, &phy_list, head) { - if (phy->type == type) { - ret = -EBUSY; - dev_err(x->dev, "transceiver type %s already exists\n", + list_for_each_entry(phy, &phy_list, head) + if (phy->type == type) + dev_dbg(x->dev, "transceiver type %s already exists\n", usb_phy_type_string(type)); - goto out; - } - } x->type = type; list_add_tail(&x->head, &phy_list); -out: spin_unlock_irqrestore(&phy_lock, flags); - return ret; + return 0; } EXPORT_SYMBOL(usb_add_phy); diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 45824be..602d6b4 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -190,6 +190,8 @@ usb_phy_shutdown(struct usb_phy *x) extern struct usb_phy *usb_get_phy(enum usb_phy_type type); extern struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type); +extern struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, + const char *phandle); extern void usb_put_phy(struct usb_phy *); extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); extern const char *otg_state_string(enum usb_otg_state state); @@ -205,6 +207,12 @@ static inline struct usb_phy *devm_usb_get_phy(struct device *dev, return NULL; } +static inline struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, + const char *phandle) +{ + return NULL; +} + static inline void usb_put_phy(struct usb_phy *x) { } -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html