18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * hub.c - virtual hub handling
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright 2017 IBM Corporation
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
108c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
118c2ecf20Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
128c2ecf20Sopenharmony_ci * (at your option) any later version.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/ioport.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/errno.h>
228c2ecf20Sopenharmony_ci#include <linux/list.h>
238c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
248c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
258c2ecf20Sopenharmony_ci#include <linux/prefetch.h>
268c2ecf20Sopenharmony_ci#include <linux/clk.h>
278c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h>
288c2ecf20Sopenharmony_ci#include <linux/of.h>
298c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
308c2ecf20Sopenharmony_ci#include <linux/regmap.h>
318c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
328c2ecf20Sopenharmony_ci#include <linux/bcd.h>
338c2ecf20Sopenharmony_ci#include <linux/version.h>
348c2ecf20Sopenharmony_ci#include <linux/usb.h>
358c2ecf20Sopenharmony_ci#include <linux/usb/hcd.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "vhub.h"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* usb 2.0 hub device descriptor
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * A few things we may want to improve here:
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci *    - We may need to indicate TT support
448c2ecf20Sopenharmony_ci *    - We may need a device qualifier descriptor
458c2ecf20Sopenharmony_ci *	as devices can pretend to be usb1 or 2
468c2ecf20Sopenharmony_ci *    - Make vid/did overridable
478c2ecf20Sopenharmony_ci *    - make it look like usb1 if usb1 mode forced
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_ci#define KERNEL_REL	bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff))
508c2ecf20Sopenharmony_ci#define KERNEL_VER	bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cienum {
538c2ecf20Sopenharmony_ci	AST_VHUB_STR_INDEX_MAX = 4,
548c2ecf20Sopenharmony_ci	AST_VHUB_STR_MANUF = 3,
558c2ecf20Sopenharmony_ci	AST_VHUB_STR_PRODUCT = 2,
568c2ecf20Sopenharmony_ci	AST_VHUB_STR_SERIAL = 1,
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic const struct usb_device_descriptor ast_vhub_dev_desc = {
608c2ecf20Sopenharmony_ci	.bLength		= USB_DT_DEVICE_SIZE,
618c2ecf20Sopenharmony_ci	.bDescriptorType	= USB_DT_DEVICE,
628c2ecf20Sopenharmony_ci	.bcdUSB			= cpu_to_le16(0x0200),
638c2ecf20Sopenharmony_ci	.bDeviceClass		= USB_CLASS_HUB,
648c2ecf20Sopenharmony_ci	.bDeviceSubClass	= 0,
658c2ecf20Sopenharmony_ci	.bDeviceProtocol	= 1,
668c2ecf20Sopenharmony_ci	.bMaxPacketSize0	= 64,
678c2ecf20Sopenharmony_ci	.idVendor		= cpu_to_le16(0x1d6b),
688c2ecf20Sopenharmony_ci	.idProduct		= cpu_to_le16(0x0107),
698c2ecf20Sopenharmony_ci	.bcdDevice		= cpu_to_le16(0x0100),
708c2ecf20Sopenharmony_ci	.iManufacturer		= AST_VHUB_STR_MANUF,
718c2ecf20Sopenharmony_ci	.iProduct		= AST_VHUB_STR_PRODUCT,
728c2ecf20Sopenharmony_ci	.iSerialNumber		= AST_VHUB_STR_SERIAL,
738c2ecf20Sopenharmony_ci	.bNumConfigurations	= 1,
748c2ecf20Sopenharmony_ci};
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/*
778c2ecf20Sopenharmony_ci * Configuration descriptor: same comments as above
788c2ecf20Sopenharmony_ci * regarding handling USB1 mode.
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/*
828c2ecf20Sopenharmony_ci * We don't use sizeof() as Linux definition of
838c2ecf20Sopenharmony_ci * struct usb_endpoint_descriptor contains 2
848c2ecf20Sopenharmony_ci * extra bytes
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_ci#define AST_VHUB_CONF_DESC_SIZE	(USB_DT_CONFIG_SIZE + \
878c2ecf20Sopenharmony_ci				 USB_DT_INTERFACE_SIZE + \
888c2ecf20Sopenharmony_ci				 USB_DT_ENDPOINT_SIZE)
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic const struct ast_vhub_full_cdesc ast_vhub_conf_desc = {
918c2ecf20Sopenharmony_ci	.cfg = {
928c2ecf20Sopenharmony_ci		.bLength		= USB_DT_CONFIG_SIZE,
938c2ecf20Sopenharmony_ci		.bDescriptorType	= USB_DT_CONFIG,
948c2ecf20Sopenharmony_ci		.wTotalLength		= cpu_to_le16(AST_VHUB_CONF_DESC_SIZE),
958c2ecf20Sopenharmony_ci		.bNumInterfaces		= 1,
968c2ecf20Sopenharmony_ci		.bConfigurationValue	= 1,
978c2ecf20Sopenharmony_ci		.iConfiguration		= 0,
988c2ecf20Sopenharmony_ci		.bmAttributes		= USB_CONFIG_ATT_ONE |
998c2ecf20Sopenharmony_ci					  USB_CONFIG_ATT_SELFPOWER |
1008c2ecf20Sopenharmony_ci					  USB_CONFIG_ATT_WAKEUP,
1018c2ecf20Sopenharmony_ci		.bMaxPower		= 0,
1028c2ecf20Sopenharmony_ci	},
1038c2ecf20Sopenharmony_ci	.intf = {
1048c2ecf20Sopenharmony_ci		.bLength		= USB_DT_INTERFACE_SIZE,
1058c2ecf20Sopenharmony_ci		.bDescriptorType	= USB_DT_INTERFACE,
1068c2ecf20Sopenharmony_ci		.bInterfaceNumber	= 0,
1078c2ecf20Sopenharmony_ci		.bAlternateSetting	= 0,
1088c2ecf20Sopenharmony_ci		.bNumEndpoints		= 1,
1098c2ecf20Sopenharmony_ci		.bInterfaceClass	= USB_CLASS_HUB,
1108c2ecf20Sopenharmony_ci		.bInterfaceSubClass	= 0,
1118c2ecf20Sopenharmony_ci		.bInterfaceProtocol	= 0,
1128c2ecf20Sopenharmony_ci		.iInterface		= 0,
1138c2ecf20Sopenharmony_ci	},
1148c2ecf20Sopenharmony_ci	.ep = {
1158c2ecf20Sopenharmony_ci		.bLength		= USB_DT_ENDPOINT_SIZE,
1168c2ecf20Sopenharmony_ci		.bDescriptorType	= USB_DT_ENDPOINT,
1178c2ecf20Sopenharmony_ci		.bEndpointAddress	= 0x81,
1188c2ecf20Sopenharmony_ci		.bmAttributes		= USB_ENDPOINT_XFER_INT,
1198c2ecf20Sopenharmony_ci		.wMaxPacketSize		= cpu_to_le16(1),
1208c2ecf20Sopenharmony_ci		.bInterval		= 0x0c,
1218c2ecf20Sopenharmony_ci	},
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#define AST_VHUB_HUB_DESC_SIZE	(USB_DT_HUB_NONVAR_SIZE + 2)
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic const struct usb_hub_descriptor ast_vhub_hub_desc = {
1278c2ecf20Sopenharmony_ci	.bDescLength			= AST_VHUB_HUB_DESC_SIZE,
1288c2ecf20Sopenharmony_ci	.bDescriptorType		= USB_DT_HUB,
1298c2ecf20Sopenharmony_ci	.bNbrPorts			= AST_VHUB_NUM_PORTS,
1308c2ecf20Sopenharmony_ci	.wHubCharacteristics		= cpu_to_le16(HUB_CHAR_NO_LPSM),
1318c2ecf20Sopenharmony_ci	.bPwrOn2PwrGood			= 10,
1328c2ecf20Sopenharmony_ci	.bHubContrCurrent		= 0,
1338c2ecf20Sopenharmony_ci	.u.hs.DeviceRemovable[0]	= 0,
1348c2ecf20Sopenharmony_ci	.u.hs.DeviceRemovable[1]	= 0xff,
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/*
1388c2ecf20Sopenharmony_ci * These strings converted to UTF-16 must be smaller than
1398c2ecf20Sopenharmony_ci * our EP0 buffer.
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_cistatic const struct usb_string ast_vhub_str_array[] = {
1428c2ecf20Sopenharmony_ci	{
1438c2ecf20Sopenharmony_ci		.id = AST_VHUB_STR_SERIAL,
1448c2ecf20Sopenharmony_ci		.s = "00000000"
1458c2ecf20Sopenharmony_ci	},
1468c2ecf20Sopenharmony_ci	{
1478c2ecf20Sopenharmony_ci		.id = AST_VHUB_STR_PRODUCT,
1488c2ecf20Sopenharmony_ci		.s = "USB Virtual Hub"
1498c2ecf20Sopenharmony_ci	},
1508c2ecf20Sopenharmony_ci	{
1518c2ecf20Sopenharmony_ci		.id = AST_VHUB_STR_MANUF,
1528c2ecf20Sopenharmony_ci		.s = "Aspeed"
1538c2ecf20Sopenharmony_ci	},
1548c2ecf20Sopenharmony_ci	{ }
1558c2ecf20Sopenharmony_ci};
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic const struct usb_gadget_strings ast_vhub_strings = {
1588c2ecf20Sopenharmony_ci	.language = 0x0409,
1598c2ecf20Sopenharmony_ci	.strings = (struct usb_string *)ast_vhub_str_array
1608c2ecf20Sopenharmony_ci};
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int ast_vhub_hub_dev_status(struct ast_vhub_ep *ep,
1638c2ecf20Sopenharmony_ci				   u16 wIndex, u16 wValue)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	u8 st0;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	EPDBG(ep, "GET_STATUS(dev)\n");
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/*
1708c2ecf20Sopenharmony_ci	 * Mark it as self-powered, I doubt the BMC is powered off
1718c2ecf20Sopenharmony_ci	 * the USB bus ...
1728c2ecf20Sopenharmony_ci	 */
1738c2ecf20Sopenharmony_ci	st0 = 1 << USB_DEVICE_SELF_POWERED;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/*
1768c2ecf20Sopenharmony_ci	 * Need to double check how remote wakeup actually works
1778c2ecf20Sopenharmony_ci	 * on that chip and what triggers it.
1788c2ecf20Sopenharmony_ci	 */
1798c2ecf20Sopenharmony_ci	if (ep->vhub->wakeup_en)
1808c2ecf20Sopenharmony_ci		st0 |= 1 << USB_DEVICE_REMOTE_WAKEUP;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return ast_vhub_simple_reply(ep, st0, 0);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int ast_vhub_hub_ep_status(struct ast_vhub_ep *ep,
1868c2ecf20Sopenharmony_ci				  u16 wIndex, u16 wValue)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	int ep_num;
1898c2ecf20Sopenharmony_ci	u8 st0 = 0;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
1928c2ecf20Sopenharmony_ci	EPDBG(ep, "GET_STATUS(ep%d)\n", ep_num);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* On the hub we have only EP 0 and 1 */
1958c2ecf20Sopenharmony_ci	if (ep_num == 1) {
1968c2ecf20Sopenharmony_ci		if (ep->vhub->ep1_stalled)
1978c2ecf20Sopenharmony_ci			st0 |= 1 << USB_ENDPOINT_HALT;
1988c2ecf20Sopenharmony_ci	} else if (ep_num != 0)
1998c2ecf20Sopenharmony_ci		return std_req_stall;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return ast_vhub_simple_reply(ep, st0, 0);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int ast_vhub_hub_dev_feature(struct ast_vhub_ep *ep,
2058c2ecf20Sopenharmony_ci				    u16 wIndex, u16 wValue,
2068c2ecf20Sopenharmony_ci				    bool is_set)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	EPDBG(ep, "%s_FEATURE(dev val=%02x)\n",
2098c2ecf20Sopenharmony_ci	      is_set ? "SET" : "CLEAR", wValue);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (wValue != USB_DEVICE_REMOTE_WAKEUP)
2128c2ecf20Sopenharmony_ci		return std_req_stall;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	ep->vhub->wakeup_en = is_set;
2158c2ecf20Sopenharmony_ci	EPDBG(ep, "Hub remote wakeup %s\n",
2168c2ecf20Sopenharmony_ci	      is_set ? "enabled" : "disabled");
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return std_req_complete;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic int ast_vhub_hub_ep_feature(struct ast_vhub_ep *ep,
2228c2ecf20Sopenharmony_ci				   u16 wIndex, u16 wValue,
2238c2ecf20Sopenharmony_ci				   bool is_set)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	int ep_num;
2268c2ecf20Sopenharmony_ci	u32 reg;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
2298c2ecf20Sopenharmony_ci	EPDBG(ep, "%s_FEATURE(ep%d val=%02x)\n",
2308c2ecf20Sopenharmony_ci	      is_set ? "SET" : "CLEAR", ep_num, wValue);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (ep_num > 1)
2338c2ecf20Sopenharmony_ci		return std_req_stall;
2348c2ecf20Sopenharmony_ci	if (wValue != USB_ENDPOINT_HALT)
2358c2ecf20Sopenharmony_ci		return std_req_stall;
2368c2ecf20Sopenharmony_ci	if (ep_num == 0)
2378c2ecf20Sopenharmony_ci		return std_req_complete;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	EPDBG(ep, "%s stall on EP 1\n",
2408c2ecf20Sopenharmony_ci	      is_set ? "setting" : "clearing");
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	ep->vhub->ep1_stalled = is_set;
2438c2ecf20Sopenharmony_ci	reg = readl(ep->vhub->regs + AST_VHUB_EP1_CTRL);
2448c2ecf20Sopenharmony_ci	if (is_set) {
2458c2ecf20Sopenharmony_ci		reg |= VHUB_EP1_CTRL_STALL;
2468c2ecf20Sopenharmony_ci	} else {
2478c2ecf20Sopenharmony_ci		reg &= ~VHUB_EP1_CTRL_STALL;
2488c2ecf20Sopenharmony_ci		reg |= VHUB_EP1_CTRL_RESET_TOGGLE;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci	writel(reg, ep->vhub->regs + AST_VHUB_EP1_CTRL);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return std_req_complete;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
2568c2ecf20Sopenharmony_ci			     u8 desc_type, u16 len)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	size_t dsize;
2598c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	EPDBG(ep, "GET_DESCRIPTOR(type:%d)\n", desc_type);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/*
2648c2ecf20Sopenharmony_ci	 * Copy first to EP buffer and send from there, so
2658c2ecf20Sopenharmony_ci	 * we can do some in-place patching if needed. We know
2668c2ecf20Sopenharmony_ci	 * the EP buffer is big enough but ensure that doesn't
2678c2ecf20Sopenharmony_ci	 * change. We do that now rather than later after we
2688c2ecf20Sopenharmony_ci	 * have checked sizes etc... to avoid a gcc bug where
2698c2ecf20Sopenharmony_ci	 * it thinks len is constant and barfs about read
2708c2ecf20Sopenharmony_ci	 * overflows in memcpy.
2718c2ecf20Sopenharmony_ci	 */
2728c2ecf20Sopenharmony_ci	switch(desc_type) {
2738c2ecf20Sopenharmony_ci	case USB_DT_DEVICE:
2748c2ecf20Sopenharmony_ci		dsize = USB_DT_DEVICE_SIZE;
2758c2ecf20Sopenharmony_ci		memcpy(ep->buf, &vhub->vhub_dev_desc, dsize);
2768c2ecf20Sopenharmony_ci		BUILD_BUG_ON(dsize > sizeof(vhub->vhub_dev_desc));
2778c2ecf20Sopenharmony_ci		BUILD_BUG_ON(USB_DT_DEVICE_SIZE >= AST_VHUB_EP0_MAX_PACKET);
2788c2ecf20Sopenharmony_ci		break;
2798c2ecf20Sopenharmony_ci	case USB_DT_CONFIG:
2808c2ecf20Sopenharmony_ci		dsize = AST_VHUB_CONF_DESC_SIZE;
2818c2ecf20Sopenharmony_ci		memcpy(ep->buf, &vhub->vhub_conf_desc, dsize);
2828c2ecf20Sopenharmony_ci		BUILD_BUG_ON(dsize > sizeof(vhub->vhub_conf_desc));
2838c2ecf20Sopenharmony_ci		BUILD_BUG_ON(AST_VHUB_CONF_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
2848c2ecf20Sopenharmony_ci		break;
2858c2ecf20Sopenharmony_ci	case USB_DT_HUB:
2868c2ecf20Sopenharmony_ci		dsize = AST_VHUB_HUB_DESC_SIZE;
2878c2ecf20Sopenharmony_ci		memcpy(ep->buf, &vhub->vhub_hub_desc, dsize);
2888c2ecf20Sopenharmony_ci		BUILD_BUG_ON(dsize > sizeof(vhub->vhub_hub_desc));
2898c2ecf20Sopenharmony_ci		BUILD_BUG_ON(AST_VHUB_HUB_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
2908c2ecf20Sopenharmony_ci		break;
2918c2ecf20Sopenharmony_ci	default:
2928c2ecf20Sopenharmony_ci		return std_req_stall;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* Crop requested length */
2968c2ecf20Sopenharmony_ci	if (len > dsize)
2978c2ecf20Sopenharmony_ci		len = dsize;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* Shoot it from the EP buffer */
3008c2ecf20Sopenharmony_ci	return ast_vhub_reply(ep, NULL, len);
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic struct usb_gadget_strings*
3048c2ecf20Sopenharmony_ciast_vhub_str_of_container(struct usb_gadget_string_container *container)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	return (struct usb_gadget_strings *)container->stash;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int ast_vhub_collect_languages(struct ast_vhub *vhub, void *buf,
3108c2ecf20Sopenharmony_ci				      size_t size)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	int rc, hdr_len, nlangs, max_langs;
3138c2ecf20Sopenharmony_ci	struct usb_gadget_strings *lang_str;
3148c2ecf20Sopenharmony_ci	struct usb_gadget_string_container *container;
3158c2ecf20Sopenharmony_ci	struct usb_string_descriptor *sdesc = buf;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	nlangs = 0;
3188c2ecf20Sopenharmony_ci	hdr_len = sizeof(struct usb_descriptor_header);
3198c2ecf20Sopenharmony_ci	max_langs = (size - hdr_len) / sizeof(sdesc->wData[0]);
3208c2ecf20Sopenharmony_ci	list_for_each_entry(container, &vhub->vhub_str_desc, list) {
3218c2ecf20Sopenharmony_ci		if (nlangs >= max_langs)
3228c2ecf20Sopenharmony_ci			break;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		lang_str = ast_vhub_str_of_container(container);
3258c2ecf20Sopenharmony_ci		sdesc->wData[nlangs++] = cpu_to_le16(lang_str->language);
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	rc = hdr_len + nlangs * sizeof(sdesc->wData[0]);
3298c2ecf20Sopenharmony_ci	sdesc->bLength = rc;
3308c2ecf20Sopenharmony_ci	sdesc->bDescriptorType = USB_DT_STRING;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return rc;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic struct usb_gadget_strings *ast_vhub_lookup_string(struct ast_vhub *vhub,
3368c2ecf20Sopenharmony_ci							 u16 lang_id)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct usb_gadget_strings *lang_str;
3398c2ecf20Sopenharmony_ci	struct usb_gadget_string_container *container;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	list_for_each_entry(container, &vhub->vhub_str_desc, list) {
3428c2ecf20Sopenharmony_ci		lang_str = ast_vhub_str_of_container(container);
3438c2ecf20Sopenharmony_ci		if (lang_str->language == lang_id)
3448c2ecf20Sopenharmony_ci			return lang_str;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return NULL;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int ast_vhub_rep_string(struct ast_vhub_ep *ep,
3518c2ecf20Sopenharmony_ci			       u8 string_id, u16 lang_id,
3528c2ecf20Sopenharmony_ci			       u16 len)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	int rc;
3558c2ecf20Sopenharmony_ci	u8 buf[256];
3568c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
3578c2ecf20Sopenharmony_ci	struct usb_gadget_strings *lang_str;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (string_id == 0) {
3608c2ecf20Sopenharmony_ci		rc = ast_vhub_collect_languages(vhub, buf, sizeof(buf));
3618c2ecf20Sopenharmony_ci	} else {
3628c2ecf20Sopenharmony_ci		lang_str = ast_vhub_lookup_string(vhub, lang_id);
3638c2ecf20Sopenharmony_ci		if (!lang_str)
3648c2ecf20Sopenharmony_ci			return std_req_stall;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		rc = usb_gadget_get_string(lang_str, string_id, buf);
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (rc < 0 || rc >= AST_VHUB_EP0_MAX_PACKET)
3708c2ecf20Sopenharmony_ci		return std_req_stall;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* Shoot it from the EP buffer */
3738c2ecf20Sopenharmony_ci	memcpy(ep->buf, buf, rc);
3748c2ecf20Sopenharmony_ci	return ast_vhub_reply(ep, NULL, min_t(u16, rc, len));
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cienum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
3788c2ecf20Sopenharmony_ci					 struct usb_ctrlrequest *crq)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
3818c2ecf20Sopenharmony_ci	u16 wValue, wIndex, wLength;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	wValue = le16_to_cpu(crq->wValue);
3848c2ecf20Sopenharmony_ci	wIndex = le16_to_cpu(crq->wIndex);
3858c2ecf20Sopenharmony_ci	wLength = le16_to_cpu(crq->wLength);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/* First packet, grab speed */
3888c2ecf20Sopenharmony_ci	if (vhub->speed == USB_SPEED_UNKNOWN) {
3898c2ecf20Sopenharmony_ci		u32 ustat = readl(vhub->regs + AST_VHUB_USBSTS);
3908c2ecf20Sopenharmony_ci		if (ustat & VHUB_USBSTS_HISPEED)
3918c2ecf20Sopenharmony_ci			vhub->speed = USB_SPEED_HIGH;
3928c2ecf20Sopenharmony_ci		else
3938c2ecf20Sopenharmony_ci			vhub->speed = USB_SPEED_FULL;
3948c2ecf20Sopenharmony_ci		UDCDBG(vhub, "USB status=%08x speed=%s\n", ustat,
3958c2ecf20Sopenharmony_ci		       vhub->speed == USB_SPEED_HIGH ? "high" : "full");
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	switch ((crq->bRequestType << 8) | crq->bRequest) {
3998c2ecf20Sopenharmony_ci		/* SET_ADDRESS */
4008c2ecf20Sopenharmony_ci	case DeviceOutRequest | USB_REQ_SET_ADDRESS:
4018c2ecf20Sopenharmony_ci		EPDBG(ep, "SET_ADDRESS: Got address %x\n", wValue);
4028c2ecf20Sopenharmony_ci		writel(wValue, vhub->regs + AST_VHUB_CONF);
4038c2ecf20Sopenharmony_ci		return std_req_complete;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		/* GET_STATUS */
4068c2ecf20Sopenharmony_ci	case DeviceRequest | USB_REQ_GET_STATUS:
4078c2ecf20Sopenharmony_ci		return ast_vhub_hub_dev_status(ep, wIndex, wValue);
4088c2ecf20Sopenharmony_ci	case InterfaceRequest | USB_REQ_GET_STATUS:
4098c2ecf20Sopenharmony_ci		return ast_vhub_simple_reply(ep, 0, 0);
4108c2ecf20Sopenharmony_ci	case EndpointRequest | USB_REQ_GET_STATUS:
4118c2ecf20Sopenharmony_ci		return ast_vhub_hub_ep_status(ep, wIndex, wValue);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		/* SET/CLEAR_FEATURE */
4148c2ecf20Sopenharmony_ci	case DeviceOutRequest | USB_REQ_SET_FEATURE:
4158c2ecf20Sopenharmony_ci		return ast_vhub_hub_dev_feature(ep, wIndex, wValue, true);
4168c2ecf20Sopenharmony_ci	case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
4178c2ecf20Sopenharmony_ci		return ast_vhub_hub_dev_feature(ep, wIndex, wValue, false);
4188c2ecf20Sopenharmony_ci	case EndpointOutRequest | USB_REQ_SET_FEATURE:
4198c2ecf20Sopenharmony_ci		return ast_vhub_hub_ep_feature(ep, wIndex, wValue, true);
4208c2ecf20Sopenharmony_ci	case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
4218c2ecf20Sopenharmony_ci		return ast_vhub_hub_ep_feature(ep, wIndex, wValue, false);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci		/* GET/SET_CONFIGURATION */
4248c2ecf20Sopenharmony_ci	case DeviceRequest | USB_REQ_GET_CONFIGURATION:
4258c2ecf20Sopenharmony_ci		return ast_vhub_simple_reply(ep, 1);
4268c2ecf20Sopenharmony_ci	case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
4278c2ecf20Sopenharmony_ci		if (wValue != 1)
4288c2ecf20Sopenharmony_ci			return std_req_stall;
4298c2ecf20Sopenharmony_ci		return std_req_complete;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		/* GET_DESCRIPTOR */
4328c2ecf20Sopenharmony_ci	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
4338c2ecf20Sopenharmony_ci		switch (wValue >> 8) {
4348c2ecf20Sopenharmony_ci		case USB_DT_DEVICE:
4358c2ecf20Sopenharmony_ci		case USB_DT_CONFIG:
4368c2ecf20Sopenharmony_ci			return ast_vhub_rep_desc(ep, wValue >> 8,
4378c2ecf20Sopenharmony_ci						 wLength);
4388c2ecf20Sopenharmony_ci		case USB_DT_STRING:
4398c2ecf20Sopenharmony_ci			return ast_vhub_rep_string(ep, wValue & 0xff,
4408c2ecf20Sopenharmony_ci						   wIndex, wLength);
4418c2ecf20Sopenharmony_ci		}
4428c2ecf20Sopenharmony_ci		return std_req_stall;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		/* GET/SET_INTERFACE */
4458c2ecf20Sopenharmony_ci	case DeviceRequest | USB_REQ_GET_INTERFACE:
4468c2ecf20Sopenharmony_ci		return ast_vhub_simple_reply(ep, 0);
4478c2ecf20Sopenharmony_ci	case DeviceOutRequest | USB_REQ_SET_INTERFACE:
4488c2ecf20Sopenharmony_ci		if (wValue != 0 || wIndex != 0)
4498c2ecf20Sopenharmony_ci			return std_req_stall;
4508c2ecf20Sopenharmony_ci		return std_req_complete;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci	return std_req_stall;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic void ast_vhub_update_hub_ep1(struct ast_vhub *vhub,
4568c2ecf20Sopenharmony_ci				    unsigned int port)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	/* Update HW EP1 response */
4598c2ecf20Sopenharmony_ci	u32 reg = readl(vhub->regs + AST_VHUB_EP1_STS_CHG);
4608c2ecf20Sopenharmony_ci	u32 pmask = (1 << (port + 1));
4618c2ecf20Sopenharmony_ci	if (vhub->ports[port].change)
4628c2ecf20Sopenharmony_ci		reg |= pmask;
4638c2ecf20Sopenharmony_ci	else
4648c2ecf20Sopenharmony_ci		reg &= ~pmask;
4658c2ecf20Sopenharmony_ci	writel(reg, vhub->regs + AST_VHUB_EP1_STS_CHG);
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic void ast_vhub_change_port_stat(struct ast_vhub *vhub,
4698c2ecf20Sopenharmony_ci				      unsigned int port,
4708c2ecf20Sopenharmony_ci				      u16 clr_flags,
4718c2ecf20Sopenharmony_ci				      u16 set_flags,
4728c2ecf20Sopenharmony_ci				      bool set_c)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	struct ast_vhub_port *p = &vhub->ports[port];
4758c2ecf20Sopenharmony_ci	u16 prev;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	/* Update port status */
4788c2ecf20Sopenharmony_ci	prev = p->status;
4798c2ecf20Sopenharmony_ci	p->status = (prev & ~clr_flags) | set_flags;
4808c2ecf20Sopenharmony_ci	DDBG(&p->dev, "port %d status %04x -> %04x (C=%d)\n",
4818c2ecf20Sopenharmony_ci	     port + 1, prev, p->status, set_c);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* Update change bits if needed */
4848c2ecf20Sopenharmony_ci	if (set_c) {
4858c2ecf20Sopenharmony_ci		u16 chg = p->status ^ prev;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		/* Only these are relevant for change */
4888c2ecf20Sopenharmony_ci		chg &= USB_PORT_STAT_C_CONNECTION |
4898c2ecf20Sopenharmony_ci		       USB_PORT_STAT_C_ENABLE |
4908c2ecf20Sopenharmony_ci		       USB_PORT_STAT_C_SUSPEND |
4918c2ecf20Sopenharmony_ci		       USB_PORT_STAT_C_OVERCURRENT |
4928c2ecf20Sopenharmony_ci		       USB_PORT_STAT_C_RESET |
4938c2ecf20Sopenharmony_ci		       USB_PORT_STAT_C_L1;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		/*
4968c2ecf20Sopenharmony_ci		 * We only set USB_PORT_STAT_C_ENABLE if we are disabling
4978c2ecf20Sopenharmony_ci		 * the port as per USB spec, otherwise MacOS gets upset
4988c2ecf20Sopenharmony_ci		 */
4998c2ecf20Sopenharmony_ci		if (p->status & USB_PORT_STAT_ENABLE)
5008c2ecf20Sopenharmony_ci			chg &= ~USB_PORT_STAT_C_ENABLE;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		p->change = chg;
5038c2ecf20Sopenharmony_ci		ast_vhub_update_hub_ep1(vhub, port);
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void ast_vhub_send_host_wakeup(struct ast_vhub *vhub)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	u32 reg = readl(vhub->regs + AST_VHUB_CTRL);
5108c2ecf20Sopenharmony_ci	UDCDBG(vhub, "Waking up host !\n");
5118c2ecf20Sopenharmony_ci	reg |= VHUB_CTRL_MANUAL_REMOTE_WAKEUP;
5128c2ecf20Sopenharmony_ci	writel(reg, vhub->regs + AST_VHUB_CTRL);
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_civoid ast_vhub_device_connect(struct ast_vhub *vhub,
5168c2ecf20Sopenharmony_ci			     unsigned int port, bool on)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	if (on)
5198c2ecf20Sopenharmony_ci		ast_vhub_change_port_stat(vhub, port, 0,
5208c2ecf20Sopenharmony_ci					  USB_PORT_STAT_CONNECTION, true);
5218c2ecf20Sopenharmony_ci	else
5228c2ecf20Sopenharmony_ci		ast_vhub_change_port_stat(vhub, port,
5238c2ecf20Sopenharmony_ci					  USB_PORT_STAT_CONNECTION |
5248c2ecf20Sopenharmony_ci					  USB_PORT_STAT_ENABLE,
5258c2ecf20Sopenharmony_ci					  0, true);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	/*
5288c2ecf20Sopenharmony_ci	 * If the hub is set to wakup the host on connection events
5298c2ecf20Sopenharmony_ci	 * then send a wakeup.
5308c2ecf20Sopenharmony_ci	 */
5318c2ecf20Sopenharmony_ci	if (vhub->wakeup_en)
5328c2ecf20Sopenharmony_ci		ast_vhub_send_host_wakeup(vhub);
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic void ast_vhub_wake_work(struct work_struct *work)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = container_of(work,
5388c2ecf20Sopenharmony_ci					     struct ast_vhub,
5398c2ecf20Sopenharmony_ci					     wake_work);
5408c2ecf20Sopenharmony_ci	unsigned long flags;
5418c2ecf20Sopenharmony_ci	unsigned int i;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/*
5448c2ecf20Sopenharmony_ci	 * Wake all sleeping ports. If a port is suspended by
5458c2ecf20Sopenharmony_ci	 * the host suspend (without explicit state suspend),
5468c2ecf20Sopenharmony_ci	 * we let the normal host wake path deal with it later.
5478c2ecf20Sopenharmony_ci	 */
5488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vhub->lock, flags);
5498c2ecf20Sopenharmony_ci	for (i = 0; i < vhub->max_ports; i++) {
5508c2ecf20Sopenharmony_ci		struct ast_vhub_port *p = &vhub->ports[i];
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		if (!(p->status & USB_PORT_STAT_SUSPEND))
5538c2ecf20Sopenharmony_ci			continue;
5548c2ecf20Sopenharmony_ci		ast_vhub_change_port_stat(vhub, i,
5558c2ecf20Sopenharmony_ci					  USB_PORT_STAT_SUSPEND,
5568c2ecf20Sopenharmony_ci					  0, true);
5578c2ecf20Sopenharmony_ci		ast_vhub_dev_resume(&p->dev);
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci	ast_vhub_send_host_wakeup(vhub);
5608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vhub->lock, flags);
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_civoid ast_vhub_hub_wake_all(struct ast_vhub *vhub)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	/*
5668c2ecf20Sopenharmony_ci	 * A device is trying to wake the world, because this
5678c2ecf20Sopenharmony_ci	 * can recurse into the device, we break the call chain
5688c2ecf20Sopenharmony_ci	 * using a work queue
5698c2ecf20Sopenharmony_ci	 */
5708c2ecf20Sopenharmony_ci	schedule_work(&vhub->wake_work);
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic void ast_vhub_port_reset(struct ast_vhub *vhub, u8 port)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	struct ast_vhub_port *p = &vhub->ports[port];
5768c2ecf20Sopenharmony_ci	u16 set, clr, speed;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/* First mark disabled */
5798c2ecf20Sopenharmony_ci	ast_vhub_change_port_stat(vhub, port,
5808c2ecf20Sopenharmony_ci				  USB_PORT_STAT_ENABLE |
5818c2ecf20Sopenharmony_ci				  USB_PORT_STAT_SUSPEND,
5828c2ecf20Sopenharmony_ci				  USB_PORT_STAT_RESET,
5838c2ecf20Sopenharmony_ci				  false);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (!p->dev.driver)
5868c2ecf20Sopenharmony_ci		return;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	/*
5898c2ecf20Sopenharmony_ci	 * This will either "start" the port or reset the
5908c2ecf20Sopenharmony_ci	 * device if already started...
5918c2ecf20Sopenharmony_ci	 */
5928c2ecf20Sopenharmony_ci	ast_vhub_dev_reset(&p->dev);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	/* Grab the right speed */
5958c2ecf20Sopenharmony_ci	speed = p->dev.driver->max_speed;
5968c2ecf20Sopenharmony_ci	if (speed == USB_SPEED_UNKNOWN || speed > vhub->speed)
5978c2ecf20Sopenharmony_ci		speed = vhub->speed;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	switch (speed) {
6008c2ecf20Sopenharmony_ci	case USB_SPEED_LOW:
6018c2ecf20Sopenharmony_ci		set = USB_PORT_STAT_LOW_SPEED;
6028c2ecf20Sopenharmony_ci		clr = USB_PORT_STAT_HIGH_SPEED;
6038c2ecf20Sopenharmony_ci		break;
6048c2ecf20Sopenharmony_ci	case USB_SPEED_FULL:
6058c2ecf20Sopenharmony_ci		set = 0;
6068c2ecf20Sopenharmony_ci		clr = USB_PORT_STAT_LOW_SPEED |
6078c2ecf20Sopenharmony_ci			USB_PORT_STAT_HIGH_SPEED;
6088c2ecf20Sopenharmony_ci		break;
6098c2ecf20Sopenharmony_ci	case USB_SPEED_HIGH:
6108c2ecf20Sopenharmony_ci		set = USB_PORT_STAT_HIGH_SPEED;
6118c2ecf20Sopenharmony_ci		clr = USB_PORT_STAT_LOW_SPEED;
6128c2ecf20Sopenharmony_ci		break;
6138c2ecf20Sopenharmony_ci	default:
6148c2ecf20Sopenharmony_ci		UDCDBG(vhub, "Unsupported speed %d when"
6158c2ecf20Sopenharmony_ci		       " connecting device\n",
6168c2ecf20Sopenharmony_ci		       speed);
6178c2ecf20Sopenharmony_ci		return;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci	clr |= USB_PORT_STAT_RESET;
6208c2ecf20Sopenharmony_ci	set |= USB_PORT_STAT_ENABLE;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	/* This should ideally be delayed ... */
6238c2ecf20Sopenharmony_ci	ast_vhub_change_port_stat(vhub, port, clr, set, true);
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic enum std_req_rc ast_vhub_set_port_feature(struct ast_vhub_ep *ep,
6278c2ecf20Sopenharmony_ci						 u8 port, u16 feat)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
6308c2ecf20Sopenharmony_ci	struct ast_vhub_port *p;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (port == 0 || port > vhub->max_ports)
6338c2ecf20Sopenharmony_ci		return std_req_stall;
6348c2ecf20Sopenharmony_ci	port--;
6358c2ecf20Sopenharmony_ci	p = &vhub->ports[port];
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	switch(feat) {
6388c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_SUSPEND:
6398c2ecf20Sopenharmony_ci		if (!(p->status & USB_PORT_STAT_ENABLE))
6408c2ecf20Sopenharmony_ci			return std_req_complete;
6418c2ecf20Sopenharmony_ci		ast_vhub_change_port_stat(vhub, port,
6428c2ecf20Sopenharmony_ci					  0, USB_PORT_STAT_SUSPEND,
6438c2ecf20Sopenharmony_ci					  false);
6448c2ecf20Sopenharmony_ci		ast_vhub_dev_suspend(&p->dev);
6458c2ecf20Sopenharmony_ci		return std_req_complete;
6468c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_RESET:
6478c2ecf20Sopenharmony_ci		EPDBG(ep, "Port reset !\n");
6488c2ecf20Sopenharmony_ci		ast_vhub_port_reset(vhub, port);
6498c2ecf20Sopenharmony_ci		return std_req_complete;
6508c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_POWER:
6518c2ecf20Sopenharmony_ci		/*
6528c2ecf20Sopenharmony_ci		 * On Power-on, we mark the connected flag changed,
6538c2ecf20Sopenharmony_ci		 * if there's a connected device, some hosts will
6548c2ecf20Sopenharmony_ci		 * otherwise fail to detect it.
6558c2ecf20Sopenharmony_ci		 */
6568c2ecf20Sopenharmony_ci		if (p->status & USB_PORT_STAT_CONNECTION) {
6578c2ecf20Sopenharmony_ci			p->change |= USB_PORT_STAT_C_CONNECTION;
6588c2ecf20Sopenharmony_ci			ast_vhub_update_hub_ep1(vhub, port);
6598c2ecf20Sopenharmony_ci		}
6608c2ecf20Sopenharmony_ci		return std_req_complete;
6618c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_TEST:
6628c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_INDICATOR:
6638c2ecf20Sopenharmony_ci		/* We don't do anything with these */
6648c2ecf20Sopenharmony_ci		return std_req_complete;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci	return std_req_stall;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic enum std_req_rc ast_vhub_clr_port_feature(struct ast_vhub_ep *ep,
6708c2ecf20Sopenharmony_ci						 u8 port, u16 feat)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
6738c2ecf20Sopenharmony_ci	struct ast_vhub_port *p;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (port == 0 || port > vhub->max_ports)
6768c2ecf20Sopenharmony_ci		return std_req_stall;
6778c2ecf20Sopenharmony_ci	port--;
6788c2ecf20Sopenharmony_ci	p = &vhub->ports[port];
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	switch(feat) {
6818c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_ENABLE:
6828c2ecf20Sopenharmony_ci		ast_vhub_change_port_stat(vhub, port,
6838c2ecf20Sopenharmony_ci					  USB_PORT_STAT_ENABLE |
6848c2ecf20Sopenharmony_ci					  USB_PORT_STAT_SUSPEND, 0,
6858c2ecf20Sopenharmony_ci					  false);
6868c2ecf20Sopenharmony_ci		ast_vhub_dev_suspend(&p->dev);
6878c2ecf20Sopenharmony_ci		return std_req_complete;
6888c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_SUSPEND:
6898c2ecf20Sopenharmony_ci		if (!(p->status & USB_PORT_STAT_SUSPEND))
6908c2ecf20Sopenharmony_ci			return std_req_complete;
6918c2ecf20Sopenharmony_ci		ast_vhub_change_port_stat(vhub, port,
6928c2ecf20Sopenharmony_ci					  USB_PORT_STAT_SUSPEND, 0,
6938c2ecf20Sopenharmony_ci					  false);
6948c2ecf20Sopenharmony_ci		ast_vhub_dev_resume(&p->dev);
6958c2ecf20Sopenharmony_ci		return std_req_complete;
6968c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_POWER:
6978c2ecf20Sopenharmony_ci		/* We don't do power control */
6988c2ecf20Sopenharmony_ci		return std_req_complete;
6998c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_INDICATOR:
7008c2ecf20Sopenharmony_ci		/* We don't have indicators */
7018c2ecf20Sopenharmony_ci		return std_req_complete;
7028c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_C_CONNECTION:
7038c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_C_ENABLE:
7048c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_C_SUSPEND:
7058c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_C_OVER_CURRENT:
7068c2ecf20Sopenharmony_ci	case USB_PORT_FEAT_C_RESET:
7078c2ecf20Sopenharmony_ci		/* Clear state-change feature */
7088c2ecf20Sopenharmony_ci		p->change &= ~(1u << (feat - 16));
7098c2ecf20Sopenharmony_ci		ast_vhub_update_hub_ep1(vhub, port);
7108c2ecf20Sopenharmony_ci		return std_req_complete;
7118c2ecf20Sopenharmony_ci	}
7128c2ecf20Sopenharmony_ci	return std_req_stall;
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic enum std_req_rc ast_vhub_get_port_stat(struct ast_vhub_ep *ep,
7168c2ecf20Sopenharmony_ci					      u8 port)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
7198c2ecf20Sopenharmony_ci	u16 stat, chg;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	if (port == 0 || port > vhub->max_ports)
7228c2ecf20Sopenharmony_ci		return std_req_stall;
7238c2ecf20Sopenharmony_ci	port--;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	stat = vhub->ports[port].status;
7268c2ecf20Sopenharmony_ci	chg = vhub->ports[port].change;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/* We always have power */
7298c2ecf20Sopenharmony_ci	stat |= USB_PORT_STAT_POWER;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	EPDBG(ep, " port status=%04x change=%04x\n", stat, chg);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	return ast_vhub_simple_reply(ep,
7348c2ecf20Sopenharmony_ci				     stat & 0xff,
7358c2ecf20Sopenharmony_ci				     stat >> 8,
7368c2ecf20Sopenharmony_ci				     chg & 0xff,
7378c2ecf20Sopenharmony_ci				     chg >> 8);
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cienum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
7418c2ecf20Sopenharmony_ci					   struct usb_ctrlrequest *crq)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	u16 wValue, wIndex, wLength;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	wValue = le16_to_cpu(crq->wValue);
7468c2ecf20Sopenharmony_ci	wIndex = le16_to_cpu(crq->wIndex);
7478c2ecf20Sopenharmony_ci	wLength = le16_to_cpu(crq->wLength);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	switch ((crq->bRequestType << 8) | crq->bRequest) {
7508c2ecf20Sopenharmony_ci	case GetHubStatus:
7518c2ecf20Sopenharmony_ci		EPDBG(ep, "GetHubStatus\n");
7528c2ecf20Sopenharmony_ci		return ast_vhub_simple_reply(ep, 0, 0, 0, 0);
7538c2ecf20Sopenharmony_ci	case GetPortStatus:
7548c2ecf20Sopenharmony_ci		EPDBG(ep, "GetPortStatus(%d)\n", wIndex & 0xff);
7558c2ecf20Sopenharmony_ci		return ast_vhub_get_port_stat(ep, wIndex & 0xf);
7568c2ecf20Sopenharmony_ci	case GetHubDescriptor:
7578c2ecf20Sopenharmony_ci		if (wValue != (USB_DT_HUB << 8))
7588c2ecf20Sopenharmony_ci			return std_req_stall;
7598c2ecf20Sopenharmony_ci		EPDBG(ep, "GetHubDescriptor(%d)\n", wIndex & 0xff);
7608c2ecf20Sopenharmony_ci		return ast_vhub_rep_desc(ep, USB_DT_HUB, wLength);
7618c2ecf20Sopenharmony_ci	case SetHubFeature:
7628c2ecf20Sopenharmony_ci	case ClearHubFeature:
7638c2ecf20Sopenharmony_ci		EPDBG(ep, "Get/SetHubFeature(%d)\n", wValue);
7648c2ecf20Sopenharmony_ci		/* No feature, just complete the requests */
7658c2ecf20Sopenharmony_ci		if (wValue == C_HUB_LOCAL_POWER ||
7668c2ecf20Sopenharmony_ci		    wValue == C_HUB_OVER_CURRENT)
7678c2ecf20Sopenharmony_ci			return std_req_complete;
7688c2ecf20Sopenharmony_ci		return std_req_stall;
7698c2ecf20Sopenharmony_ci	case SetPortFeature:
7708c2ecf20Sopenharmony_ci		EPDBG(ep, "SetPortFeature(%d,%d)\n", wIndex & 0xf, wValue);
7718c2ecf20Sopenharmony_ci		return ast_vhub_set_port_feature(ep, wIndex & 0xf, wValue);
7728c2ecf20Sopenharmony_ci	case ClearPortFeature:
7738c2ecf20Sopenharmony_ci		EPDBG(ep, "ClearPortFeature(%d,%d)\n", wIndex & 0xf, wValue);
7748c2ecf20Sopenharmony_ci		return ast_vhub_clr_port_feature(ep, wIndex & 0xf, wValue);
7758c2ecf20Sopenharmony_ci	case ClearTTBuffer:
7768c2ecf20Sopenharmony_ci	case ResetTT:
7778c2ecf20Sopenharmony_ci	case StopTT:
7788c2ecf20Sopenharmony_ci		return std_req_complete;
7798c2ecf20Sopenharmony_ci	case GetTTState:
7808c2ecf20Sopenharmony_ci		return ast_vhub_simple_reply(ep, 0, 0, 0, 0);
7818c2ecf20Sopenharmony_ci	default:
7828c2ecf20Sopenharmony_ci		EPDBG(ep, "Unknown class request\n");
7838c2ecf20Sopenharmony_ci	}
7848c2ecf20Sopenharmony_ci	return std_req_stall;
7858c2ecf20Sopenharmony_ci}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_civoid ast_vhub_hub_suspend(struct ast_vhub *vhub)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci	unsigned int i;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	UDCDBG(vhub, "USB bus suspend\n");
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	if (vhub->suspended)
7948c2ecf20Sopenharmony_ci		return;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	vhub->suspended = true;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	/*
7998c2ecf20Sopenharmony_ci	 * Forward to unsuspended ports without changing
8008c2ecf20Sopenharmony_ci	 * their connection status.
8018c2ecf20Sopenharmony_ci	 */
8028c2ecf20Sopenharmony_ci	for (i = 0; i < vhub->max_ports; i++) {
8038c2ecf20Sopenharmony_ci		struct ast_vhub_port *p = &vhub->ports[i];
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci		if (!(p->status & USB_PORT_STAT_SUSPEND))
8068c2ecf20Sopenharmony_ci			ast_vhub_dev_suspend(&p->dev);
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_civoid ast_vhub_hub_resume(struct ast_vhub *vhub)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	unsigned int i;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	UDCDBG(vhub, "USB bus resume\n");
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	if (!vhub->suspended)
8178c2ecf20Sopenharmony_ci		return;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	vhub->suspended = false;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	/*
8228c2ecf20Sopenharmony_ci	 * Forward to unsuspended ports without changing
8238c2ecf20Sopenharmony_ci	 * their connection status.
8248c2ecf20Sopenharmony_ci	 */
8258c2ecf20Sopenharmony_ci	for (i = 0; i < vhub->max_ports; i++) {
8268c2ecf20Sopenharmony_ci		struct ast_vhub_port *p = &vhub->ports[i];
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci		if (!(p->status & USB_PORT_STAT_SUSPEND))
8298c2ecf20Sopenharmony_ci			ast_vhub_dev_resume(&p->dev);
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_civoid ast_vhub_hub_reset(struct ast_vhub *vhub)
8348c2ecf20Sopenharmony_ci{
8358c2ecf20Sopenharmony_ci	unsigned int i;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	UDCDBG(vhub, "USB bus reset\n");
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	/*
8408c2ecf20Sopenharmony_ci	 * Is the speed known ? If not we don't care, we aren't
8418c2ecf20Sopenharmony_ci	 * initialized yet and ports haven't been enabled.
8428c2ecf20Sopenharmony_ci	 */
8438c2ecf20Sopenharmony_ci	if (vhub->speed == USB_SPEED_UNKNOWN)
8448c2ecf20Sopenharmony_ci		return;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	/* We aren't suspended anymore obviously */
8478c2ecf20Sopenharmony_ci	vhub->suspended = false;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	/* No speed set */
8508c2ecf20Sopenharmony_ci	vhub->speed = USB_SPEED_UNKNOWN;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	/* Wakeup not enabled anymore */
8538c2ecf20Sopenharmony_ci	vhub->wakeup_en = false;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	/*
8568c2ecf20Sopenharmony_ci	 * Clear all port status, disable gadgets and "suspend"
8578c2ecf20Sopenharmony_ci	 * them. They will be woken up by a port reset.
8588c2ecf20Sopenharmony_ci	 */
8598c2ecf20Sopenharmony_ci	for (i = 0; i < vhub->max_ports; i++) {
8608c2ecf20Sopenharmony_ci		struct ast_vhub_port *p = &vhub->ports[i];
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci		/* Only keep the connected flag */
8638c2ecf20Sopenharmony_ci		p->status &= USB_PORT_STAT_CONNECTION;
8648c2ecf20Sopenharmony_ci		p->change = 0;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci		/* Suspend the gadget if any */
8678c2ecf20Sopenharmony_ci		ast_vhub_dev_suspend(&p->dev);
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/* Cleanup HW */
8718c2ecf20Sopenharmony_ci	writel(0, vhub->regs + AST_VHUB_CONF);
8728c2ecf20Sopenharmony_ci	writel(0, vhub->regs + AST_VHUB_EP0_CTRL);
8738c2ecf20Sopenharmony_ci	writel(VHUB_EP1_CTRL_RESET_TOGGLE |
8748c2ecf20Sopenharmony_ci	       VHUB_EP1_CTRL_ENABLE,
8758c2ecf20Sopenharmony_ci	       vhub->regs + AST_VHUB_EP1_CTRL);
8768c2ecf20Sopenharmony_ci	writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_cistatic void ast_vhub_of_parse_dev_desc(struct ast_vhub *vhub,
8808c2ecf20Sopenharmony_ci				       const struct device_node *vhub_np)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	u16 id;
8838c2ecf20Sopenharmony_ci	u32 data;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	if (!of_property_read_u32(vhub_np, "vhub-vendor-id", &data)) {
8868c2ecf20Sopenharmony_ci		id = (u16)data;
8878c2ecf20Sopenharmony_ci		vhub->vhub_dev_desc.idVendor = cpu_to_le16(id);
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci	if (!of_property_read_u32(vhub_np, "vhub-product-id", &data)) {
8908c2ecf20Sopenharmony_ci		id = (u16)data;
8918c2ecf20Sopenharmony_ci		vhub->vhub_dev_desc.idProduct = cpu_to_le16(id);
8928c2ecf20Sopenharmony_ci	}
8938c2ecf20Sopenharmony_ci	if (!of_property_read_u32(vhub_np, "vhub-device-revision", &data)) {
8948c2ecf20Sopenharmony_ci		id = (u16)data;
8958c2ecf20Sopenharmony_ci		vhub->vhub_dev_desc.bcdDevice = cpu_to_le16(id);
8968c2ecf20Sopenharmony_ci	}
8978c2ecf20Sopenharmony_ci}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_cistatic void ast_vhub_fixup_usb1_dev_desc(struct ast_vhub *vhub)
9008c2ecf20Sopenharmony_ci{
9018c2ecf20Sopenharmony_ci	vhub->vhub_dev_desc.bcdUSB = cpu_to_le16(0x0100);
9028c2ecf20Sopenharmony_ci	vhub->vhub_dev_desc.bDeviceProtocol = 0;
9038c2ecf20Sopenharmony_ci}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_cistatic struct usb_gadget_string_container*
9068c2ecf20Sopenharmony_ciast_vhub_str_container_alloc(struct ast_vhub *vhub)
9078c2ecf20Sopenharmony_ci{
9088c2ecf20Sopenharmony_ci	unsigned int size;
9098c2ecf20Sopenharmony_ci	struct usb_string *str_array;
9108c2ecf20Sopenharmony_ci	struct usb_gadget_strings *lang_str;
9118c2ecf20Sopenharmony_ci	struct usb_gadget_string_container *container;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	size = sizeof(*container);
9148c2ecf20Sopenharmony_ci	size += sizeof(struct usb_gadget_strings);
9158c2ecf20Sopenharmony_ci	size += sizeof(struct usb_string) * AST_VHUB_STR_INDEX_MAX;
9168c2ecf20Sopenharmony_ci	container = devm_kzalloc(&vhub->pdev->dev, size, GFP_KERNEL);
9178c2ecf20Sopenharmony_ci	if (!container)
9188c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	lang_str = ast_vhub_str_of_container(container);
9218c2ecf20Sopenharmony_ci	str_array = (struct usb_string *)(lang_str + 1);
9228c2ecf20Sopenharmony_ci	lang_str->strings = str_array;
9238c2ecf20Sopenharmony_ci	return container;
9248c2ecf20Sopenharmony_ci}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_cistatic void ast_vhub_str_deep_copy(struct usb_gadget_strings *dest,
9278c2ecf20Sopenharmony_ci				   const struct usb_gadget_strings *src)
9288c2ecf20Sopenharmony_ci{
9298c2ecf20Sopenharmony_ci	struct usb_string *src_array = src->strings;
9308c2ecf20Sopenharmony_ci	struct usb_string *dest_array = dest->strings;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	dest->language = src->language;
9338c2ecf20Sopenharmony_ci	if (src_array && dest_array) {
9348c2ecf20Sopenharmony_ci		do {
9358c2ecf20Sopenharmony_ci			*dest_array = *src_array;
9368c2ecf20Sopenharmony_ci			dest_array++;
9378c2ecf20Sopenharmony_ci			src_array++;
9388c2ecf20Sopenharmony_ci		} while (src_array->s);
9398c2ecf20Sopenharmony_ci	}
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cistatic int ast_vhub_str_alloc_add(struct ast_vhub *vhub,
9438c2ecf20Sopenharmony_ci				  const struct usb_gadget_strings *src_str)
9448c2ecf20Sopenharmony_ci{
9458c2ecf20Sopenharmony_ci	struct usb_gadget_strings *dest_str;
9468c2ecf20Sopenharmony_ci	struct usb_gadget_string_container *container;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	container = ast_vhub_str_container_alloc(vhub);
9498c2ecf20Sopenharmony_ci	if (IS_ERR(container))
9508c2ecf20Sopenharmony_ci		return PTR_ERR(container);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	dest_str = ast_vhub_str_of_container(container);
9538c2ecf20Sopenharmony_ci	ast_vhub_str_deep_copy(dest_str, src_str);
9548c2ecf20Sopenharmony_ci	list_add_tail(&container->list, &vhub->vhub_str_desc);
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	return 0;
9578c2ecf20Sopenharmony_ci}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_cistatic const struct {
9608c2ecf20Sopenharmony_ci	const char *name;
9618c2ecf20Sopenharmony_ci	u8 id;
9628c2ecf20Sopenharmony_ci} str_id_map[] = {
9638c2ecf20Sopenharmony_ci	{"manufacturer",	AST_VHUB_STR_MANUF},
9648c2ecf20Sopenharmony_ci	{"product",		AST_VHUB_STR_PRODUCT},
9658c2ecf20Sopenharmony_ci	{"serial-number",	AST_VHUB_STR_SERIAL},
9668c2ecf20Sopenharmony_ci	{},
9678c2ecf20Sopenharmony_ci};
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_cistatic int ast_vhub_of_parse_str_desc(struct ast_vhub *vhub,
9708c2ecf20Sopenharmony_ci				      const struct device_node *desc_np)
9718c2ecf20Sopenharmony_ci{
9728c2ecf20Sopenharmony_ci	u32 langid;
9738c2ecf20Sopenharmony_ci	int ret = 0;
9748c2ecf20Sopenharmony_ci	int i, offset;
9758c2ecf20Sopenharmony_ci	const char *str;
9768c2ecf20Sopenharmony_ci	struct device_node *child;
9778c2ecf20Sopenharmony_ci	struct usb_string str_array[AST_VHUB_STR_INDEX_MAX];
9788c2ecf20Sopenharmony_ci	struct usb_gadget_strings lang_str = {
9798c2ecf20Sopenharmony_ci		.strings = (struct usb_string *)str_array,
9808c2ecf20Sopenharmony_ci	};
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	for_each_child_of_node(desc_np, child) {
9838c2ecf20Sopenharmony_ci		if (of_property_read_u32(child, "reg", &langid))
9848c2ecf20Sopenharmony_ci			continue; /* no language identifier specified */
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci		if (!usb_validate_langid(langid))
9878c2ecf20Sopenharmony_ci			continue; /* invalid language identifier */
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci		lang_str.language = langid;
9908c2ecf20Sopenharmony_ci		for (i = offset = 0; str_id_map[i].name; i++) {
9918c2ecf20Sopenharmony_ci			str = of_get_property(child, str_id_map[i].name, NULL);
9928c2ecf20Sopenharmony_ci			if (str) {
9938c2ecf20Sopenharmony_ci				str_array[offset].s = str;
9948c2ecf20Sopenharmony_ci				str_array[offset].id = str_id_map[i].id;
9958c2ecf20Sopenharmony_ci				offset++;
9968c2ecf20Sopenharmony_ci			}
9978c2ecf20Sopenharmony_ci		}
9988c2ecf20Sopenharmony_ci		str_array[offset].id = 0;
9998c2ecf20Sopenharmony_ci		str_array[offset].s = NULL;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci		ret = ast_vhub_str_alloc_add(vhub, &lang_str);
10028c2ecf20Sopenharmony_ci		if (ret) {
10038c2ecf20Sopenharmony_ci			of_node_put(child);
10048c2ecf20Sopenharmony_ci			break;
10058c2ecf20Sopenharmony_ci		}
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	return ret;
10098c2ecf20Sopenharmony_ci}
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_cistatic int ast_vhub_init_desc(struct ast_vhub *vhub)
10128c2ecf20Sopenharmony_ci{
10138c2ecf20Sopenharmony_ci	int ret;
10148c2ecf20Sopenharmony_ci	struct device_node *desc_np;
10158c2ecf20Sopenharmony_ci	const struct device_node *vhub_np = vhub->pdev->dev.of_node;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/* Initialize vhub Device Descriptor. */
10188c2ecf20Sopenharmony_ci	memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc,
10198c2ecf20Sopenharmony_ci		sizeof(vhub->vhub_dev_desc));
10208c2ecf20Sopenharmony_ci	ast_vhub_of_parse_dev_desc(vhub, vhub_np);
10218c2ecf20Sopenharmony_ci	if (vhub->force_usb1)
10228c2ecf20Sopenharmony_ci		ast_vhub_fixup_usb1_dev_desc(vhub);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	/* Initialize vhub Configuration Descriptor. */
10258c2ecf20Sopenharmony_ci	memcpy(&vhub->vhub_conf_desc, &ast_vhub_conf_desc,
10268c2ecf20Sopenharmony_ci		sizeof(vhub->vhub_conf_desc));
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	/* Initialize vhub Hub Descriptor. */
10298c2ecf20Sopenharmony_ci	memcpy(&vhub->vhub_hub_desc, &ast_vhub_hub_desc,
10308c2ecf20Sopenharmony_ci		sizeof(vhub->vhub_hub_desc));
10318c2ecf20Sopenharmony_ci	vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	/* Initialize vhub String Descriptors. */
10348c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&vhub->vhub_str_desc);
10358c2ecf20Sopenharmony_ci	desc_np = of_get_child_by_name(vhub_np, "vhub-strings");
10368c2ecf20Sopenharmony_ci	if (desc_np) {
10378c2ecf20Sopenharmony_ci		ret = ast_vhub_of_parse_str_desc(vhub, desc_np);
10388c2ecf20Sopenharmony_ci		of_node_put(desc_np);
10398c2ecf20Sopenharmony_ci	}
10408c2ecf20Sopenharmony_ci	else
10418c2ecf20Sopenharmony_ci		ret = ast_vhub_str_alloc_add(vhub, &ast_vhub_strings);
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	return ret;
10448c2ecf20Sopenharmony_ci}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ciint ast_vhub_init_hub(struct ast_vhub *vhub)
10478c2ecf20Sopenharmony_ci{
10488c2ecf20Sopenharmony_ci	vhub->speed = USB_SPEED_UNKNOWN;
10498c2ecf20Sopenharmony_ci	INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	return ast_vhub_init_desc(vhub);
10528c2ecf20Sopenharmony_ci}
1053