162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * of.c		The helpers for hcd device tree support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016 Freescale Semiconductor, Inc.
662306a36Sopenharmony_ci *	Author: Peter Chen <peter.chen@freescale.com>
762306a36Sopenharmony_ci * Copyright (C) 2017 Johan Hovold <johan@kernel.org>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/usb/of.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/**
1462306a36Sopenharmony_ci * usb_of_get_device_node() - get a USB device node
1562306a36Sopenharmony_ci * @hub: hub to which device is connected
1662306a36Sopenharmony_ci * @port1: one-based index of port
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Look up the node of a USB device given its parent hub device and one-based
1962306a36Sopenharmony_ci * port number.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * Return: A pointer to the node with incremented refcount if found, or
2262306a36Sopenharmony_ci * %NULL otherwise.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_cistruct device_node *usb_of_get_device_node(struct usb_device *hub, int port1)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct device_node *node;
2762306a36Sopenharmony_ci	u32 reg;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	for_each_child_of_node(hub->dev.of_node, node) {
3062306a36Sopenharmony_ci		if (of_property_read_u32(node, "reg", &reg))
3162306a36Sopenharmony_ci			continue;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci		if (reg == port1)
3462306a36Sopenharmony_ci			return node;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return NULL;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_of_get_device_node);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/**
4262306a36Sopenharmony_ci * usb_of_has_combined_node() - determine whether a device has a combined node
4362306a36Sopenharmony_ci * @udev: USB device
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * Determine whether a USB device has a so called combined node which is
4662306a36Sopenharmony_ci * shared with its sole interface. This is the case if and only if the device
4762306a36Sopenharmony_ci * has a node and its descriptors report the following:
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci *	1) bDeviceClass is 0 or 9, and
5062306a36Sopenharmony_ci *	2) bNumConfigurations is 1, and
5162306a36Sopenharmony_ci *	3) bNumInterfaces is 1.
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci * Return: True iff the device has a device node and its descriptors match the
5462306a36Sopenharmony_ci * criteria for a combined node.
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_cibool usb_of_has_combined_node(struct usb_device *udev)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct usb_device_descriptor *ddesc = &udev->descriptor;
5962306a36Sopenharmony_ci	struct usb_config_descriptor *cdesc;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (!udev->dev.of_node)
6262306a36Sopenharmony_ci		return false;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	switch (ddesc->bDeviceClass) {
6562306a36Sopenharmony_ci	case USB_CLASS_PER_INTERFACE:
6662306a36Sopenharmony_ci	case USB_CLASS_HUB:
6762306a36Sopenharmony_ci		if (ddesc->bNumConfigurations == 1) {
6862306a36Sopenharmony_ci			cdesc = &udev->config->desc;
6962306a36Sopenharmony_ci			if (cdesc->bNumInterfaces == 1)
7062306a36Sopenharmony_ci				return true;
7162306a36Sopenharmony_ci		}
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return false;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_of_has_combined_node);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/**
7962306a36Sopenharmony_ci * usb_of_get_interface_node() - get a USB interface node
8062306a36Sopenharmony_ci * @udev: USB device of interface
8162306a36Sopenharmony_ci * @config: configuration value
8262306a36Sopenharmony_ci * @ifnum: interface number
8362306a36Sopenharmony_ci *
8462306a36Sopenharmony_ci * Look up the node of a USB interface given its USB device, configuration
8562306a36Sopenharmony_ci * value and interface number.
8662306a36Sopenharmony_ci *
8762306a36Sopenharmony_ci * Return: A pointer to the node with incremented refcount if found, or
8862306a36Sopenharmony_ci * %NULL otherwise.
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistruct device_node *
9162306a36Sopenharmony_ciusb_of_get_interface_node(struct usb_device *udev, u8 config, u8 ifnum)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct device_node *node;
9462306a36Sopenharmony_ci	u32 reg[2];
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	for_each_child_of_node(udev->dev.of_node, node) {
9762306a36Sopenharmony_ci		if (of_property_read_u32_array(node, "reg", reg, 2))
9862306a36Sopenharmony_ci			continue;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		if (reg[0] == ifnum && reg[1] == config)
10162306a36Sopenharmony_ci			return node;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return NULL;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_of_get_interface_node);
107