diff options
-rw-r--r-- | drivers/usb/core/message.c | 18 | ||||
-rw-r--r-- | drivers/usb/core/of.c | 70 | ||||
-rw-r--r-- | include/linux/usb/of.h | 14 |
3 files changed, 96 insertions, 6 deletions
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 5a8ab77bc367..f836bae1e485 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -18,6 +18,7 @@ #include <linux/usb/cdc.h> #include <linux/usb/quirks.h> #include <linux/usb/hcd.h> /* for usbcore internals */ +#include <linux/usb/of.h> #include <asm/byteorder.h> #include "usb.h" @@ -1583,6 +1584,7 @@ static void usb_release_interface(struct device *dev) kref_put(&intfc->ref, usb_release_interface_cache); usb_put_dev(interface_to_usbdev(intf)); + of_node_put(dev->of_node); kfree(intf); } @@ -1868,6 +1870,7 @@ free_interfaces: struct usb_interface_cache *intfc; struct usb_interface *intf; struct usb_host_interface *alt; + u8 ifnum; cp->interface[i] = intf = new_interfaces[i]; intfc = cp->intf_cache[i]; @@ -1886,11 +1889,17 @@ free_interfaces: if (!alt) alt = &intf->altsetting[0]; - intf->intf_assoc = - find_iad(dev, cp, alt->desc.bInterfaceNumber); + ifnum = alt->desc.bInterfaceNumber; + intf->intf_assoc = find_iad(dev, cp, ifnum); intf->cur_altsetting = alt; usb_enable_interface(dev, intf, true); intf->dev.parent = &dev->dev; + if (usb_of_has_combined_node(dev)) { + device_set_of_node_from_dev(&intf->dev, &dev->dev); + } else { + intf->dev.of_node = usb_of_get_interface_node(dev, + configuration, ifnum); + } intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; intf->dev.type = &usb_if_device_type; @@ -1905,9 +1914,8 @@ free_interfaces: intf->minor = -1; device_initialize(&intf->dev); pm_runtime_no_callbacks(&intf->dev); - dev_set_name(&intf->dev, "%d-%s:%d.%d", - dev->bus->busnum, dev->devpath, - configuration, alt->desc.bInterfaceNumber); + dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, + dev->devpath, configuration, ifnum); usb_get_dev(dev); } kfree(new_interfaces); diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index 2be968353257..074fabc26d6c 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -3,7 +3,8 @@ * of.c The helpers for hcd device tree support * * Copyright (C) 2016 Freescale Semiconductor, Inc. - * Author: Peter Chen <peter.chen@freescale.com> + * Author: Peter Chen <peter.chen@freescale.com> + * Copyright (C) 2017 Johan Hovold <johan@kernel.org> */ #include <linux/of.h> @@ -38,6 +39,73 @@ struct device_node *usb_of_get_child_node(struct device_node *parent, EXPORT_SYMBOL_GPL(usb_of_get_child_node); /** + * usb_of_has_combined_node() - determine whether a device has a combined node + * @udev: USB device + * + * Determine whether a USB device has a so called combined node which is + * shared with its sole interface. This is the case if and only if the device + * has a node and its decriptors report the following: + * + * 1) bDeviceClass is 0 or 9, and + * 2) bNumConfigurations is 1, and + * 3) bNumInterfaces is 1. + * + * Return: True iff the device has a device node and its descriptors match the + * criteria for a combined node. + */ +bool usb_of_has_combined_node(struct usb_device *udev) +{ + struct usb_device_descriptor *ddesc = &udev->descriptor; + struct usb_config_descriptor *cdesc; + + if (!udev->dev.of_node) + return false; + + switch (ddesc->bDeviceClass) { + case USB_CLASS_PER_INTERFACE: + case USB_CLASS_HUB: + if (ddesc->bNumConfigurations == 1) { + cdesc = &udev->config->desc; + if (cdesc->bNumInterfaces == 1) + return true; + } + } + + return false; +} +EXPORT_SYMBOL_GPL(usb_of_has_combined_node); + +/** + * usb_of_get_interface_node() - get a USB interface node + * @udev: USB device of interface + * @config: configuration value + * @ifnum: interface number + * + * Look up the node of a USB interface given its USB device, configuration + * value and interface number. + * + * Return: A pointer to the node with incremented refcount if found, or + * %NULL otherwise. + */ +struct device_node * +usb_of_get_interface_node(struct usb_device *udev, u8 config, u8 ifnum) +{ + struct device_node *node; + u32 reg[2]; + + for_each_child_of_node(udev->dev.of_node, node) { + if (of_property_read_u32_array(node, "reg", reg, 2)) + continue; + + if (reg[0] == ifnum && reg[1] == config) + return node; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(usb_of_get_interface_node); + +/** * usb_of_get_companion_dev - Find the companion device * @dev: the device pointer to find a companion * diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index 6cbe7a5c2b57..0294ccac4f1d 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -12,6 +12,8 @@ #include <linux/usb/otg.h> #include <linux/usb/phy.h> +struct usb_device; + #if IS_ENABLED(CONFIG_OF) enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0); bool of_usb_host_tpl_support(struct device_node *np); @@ -19,6 +21,9 @@ int of_usb_update_otg_caps(struct device_node *np, struct usb_otg_caps *otg_caps); struct device_node *usb_of_get_child_node(struct device_node *parent, int portnum); +bool usb_of_has_combined_node(struct usb_device *udev); +struct device_node *usb_of_get_interface_node(struct usb_device *udev, + u8 config, u8 ifnum); struct device *usb_of_get_companion_dev(struct device *dev); #else static inline enum usb_dr_mode @@ -40,6 +45,15 @@ static inline struct device_node *usb_of_get_child_node { return NULL; } +static inline bool usb_of_has_combined_node(struct usb_device *udev) +{ + return false; +} +static inline struct device_node * +usb_of_get_interface_node(struct usb_device *udev, u8 config, u8 ifnum) +{ + return NULL; +} static inline struct device *usb_of_get_companion_dev(struct device *dev) { return NULL; |