| 1 | |
| 2 | |
| 3 | * of.c The helpers for hcd device tree support
|
| 4 | *
|
| 5 | * Copyright (C) 2016 Freescale Semiconductor, Inc.
|
| 6 | * Author: Peter Chen <[email protected]>
|
| 7 | * Copyright (C) 2017 Johan Hovold <[email protected]>
|
| 8 |
|
| 9 |
|
| 10 | #include <linux/of.h>
|
| 11 | #include <linux/of_graph.h>
|
| 12 | #include <linux/usb/of.h>
|
| 13 |
|
| 14 | |
| 15 | * usb_of_get_device_node() - get a USB device node
|
| 16 | * @hub: hub to which device is connected
|
| 17 | * @port1: one-based index of port
|
| 18 | *
|
| 19 | * Look up the node of a USB device given its parent hub device and one-based
|
| 20 | * port number.
|
| 21 | *
|
| 22 | * Return: A pointer to the node with incremented refcount if found, or
|
| 23 | * %NULL otherwise.
|
| 24 |
|
| 25 | struct device_node *usb_of_get_device_node(struct usb_device *hub, int port1)
|
| 26 | {
|
| 27 | struct device_node *node;
|
| 28 | u32 reg;
|
| 29 |
|
| 30 | for_each_child_of_node(hub->dev.of_node, node) {
|
| 31 | if (of_property_read_u32(node, "reg", ®))
|
| 32 | continue;
|
| 33 |
|
| 34 | if (reg == port1)
|
| 35 | return node;
|
| 36 | }
|
| 37 |
|
| 38 | return NULL;
|
| 39 | }
|
| 40 | EXPORT_SYMBOL_GPL(usb_of_get_device_node);
|
| 41 |
|
| 42 | |
| 43 | * usb_of_has_combined_node() - determine whether a device has a combined node
|
| 44 | * @udev: USB device
|
| 45 | *
|
| 46 | * Determine whether a USB device has a so called combined node which is
|
| 47 | * shared with its sole interface. This is the case if and only if the device
|
| 48 | * has a node and its descriptors report the following:
|
| 49 | *
|
| 50 | * 1) bDeviceClass is 0 or 9, and
|
| 51 | * 2) bNumConfigurations is 1, and
|
| 52 | * 3) bNumInterfaces is 1.
|
| 53 | *
|
| 54 | * Return: True iff the device has a device node and its descriptors match the
|
| 55 | * criteria for a combined node.
|
| 56 |
|
| 57 | bool usb_of_has_combined_node(struct usb_device *udev)
|
| 58 | {
|
| 59 | struct usb_device_descriptor *ddesc = &udev->descriptor;
|
| 60 | struct usb_config_descriptor *cdesc;
|
| 61 |
|
| 62 | if (!udev->dev.of_node)
|
| 63 | return false;
|
| 64 |
|
| 65 | switch (ddesc->bDeviceClass) {
|
| 66 | case USB_CLASS_PER_INTERFACE:
|
| 67 | case USB_CLASS_HUB:
|
| 68 | if (ddesc->bNumConfigurations == 1) {
|
| 69 | cdesc = &udev->config->desc;
|
| 70 | if (cdesc->bNumInterfaces == 1)
|
| 71 | return true;
|
| 72 | }
|
| 73 | }
|
| 74 |
|
| 75 | return false;
|
| 76 | }
|
| 77 | EXPORT_SYMBOL_GPL(usb_of_has_combined_node);
|
| 78 |
|
| 79 | static bool usb_of_has_devices_or_graph(const struct usb_device *hub)
|
| 80 | {
|
| 81 | const struct device_node *np = hub->dev.of_node;
|
| 82 |
|
| 83 | if (of_graph_is_present(np))
|
| 84 | return true;
|
| 85 |
|
| 86 | for_each_child_of_node_scoped(np, child)
|
| 87 | if (of_property_present(child, "reg"))
|
| 88 | return true;
|
| 89 |
|
| 90 | return false;
|
| 91 | }
|
| 92 |
|
| 93 | |
| 94 | * usb_of_get_connect_type() - get a USB hub's port connect_type
|
| 95 | * @hub: hub to which port is for @port1
|
| 96 | * @port1: one-based index of port
|
| 97 | *
|
| 98 | * Get the connect_type of @port1 based on the device node for @hub. If the
|
| 99 | * port is described in the OF graph, the connect_type is "hotplug". If the
|
| 100 | * @hub has a child device has with a 'reg' property equal to @port1 the
|
| 101 | * connect_type is "hard-wired". If there isn't an OF graph or child node at
|
| 102 | * all then the connect_type is "unknown". Otherwise, the port is considered
|
| 103 | * "unused" because it isn't described at all.
|
| 104 | *
|
| 105 | * Return: A connect_type for @port1 based on the device node for @hub.
|
| 106 |
|
| 107 | enum usb_port_connect_type usb_of_get_connect_type(struct usb_device *hub, int port1)
|
| 108 | {
|
| 109 | struct device_node *np, *child, *ep, *remote_np;
|
| 110 | enum usb_port_connect_type connect_type;
|
| 111 |
|
| 112 |
|
| 113 | if (!usb_of_has_devices_or_graph(hub))
|
| 114 | return USB_PORT_CONNECT_TYPE_UNKNOWN;
|
| 115 |
|
| 116 |
|
| 117 | connect_type = USB_PORT_NOT_USED;
|
| 118 |
|
| 119 | np = hub->dev.of_node;
|
| 120 | |
| 121 | * Hotplug ports are connected to an available remote node, e.g.
|
| 122 | * usb-a-connector compatible node, in the OF graph.
|
| 123 |
|
| 124 | if (of_graph_is_present(np)) {
|
| 125 | ep = of_graph_get_endpoint_by_regs(np, port1, -1);
|
| 126 | if (ep) {
|
| 127 | remote_np = of_graph_get_remote_port_parent(ep);
|
| 128 | of_node_put(ep);
|
| 129 | if (of_device_is_available(remote_np))
|
| 130 | connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG;
|
| 131 | of_node_put(remote_np);
|
| 132 | }
|
| 133 | }
|
| 134 |
|
| 135 | |
| 136 | * Hard-wired ports are child nodes with a reg property corresponding
|
| 137 | * to the port number, i.e. a usb device.
|
| 138 |
|
| 139 | child = usb_of_get_device_node(hub, port1);
|
| 140 | if (of_device_is_available(child))
|
| 141 | connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED;
|
| 142 | of_node_put(child);
|
| 143 |
|
| 144 | return connect_type;
|
| 145 | }
|
| 146 | EXPORT_SYMBOL_GPL(usb_of_get_connect_type);
|
| 147 |
|
| 148 | |
| 149 | * usb_of_get_interface_node() - get a USB interface node
|
| 150 | * @udev: USB device of interface
|
| 151 | * @config: configuration value
|
| 152 | * @ifnum: interface number
|
| 153 | *
|
| 154 | * Look up the node of a USB interface given its USB device, configuration
|
| 155 | * value and interface number.
|
| 156 | *
|
| 157 | * Return: A pointer to the node with incremented refcount if found, or
|
| 158 | * %NULL otherwise.
|
| 159 |
|
| 160 | struct device_node *
|
| 161 | usb_of_get_interface_node(struct usb_device *udev, u8 config, u8 ifnum)
|
| 162 | {
|
| 163 | struct device_node *node;
|
| 164 | u32 reg[2];
|
| 165 |
|
| 166 | for_each_child_of_node(udev->dev.of_node, node) {
|
| 167 | if (of_property_read_u32_array(node, "reg", reg, 2))
|
| 168 | continue;
|
| 169 |
|
| 170 | if (reg[0] == ifnum && reg[1] == config)
|
| 171 | return node;
|
| 172 | }
|
| 173 |
|
| 174 | return NULL;
|
| 175 | }
|
| 176 | EXPORT_SYMBOL_GPL(usb_of_get_interface_node);
|