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