Some USB phy drivers have different handling for the controller in each dr_mode. But the phy driver does not have visibility to the dr_mode of the controller. This adds an api to return the dr_mode of the controller which associates the given phy node. Signed-off-by: Bin Liu <b-liu@xxxxxx> --- v5: - to simplify the code, add usb_get_dr_mode_from_string() to query dr_mode from string v4: - iterating all phy nodes in the associated controller - add of_node_put() to decrement refcount v3: search controller node from dt root, as the phy and controller nodes might not have the same parent. v2: move drivers/usb/phy/phy-am335x.c changes into patch 3/3. drivers/usb/common/common.c | 60 ++++++++++++++++++++++++++++++++++++++++----- include/linux/usb/of.h | 5 ++++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index 673d530..a11bb15 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -17,6 +17,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/of.h> #include <linux/usb/otg.h> +#include <linux/of_platform.h> const char *usb_otg_state_string(enum usb_otg_state state) { @@ -106,23 +107,70 @@ static const char *const usb_dr_modes[] = { [USB_DR_MODE_OTG] = "otg", }; +static enum usb_dr_mode usb_get_dr_mode_from_string(const char *str) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++) + if (!strcmp(usb_dr_modes[i], str)) + return i; + + return USB_DR_MODE_UNKNOWN; +} + enum usb_dr_mode usb_get_dr_mode(struct device *dev) { const char *dr_mode; - int err, i; + int err; err = device_property_read_string(dev, "dr_mode", &dr_mode); if (err < 0) return USB_DR_MODE_UNKNOWN; - for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++) - if (!strcmp(dr_mode, usb_dr_modes[i])) - return i; - - return USB_DR_MODE_UNKNOWN; + return usb_get_dr_mode_from_string(dr_mode); } EXPORT_SYMBOL_GPL(usb_get_dr_mode); +/** + * of_usb_get_dr_mode_by_phy - Get dual role mode for the controller device + * which is associated with the given phy device_node + * @np: Pointer to the given phy device_node + * + * In dts a usb controller associates with phy devices. The function gets + * the string from property 'dr_mode' of the controller associated with the + * given phy device node, and returns the correspondig enum usb_dr_mode. + */ +enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np) +{ + struct device_node *controller = NULL; + struct device_node *phy; + const char *dr_mode; + int index; + int err; + + do { + controller = of_find_node_with_property(controller, "phys"); + index = 0; + do { + phy = of_parse_phandle(controller, "phys", index); + of_node_put(phy); + if (phy == phy_np) + goto finish; + index++; + } while (phy); + } while (controller); + +finish: + err = of_property_read_string(controller, "dr_mode", &dr_mode); + of_node_put(controller); + + if (err < 0) + return USB_DR_MODE_UNKNOWN; + + return usb_get_dr_mode_from_string(dr_mode); +} +EXPORT_SYMBOL_GPL(of_usb_get_dr_mode_by_phy); + #ifdef CONFIG_OF /** * of_usb_host_tpl_support - to get if Targeted Peripheral List is supported diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index c3fe9e4..3805757 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -12,10 +12,15 @@ #include <linux/usb/phy.h> #if IS_ENABLED(CONFIG_OF) +enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np); bool of_usb_host_tpl_support(struct device_node *np); int of_usb_update_otg_caps(struct device_node *np, struct usb_otg_caps *otg_caps); #else +enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np) +{ + return USB_DR_MODE_UNKNOWN; +} static inline bool of_usb_host_tpl_support(struct device_node *np) { return false; -- 1.8.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