162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * hub.c - virtual hub handling 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright 2017 IBM Corporation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/ioport.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/list.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/proc_fs.h> 2062306a36Sopenharmony_ci#include <linux/prefetch.h> 2162306a36Sopenharmony_ci#include <linux/clk.h> 2262306a36Sopenharmony_ci#include <linux/usb/gadget.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/regmap.h> 2562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2662306a36Sopenharmony_ci#include <linux/bcd.h> 2762306a36Sopenharmony_ci#include <linux/version.h> 2862306a36Sopenharmony_ci#include <linux/usb.h> 2962306a36Sopenharmony_ci#include <linux/usb/hcd.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "vhub.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* usb 2.0 hub device descriptor 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * A few things we may want to improve here: 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * - We may need to indicate TT support 3862306a36Sopenharmony_ci * - We may need a device qualifier descriptor 3962306a36Sopenharmony_ci * as devices can pretend to be usb1 or 2 4062306a36Sopenharmony_ci * - Make vid/did overridable 4162306a36Sopenharmony_ci * - make it look like usb1 if usb1 mode forced 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci#define KERNEL_REL bin2bcd(LINUX_VERSION_MAJOR) 4462306a36Sopenharmony_ci#define KERNEL_VER bin2bcd(LINUX_VERSION_PATCHLEVEL) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cienum { 4762306a36Sopenharmony_ci AST_VHUB_STR_INDEX_MAX = 4, 4862306a36Sopenharmony_ci AST_VHUB_STR_MANUF = 3, 4962306a36Sopenharmony_ci AST_VHUB_STR_PRODUCT = 2, 5062306a36Sopenharmony_ci AST_VHUB_STR_SERIAL = 1, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic const struct usb_device_descriptor ast_vhub_dev_desc = { 5462306a36Sopenharmony_ci .bLength = USB_DT_DEVICE_SIZE, 5562306a36Sopenharmony_ci .bDescriptorType = USB_DT_DEVICE, 5662306a36Sopenharmony_ci .bcdUSB = cpu_to_le16(0x0200), 5762306a36Sopenharmony_ci .bDeviceClass = USB_CLASS_HUB, 5862306a36Sopenharmony_ci .bDeviceSubClass = 0, 5962306a36Sopenharmony_ci .bDeviceProtocol = 1, 6062306a36Sopenharmony_ci .bMaxPacketSize0 = 64, 6162306a36Sopenharmony_ci .idVendor = cpu_to_le16(0x1d6b), 6262306a36Sopenharmony_ci .idProduct = cpu_to_le16(0x0107), 6362306a36Sopenharmony_ci .bcdDevice = cpu_to_le16(0x0100), 6462306a36Sopenharmony_ci .iManufacturer = AST_VHUB_STR_MANUF, 6562306a36Sopenharmony_ci .iProduct = AST_VHUB_STR_PRODUCT, 6662306a36Sopenharmony_ci .iSerialNumber = AST_VHUB_STR_SERIAL, 6762306a36Sopenharmony_ci .bNumConfigurations = 1, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic const struct usb_qualifier_descriptor ast_vhub_qual_desc = { 7162306a36Sopenharmony_ci .bLength = 0xA, 7262306a36Sopenharmony_ci .bDescriptorType = USB_DT_DEVICE_QUALIFIER, 7362306a36Sopenharmony_ci .bcdUSB = cpu_to_le16(0x0200), 7462306a36Sopenharmony_ci .bDeviceClass = USB_CLASS_HUB, 7562306a36Sopenharmony_ci .bDeviceSubClass = 0, 7662306a36Sopenharmony_ci .bDeviceProtocol = 0, 7762306a36Sopenharmony_ci .bMaxPacketSize0 = 64, 7862306a36Sopenharmony_ci .bNumConfigurations = 1, 7962306a36Sopenharmony_ci .bRESERVED = 0, 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* 8362306a36Sopenharmony_ci * Configuration descriptor: same comments as above 8462306a36Sopenharmony_ci * regarding handling USB1 mode. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci * We don't use sizeof() as Linux definition of 8962306a36Sopenharmony_ci * struct usb_endpoint_descriptor contains 2 9062306a36Sopenharmony_ci * extra bytes 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci#define AST_VHUB_CONF_DESC_SIZE (USB_DT_CONFIG_SIZE + \ 9362306a36Sopenharmony_ci USB_DT_INTERFACE_SIZE + \ 9462306a36Sopenharmony_ci USB_DT_ENDPOINT_SIZE) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic const struct ast_vhub_full_cdesc ast_vhub_conf_desc = { 9762306a36Sopenharmony_ci .cfg = { 9862306a36Sopenharmony_ci .bLength = USB_DT_CONFIG_SIZE, 9962306a36Sopenharmony_ci .bDescriptorType = USB_DT_CONFIG, 10062306a36Sopenharmony_ci .wTotalLength = cpu_to_le16(AST_VHUB_CONF_DESC_SIZE), 10162306a36Sopenharmony_ci .bNumInterfaces = 1, 10262306a36Sopenharmony_ci .bConfigurationValue = 1, 10362306a36Sopenharmony_ci .iConfiguration = 0, 10462306a36Sopenharmony_ci .bmAttributes = USB_CONFIG_ATT_ONE | 10562306a36Sopenharmony_ci USB_CONFIG_ATT_SELFPOWER | 10662306a36Sopenharmony_ci USB_CONFIG_ATT_WAKEUP, 10762306a36Sopenharmony_ci .bMaxPower = 0, 10862306a36Sopenharmony_ci }, 10962306a36Sopenharmony_ci .intf = { 11062306a36Sopenharmony_ci .bLength = USB_DT_INTERFACE_SIZE, 11162306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 11262306a36Sopenharmony_ci .bInterfaceNumber = 0, 11362306a36Sopenharmony_ci .bAlternateSetting = 0, 11462306a36Sopenharmony_ci .bNumEndpoints = 1, 11562306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_HUB, 11662306a36Sopenharmony_ci .bInterfaceSubClass = 0, 11762306a36Sopenharmony_ci .bInterfaceProtocol = 0, 11862306a36Sopenharmony_ci .iInterface = 0, 11962306a36Sopenharmony_ci }, 12062306a36Sopenharmony_ci .ep = { 12162306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 12262306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 12362306a36Sopenharmony_ci .bEndpointAddress = 0x81, 12462306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 12562306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1), 12662306a36Sopenharmony_ci .bInterval = 0x0c, 12762306a36Sopenharmony_ci }, 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#define AST_VHUB_HUB_DESC_SIZE (USB_DT_HUB_NONVAR_SIZE + 2) 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic const struct usb_hub_descriptor ast_vhub_hub_desc = { 13362306a36Sopenharmony_ci .bDescLength = AST_VHUB_HUB_DESC_SIZE, 13462306a36Sopenharmony_ci .bDescriptorType = USB_DT_HUB, 13562306a36Sopenharmony_ci .bNbrPorts = AST_VHUB_NUM_PORTS, 13662306a36Sopenharmony_ci .wHubCharacteristics = cpu_to_le16(HUB_CHAR_NO_LPSM), 13762306a36Sopenharmony_ci .bPwrOn2PwrGood = 10, 13862306a36Sopenharmony_ci .bHubContrCurrent = 0, 13962306a36Sopenharmony_ci .u.hs.DeviceRemovable[0] = 0, 14062306a36Sopenharmony_ci .u.hs.DeviceRemovable[1] = 0xff, 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* 14462306a36Sopenharmony_ci * These strings converted to UTF-16 must be smaller than 14562306a36Sopenharmony_ci * our EP0 buffer. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_cistatic const struct usb_string ast_vhub_str_array[] = { 14862306a36Sopenharmony_ci { 14962306a36Sopenharmony_ci .id = AST_VHUB_STR_SERIAL, 15062306a36Sopenharmony_ci .s = "00000000" 15162306a36Sopenharmony_ci }, 15262306a36Sopenharmony_ci { 15362306a36Sopenharmony_ci .id = AST_VHUB_STR_PRODUCT, 15462306a36Sopenharmony_ci .s = "USB Virtual Hub" 15562306a36Sopenharmony_ci }, 15662306a36Sopenharmony_ci { 15762306a36Sopenharmony_ci .id = AST_VHUB_STR_MANUF, 15862306a36Sopenharmony_ci .s = "Aspeed" 15962306a36Sopenharmony_ci }, 16062306a36Sopenharmony_ci { } 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic const struct usb_gadget_strings ast_vhub_strings = { 16462306a36Sopenharmony_ci .language = 0x0409, 16562306a36Sopenharmony_ci .strings = (struct usb_string *)ast_vhub_str_array 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int ast_vhub_hub_dev_status(struct ast_vhub_ep *ep, 16962306a36Sopenharmony_ci u16 wIndex, u16 wValue) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci u8 st0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci EPDBG(ep, "GET_STATUS(dev)\n"); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* 17662306a36Sopenharmony_ci * Mark it as self-powered, I doubt the BMC is powered off 17762306a36Sopenharmony_ci * the USB bus ... 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci st0 = 1 << USB_DEVICE_SELF_POWERED; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* 18262306a36Sopenharmony_ci * Need to double check how remote wakeup actually works 18362306a36Sopenharmony_ci * on that chip and what triggers it. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci if (ep->vhub->wakeup_en) 18662306a36Sopenharmony_ci st0 |= 1 << USB_DEVICE_REMOTE_WAKEUP; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return ast_vhub_simple_reply(ep, st0, 0); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int ast_vhub_hub_ep_status(struct ast_vhub_ep *ep, 19262306a36Sopenharmony_ci u16 wIndex, u16 wValue) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci int ep_num; 19562306a36Sopenharmony_ci u8 st0 = 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK; 19862306a36Sopenharmony_ci EPDBG(ep, "GET_STATUS(ep%d)\n", ep_num); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* On the hub we have only EP 0 and 1 */ 20162306a36Sopenharmony_ci if (ep_num == 1) { 20262306a36Sopenharmony_ci if (ep->vhub->ep1_stalled) 20362306a36Sopenharmony_ci st0 |= 1 << USB_ENDPOINT_HALT; 20462306a36Sopenharmony_ci } else if (ep_num != 0) 20562306a36Sopenharmony_ci return std_req_stall; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return ast_vhub_simple_reply(ep, st0, 0); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int ast_vhub_hub_dev_feature(struct ast_vhub_ep *ep, 21162306a36Sopenharmony_ci u16 wIndex, u16 wValue, 21262306a36Sopenharmony_ci bool is_set) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci u32 val; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci EPDBG(ep, "%s_FEATURE(dev val=%02x)\n", 21762306a36Sopenharmony_ci is_set ? "SET" : "CLEAR", wValue); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (wValue == USB_DEVICE_REMOTE_WAKEUP) { 22062306a36Sopenharmony_ci ep->vhub->wakeup_en = is_set; 22162306a36Sopenharmony_ci EPDBG(ep, "Hub remote wakeup %s\n", 22262306a36Sopenharmony_ci is_set ? "enabled" : "disabled"); 22362306a36Sopenharmony_ci return std_req_complete; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (wValue == USB_DEVICE_TEST_MODE) { 22762306a36Sopenharmony_ci val = readl(ep->vhub->regs + AST_VHUB_CTRL); 22862306a36Sopenharmony_ci val &= ~GENMASK(10, 8); 22962306a36Sopenharmony_ci val |= VHUB_CTRL_SET_TEST_MODE((wIndex >> 8) & 0x7); 23062306a36Sopenharmony_ci writel(val, ep->vhub->regs + AST_VHUB_CTRL); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return std_req_complete; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return std_req_stall; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int ast_vhub_hub_ep_feature(struct ast_vhub_ep *ep, 23962306a36Sopenharmony_ci u16 wIndex, u16 wValue, 24062306a36Sopenharmony_ci bool is_set) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci int ep_num; 24362306a36Sopenharmony_ci u32 reg; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK; 24662306a36Sopenharmony_ci EPDBG(ep, "%s_FEATURE(ep%d val=%02x)\n", 24762306a36Sopenharmony_ci is_set ? "SET" : "CLEAR", ep_num, wValue); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (ep_num > 1) 25062306a36Sopenharmony_ci return std_req_stall; 25162306a36Sopenharmony_ci if (wValue != USB_ENDPOINT_HALT) 25262306a36Sopenharmony_ci return std_req_stall; 25362306a36Sopenharmony_ci if (ep_num == 0) 25462306a36Sopenharmony_ci return std_req_complete; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci EPDBG(ep, "%s stall on EP 1\n", 25762306a36Sopenharmony_ci is_set ? "setting" : "clearing"); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ep->vhub->ep1_stalled = is_set; 26062306a36Sopenharmony_ci reg = readl(ep->vhub->regs + AST_VHUB_EP1_CTRL); 26162306a36Sopenharmony_ci if (is_set) { 26262306a36Sopenharmony_ci reg |= VHUB_EP1_CTRL_STALL; 26362306a36Sopenharmony_ci } else { 26462306a36Sopenharmony_ci reg &= ~VHUB_EP1_CTRL_STALL; 26562306a36Sopenharmony_ci reg |= VHUB_EP1_CTRL_RESET_TOGGLE; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci writel(reg, ep->vhub->regs + AST_VHUB_EP1_CTRL); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return std_req_complete; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int ast_vhub_rep_desc(struct ast_vhub_ep *ep, 27362306a36Sopenharmony_ci u8 desc_type, u16 len) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci size_t dsize; 27662306a36Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci EPDBG(ep, "GET_DESCRIPTOR(type:%d)\n", desc_type); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* 28162306a36Sopenharmony_ci * Copy first to EP buffer and send from there, so 28262306a36Sopenharmony_ci * we can do some in-place patching if needed. We know 28362306a36Sopenharmony_ci * the EP buffer is big enough but ensure that doesn't 28462306a36Sopenharmony_ci * change. We do that now rather than later after we 28562306a36Sopenharmony_ci * have checked sizes etc... to avoid a gcc bug where 28662306a36Sopenharmony_ci * it thinks len is constant and barfs about read 28762306a36Sopenharmony_ci * overflows in memcpy. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci switch(desc_type) { 29062306a36Sopenharmony_ci case USB_DT_DEVICE: 29162306a36Sopenharmony_ci dsize = USB_DT_DEVICE_SIZE; 29262306a36Sopenharmony_ci memcpy(ep->buf, &vhub->vhub_dev_desc, dsize); 29362306a36Sopenharmony_ci BUILD_BUG_ON(dsize > sizeof(vhub->vhub_dev_desc)); 29462306a36Sopenharmony_ci BUILD_BUG_ON(USB_DT_DEVICE_SIZE >= AST_VHUB_EP0_MAX_PACKET); 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci case USB_DT_OTHER_SPEED_CONFIG: 29762306a36Sopenharmony_ci case USB_DT_CONFIG: 29862306a36Sopenharmony_ci dsize = AST_VHUB_CONF_DESC_SIZE; 29962306a36Sopenharmony_ci memcpy(ep->buf, &vhub->vhub_conf_desc, dsize); 30062306a36Sopenharmony_ci ((u8 *)ep->buf)[1] = desc_type; 30162306a36Sopenharmony_ci BUILD_BUG_ON(dsize > sizeof(vhub->vhub_conf_desc)); 30262306a36Sopenharmony_ci BUILD_BUG_ON(AST_VHUB_CONF_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET); 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci case USB_DT_HUB: 30562306a36Sopenharmony_ci dsize = AST_VHUB_HUB_DESC_SIZE; 30662306a36Sopenharmony_ci memcpy(ep->buf, &vhub->vhub_hub_desc, dsize); 30762306a36Sopenharmony_ci BUILD_BUG_ON(dsize > sizeof(vhub->vhub_hub_desc)); 30862306a36Sopenharmony_ci BUILD_BUG_ON(AST_VHUB_HUB_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET); 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci case USB_DT_DEVICE_QUALIFIER: 31162306a36Sopenharmony_ci dsize = sizeof(vhub->vhub_qual_desc); 31262306a36Sopenharmony_ci memcpy(ep->buf, &vhub->vhub_qual_desc, dsize); 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci default: 31562306a36Sopenharmony_ci return std_req_stall; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Crop requested length */ 31962306a36Sopenharmony_ci if (len > dsize) 32062306a36Sopenharmony_ci len = dsize; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* Shoot it from the EP buffer */ 32362306a36Sopenharmony_ci return ast_vhub_reply(ep, NULL, len); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic struct usb_gadget_strings* 32762306a36Sopenharmony_ciast_vhub_str_of_container(struct usb_gadget_string_container *container) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci return (struct usb_gadget_strings *)container->stash; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int ast_vhub_collect_languages(struct ast_vhub *vhub, void *buf, 33362306a36Sopenharmony_ci size_t size) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci int rc, hdr_len, nlangs, max_langs; 33662306a36Sopenharmony_ci struct usb_gadget_strings *lang_str; 33762306a36Sopenharmony_ci struct usb_gadget_string_container *container; 33862306a36Sopenharmony_ci struct usb_string_descriptor *sdesc = buf; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci nlangs = 0; 34162306a36Sopenharmony_ci hdr_len = sizeof(struct usb_descriptor_header); 34262306a36Sopenharmony_ci max_langs = (size - hdr_len) / sizeof(sdesc->wData[0]); 34362306a36Sopenharmony_ci list_for_each_entry(container, &vhub->vhub_str_desc, list) { 34462306a36Sopenharmony_ci if (nlangs >= max_langs) 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci lang_str = ast_vhub_str_of_container(container); 34862306a36Sopenharmony_ci sdesc->wData[nlangs++] = cpu_to_le16(lang_str->language); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci rc = hdr_len + nlangs * sizeof(sdesc->wData[0]); 35262306a36Sopenharmony_ci sdesc->bLength = rc; 35362306a36Sopenharmony_ci sdesc->bDescriptorType = USB_DT_STRING; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return rc; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic struct usb_gadget_strings *ast_vhub_lookup_string(struct ast_vhub *vhub, 35962306a36Sopenharmony_ci u16 lang_id) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct usb_gadget_strings *lang_str; 36262306a36Sopenharmony_ci struct usb_gadget_string_container *container; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci list_for_each_entry(container, &vhub->vhub_str_desc, list) { 36562306a36Sopenharmony_ci lang_str = ast_vhub_str_of_container(container); 36662306a36Sopenharmony_ci if (lang_str->language == lang_id) 36762306a36Sopenharmony_ci return lang_str; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return NULL; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int ast_vhub_rep_string(struct ast_vhub_ep *ep, 37462306a36Sopenharmony_ci u8 string_id, u16 lang_id, 37562306a36Sopenharmony_ci u16 len) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci int rc; 37862306a36Sopenharmony_ci u8 buf[256]; 37962306a36Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 38062306a36Sopenharmony_ci struct usb_gadget_strings *lang_str; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (string_id == 0) { 38362306a36Sopenharmony_ci rc = ast_vhub_collect_languages(vhub, buf, sizeof(buf)); 38462306a36Sopenharmony_ci } else { 38562306a36Sopenharmony_ci lang_str = ast_vhub_lookup_string(vhub, lang_id); 38662306a36Sopenharmony_ci if (!lang_str) 38762306a36Sopenharmony_ci return std_req_stall; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci rc = usb_gadget_get_string(lang_str, string_id, buf); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (rc < 0 || rc >= AST_VHUB_EP0_MAX_PACKET) 39362306a36Sopenharmony_ci return std_req_stall; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* Shoot it from the EP buffer */ 39662306a36Sopenharmony_ci memcpy(ep->buf, buf, rc); 39762306a36Sopenharmony_ci return ast_vhub_reply(ep, NULL, min_t(u16, rc, len)); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cienum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep, 40162306a36Sopenharmony_ci struct usb_ctrlrequest *crq) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 40462306a36Sopenharmony_ci u16 wValue, wIndex, wLength; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci wValue = le16_to_cpu(crq->wValue); 40762306a36Sopenharmony_ci wIndex = le16_to_cpu(crq->wIndex); 40862306a36Sopenharmony_ci wLength = le16_to_cpu(crq->wLength); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* First packet, grab speed */ 41162306a36Sopenharmony_ci if (vhub->speed == USB_SPEED_UNKNOWN) { 41262306a36Sopenharmony_ci u32 ustat = readl(vhub->regs + AST_VHUB_USBSTS); 41362306a36Sopenharmony_ci if (ustat & VHUB_USBSTS_HISPEED) 41462306a36Sopenharmony_ci vhub->speed = USB_SPEED_HIGH; 41562306a36Sopenharmony_ci else 41662306a36Sopenharmony_ci vhub->speed = USB_SPEED_FULL; 41762306a36Sopenharmony_ci UDCDBG(vhub, "USB status=%08x speed=%s\n", ustat, 41862306a36Sopenharmony_ci vhub->speed == USB_SPEED_HIGH ? "high" : "full"); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci switch ((crq->bRequestType << 8) | crq->bRequest) { 42262306a36Sopenharmony_ci /* SET_ADDRESS */ 42362306a36Sopenharmony_ci case DeviceOutRequest | USB_REQ_SET_ADDRESS: 42462306a36Sopenharmony_ci EPDBG(ep, "SET_ADDRESS: Got address %x\n", wValue); 42562306a36Sopenharmony_ci writel(wValue, vhub->regs + AST_VHUB_CONF); 42662306a36Sopenharmony_ci return std_req_complete; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* GET_STATUS */ 42962306a36Sopenharmony_ci case DeviceRequest | USB_REQ_GET_STATUS: 43062306a36Sopenharmony_ci return ast_vhub_hub_dev_status(ep, wIndex, wValue); 43162306a36Sopenharmony_ci case InterfaceRequest | USB_REQ_GET_STATUS: 43262306a36Sopenharmony_ci return ast_vhub_simple_reply(ep, 0, 0); 43362306a36Sopenharmony_ci case EndpointRequest | USB_REQ_GET_STATUS: 43462306a36Sopenharmony_ci return ast_vhub_hub_ep_status(ep, wIndex, wValue); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* SET/CLEAR_FEATURE */ 43762306a36Sopenharmony_ci case DeviceOutRequest | USB_REQ_SET_FEATURE: 43862306a36Sopenharmony_ci return ast_vhub_hub_dev_feature(ep, wIndex, wValue, true); 43962306a36Sopenharmony_ci case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: 44062306a36Sopenharmony_ci return ast_vhub_hub_dev_feature(ep, wIndex, wValue, false); 44162306a36Sopenharmony_ci case EndpointOutRequest | USB_REQ_SET_FEATURE: 44262306a36Sopenharmony_ci return ast_vhub_hub_ep_feature(ep, wIndex, wValue, true); 44362306a36Sopenharmony_ci case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: 44462306a36Sopenharmony_ci return ast_vhub_hub_ep_feature(ep, wIndex, wValue, false); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* GET/SET_CONFIGURATION */ 44762306a36Sopenharmony_ci case DeviceRequest | USB_REQ_GET_CONFIGURATION: 44862306a36Sopenharmony_ci return ast_vhub_simple_reply(ep, 1); 44962306a36Sopenharmony_ci case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: 45062306a36Sopenharmony_ci if (wValue != 1) 45162306a36Sopenharmony_ci return std_req_stall; 45262306a36Sopenharmony_ci return std_req_complete; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* GET_DESCRIPTOR */ 45562306a36Sopenharmony_ci case DeviceRequest | USB_REQ_GET_DESCRIPTOR: 45662306a36Sopenharmony_ci switch (wValue >> 8) { 45762306a36Sopenharmony_ci case USB_DT_DEVICE: 45862306a36Sopenharmony_ci case USB_DT_CONFIG: 45962306a36Sopenharmony_ci case USB_DT_DEVICE_QUALIFIER: 46062306a36Sopenharmony_ci case USB_DT_OTHER_SPEED_CONFIG: 46162306a36Sopenharmony_ci return ast_vhub_rep_desc(ep, wValue >> 8, 46262306a36Sopenharmony_ci wLength); 46362306a36Sopenharmony_ci case USB_DT_STRING: 46462306a36Sopenharmony_ci return ast_vhub_rep_string(ep, wValue & 0xff, 46562306a36Sopenharmony_ci wIndex, wLength); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci return std_req_stall; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* GET/SET_INTERFACE */ 47062306a36Sopenharmony_ci case DeviceRequest | USB_REQ_GET_INTERFACE: 47162306a36Sopenharmony_ci return ast_vhub_simple_reply(ep, 0); 47262306a36Sopenharmony_ci case DeviceOutRequest | USB_REQ_SET_INTERFACE: 47362306a36Sopenharmony_ci if (wValue != 0 || wIndex != 0) 47462306a36Sopenharmony_ci return std_req_stall; 47562306a36Sopenharmony_ci return std_req_complete; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci return std_req_stall; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic void ast_vhub_update_hub_ep1(struct ast_vhub *vhub, 48162306a36Sopenharmony_ci unsigned int port) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci /* Update HW EP1 response */ 48462306a36Sopenharmony_ci u32 reg = readl(vhub->regs + AST_VHUB_EP1_STS_CHG); 48562306a36Sopenharmony_ci u32 pmask = (1 << (port + 1)); 48662306a36Sopenharmony_ci if (vhub->ports[port].change) 48762306a36Sopenharmony_ci reg |= pmask; 48862306a36Sopenharmony_ci else 48962306a36Sopenharmony_ci reg &= ~pmask; 49062306a36Sopenharmony_ci writel(reg, vhub->regs + AST_VHUB_EP1_STS_CHG); 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic void ast_vhub_change_port_stat(struct ast_vhub *vhub, 49462306a36Sopenharmony_ci unsigned int port, 49562306a36Sopenharmony_ci u16 clr_flags, 49662306a36Sopenharmony_ci u16 set_flags, 49762306a36Sopenharmony_ci bool set_c) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct ast_vhub_port *p = &vhub->ports[port]; 50062306a36Sopenharmony_ci u16 prev; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* Update port status */ 50362306a36Sopenharmony_ci prev = p->status; 50462306a36Sopenharmony_ci p->status = (prev & ~clr_flags) | set_flags; 50562306a36Sopenharmony_ci DDBG(&p->dev, "port %d status %04x -> %04x (C=%d)\n", 50662306a36Sopenharmony_ci port + 1, prev, p->status, set_c); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Update change bits if needed */ 50962306a36Sopenharmony_ci if (set_c) { 51062306a36Sopenharmony_ci u16 chg = p->status ^ prev; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Only these are relevant for change */ 51362306a36Sopenharmony_ci chg &= USB_PORT_STAT_C_CONNECTION | 51462306a36Sopenharmony_ci USB_PORT_STAT_C_ENABLE | 51562306a36Sopenharmony_ci USB_PORT_STAT_C_SUSPEND | 51662306a36Sopenharmony_ci USB_PORT_STAT_C_OVERCURRENT | 51762306a36Sopenharmony_ci USB_PORT_STAT_C_RESET | 51862306a36Sopenharmony_ci USB_PORT_STAT_C_L1; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* 52162306a36Sopenharmony_ci * We only set USB_PORT_STAT_C_ENABLE if we are disabling 52262306a36Sopenharmony_ci * the port as per USB spec, otherwise MacOS gets upset 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci if (p->status & USB_PORT_STAT_ENABLE) 52562306a36Sopenharmony_ci chg &= ~USB_PORT_STAT_C_ENABLE; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci p->change = chg; 52862306a36Sopenharmony_ci ast_vhub_update_hub_ep1(vhub, port); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic void ast_vhub_send_host_wakeup(struct ast_vhub *vhub) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci u32 reg = readl(vhub->regs + AST_VHUB_CTRL); 53562306a36Sopenharmony_ci UDCDBG(vhub, "Waking up host !\n"); 53662306a36Sopenharmony_ci reg |= VHUB_CTRL_MANUAL_REMOTE_WAKEUP; 53762306a36Sopenharmony_ci writel(reg, vhub->regs + AST_VHUB_CTRL); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_civoid ast_vhub_device_connect(struct ast_vhub *vhub, 54162306a36Sopenharmony_ci unsigned int port, bool on) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci if (on) 54462306a36Sopenharmony_ci ast_vhub_change_port_stat(vhub, port, 0, 54562306a36Sopenharmony_ci USB_PORT_STAT_CONNECTION, true); 54662306a36Sopenharmony_ci else 54762306a36Sopenharmony_ci ast_vhub_change_port_stat(vhub, port, 54862306a36Sopenharmony_ci USB_PORT_STAT_CONNECTION | 54962306a36Sopenharmony_ci USB_PORT_STAT_ENABLE, 55062306a36Sopenharmony_ci 0, true); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* 55362306a36Sopenharmony_ci * If the hub is set to wakup the host on connection events 55462306a36Sopenharmony_ci * then send a wakeup. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci if (vhub->wakeup_en) 55762306a36Sopenharmony_ci ast_vhub_send_host_wakeup(vhub); 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic void ast_vhub_wake_work(struct work_struct *work) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct ast_vhub *vhub = container_of(work, 56362306a36Sopenharmony_ci struct ast_vhub, 56462306a36Sopenharmony_ci wake_work); 56562306a36Sopenharmony_ci unsigned long flags; 56662306a36Sopenharmony_ci unsigned int i; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* 56962306a36Sopenharmony_ci * Wake all sleeping ports. If a port is suspended by 57062306a36Sopenharmony_ci * the host suspend (without explicit state suspend), 57162306a36Sopenharmony_ci * we let the normal host wake path deal with it later. 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_ci spin_lock_irqsave(&vhub->lock, flags); 57462306a36Sopenharmony_ci for (i = 0; i < vhub->max_ports; i++) { 57562306a36Sopenharmony_ci struct ast_vhub_port *p = &vhub->ports[i]; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (!(p->status & USB_PORT_STAT_SUSPEND)) 57862306a36Sopenharmony_ci continue; 57962306a36Sopenharmony_ci ast_vhub_change_port_stat(vhub, i, 58062306a36Sopenharmony_ci USB_PORT_STAT_SUSPEND, 58162306a36Sopenharmony_ci 0, true); 58262306a36Sopenharmony_ci ast_vhub_dev_resume(&p->dev); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci ast_vhub_send_host_wakeup(vhub); 58562306a36Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_civoid ast_vhub_hub_wake_all(struct ast_vhub *vhub) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci /* 59162306a36Sopenharmony_ci * A device is trying to wake the world, because this 59262306a36Sopenharmony_ci * can recurse into the device, we break the call chain 59362306a36Sopenharmony_ci * using a work queue 59462306a36Sopenharmony_ci */ 59562306a36Sopenharmony_ci schedule_work(&vhub->wake_work); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void ast_vhub_port_reset(struct ast_vhub *vhub, u8 port) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct ast_vhub_port *p = &vhub->ports[port]; 60162306a36Sopenharmony_ci u16 set, clr, speed; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* First mark disabled */ 60462306a36Sopenharmony_ci ast_vhub_change_port_stat(vhub, port, 60562306a36Sopenharmony_ci USB_PORT_STAT_ENABLE | 60662306a36Sopenharmony_ci USB_PORT_STAT_SUSPEND, 60762306a36Sopenharmony_ci USB_PORT_STAT_RESET, 60862306a36Sopenharmony_ci false); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (!p->dev.driver) 61162306a36Sopenharmony_ci return; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * This will either "start" the port or reset the 61562306a36Sopenharmony_ci * device if already started... 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci ast_vhub_dev_reset(&p->dev); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* Grab the right speed */ 62062306a36Sopenharmony_ci speed = p->dev.driver->max_speed; 62162306a36Sopenharmony_ci if (speed == USB_SPEED_UNKNOWN || speed > vhub->speed) 62262306a36Sopenharmony_ci speed = vhub->speed; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci switch (speed) { 62562306a36Sopenharmony_ci case USB_SPEED_LOW: 62662306a36Sopenharmony_ci set = USB_PORT_STAT_LOW_SPEED; 62762306a36Sopenharmony_ci clr = USB_PORT_STAT_HIGH_SPEED; 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci case USB_SPEED_FULL: 63062306a36Sopenharmony_ci set = 0; 63162306a36Sopenharmony_ci clr = USB_PORT_STAT_LOW_SPEED | 63262306a36Sopenharmony_ci USB_PORT_STAT_HIGH_SPEED; 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci case USB_SPEED_HIGH: 63562306a36Sopenharmony_ci set = USB_PORT_STAT_HIGH_SPEED; 63662306a36Sopenharmony_ci clr = USB_PORT_STAT_LOW_SPEED; 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci default: 63962306a36Sopenharmony_ci UDCDBG(vhub, "Unsupported speed %d when" 64062306a36Sopenharmony_ci " connecting device\n", 64162306a36Sopenharmony_ci speed); 64262306a36Sopenharmony_ci return; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci clr |= USB_PORT_STAT_RESET; 64562306a36Sopenharmony_ci set |= USB_PORT_STAT_ENABLE; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* This should ideally be delayed ... */ 64862306a36Sopenharmony_ci ast_vhub_change_port_stat(vhub, port, clr, set, true); 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic enum std_req_rc ast_vhub_set_port_feature(struct ast_vhub_ep *ep, 65262306a36Sopenharmony_ci u8 port, u16 feat) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 65562306a36Sopenharmony_ci struct ast_vhub_port *p; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (port == 0 || port > vhub->max_ports) 65862306a36Sopenharmony_ci return std_req_stall; 65962306a36Sopenharmony_ci port--; 66062306a36Sopenharmony_ci p = &vhub->ports[port]; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci switch(feat) { 66362306a36Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 66462306a36Sopenharmony_ci if (!(p->status & USB_PORT_STAT_ENABLE)) 66562306a36Sopenharmony_ci return std_req_complete; 66662306a36Sopenharmony_ci ast_vhub_change_port_stat(vhub, port, 66762306a36Sopenharmony_ci 0, USB_PORT_STAT_SUSPEND, 66862306a36Sopenharmony_ci false); 66962306a36Sopenharmony_ci ast_vhub_dev_suspend(&p->dev); 67062306a36Sopenharmony_ci return std_req_complete; 67162306a36Sopenharmony_ci case USB_PORT_FEAT_RESET: 67262306a36Sopenharmony_ci EPDBG(ep, "Port reset !\n"); 67362306a36Sopenharmony_ci ast_vhub_port_reset(vhub, port); 67462306a36Sopenharmony_ci return std_req_complete; 67562306a36Sopenharmony_ci case USB_PORT_FEAT_POWER: 67662306a36Sopenharmony_ci /* 67762306a36Sopenharmony_ci * On Power-on, we mark the connected flag changed, 67862306a36Sopenharmony_ci * if there's a connected device, some hosts will 67962306a36Sopenharmony_ci * otherwise fail to detect it. 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_ci if (p->status & USB_PORT_STAT_CONNECTION) { 68262306a36Sopenharmony_ci p->change |= USB_PORT_STAT_C_CONNECTION; 68362306a36Sopenharmony_ci ast_vhub_update_hub_ep1(vhub, port); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci return std_req_complete; 68662306a36Sopenharmony_ci case USB_PORT_FEAT_TEST: 68762306a36Sopenharmony_ci case USB_PORT_FEAT_INDICATOR: 68862306a36Sopenharmony_ci /* We don't do anything with these */ 68962306a36Sopenharmony_ci return std_req_complete; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci return std_req_stall; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic enum std_req_rc ast_vhub_clr_port_feature(struct ast_vhub_ep *ep, 69562306a36Sopenharmony_ci u8 port, u16 feat) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 69862306a36Sopenharmony_ci struct ast_vhub_port *p; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (port == 0 || port > vhub->max_ports) 70162306a36Sopenharmony_ci return std_req_stall; 70262306a36Sopenharmony_ci port--; 70362306a36Sopenharmony_ci p = &vhub->ports[port]; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci switch(feat) { 70662306a36Sopenharmony_ci case USB_PORT_FEAT_ENABLE: 70762306a36Sopenharmony_ci ast_vhub_change_port_stat(vhub, port, 70862306a36Sopenharmony_ci USB_PORT_STAT_ENABLE | 70962306a36Sopenharmony_ci USB_PORT_STAT_SUSPEND, 0, 71062306a36Sopenharmony_ci false); 71162306a36Sopenharmony_ci ast_vhub_dev_suspend(&p->dev); 71262306a36Sopenharmony_ci return std_req_complete; 71362306a36Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 71462306a36Sopenharmony_ci if (!(p->status & USB_PORT_STAT_SUSPEND)) 71562306a36Sopenharmony_ci return std_req_complete; 71662306a36Sopenharmony_ci ast_vhub_change_port_stat(vhub, port, 71762306a36Sopenharmony_ci USB_PORT_STAT_SUSPEND, 0, 71862306a36Sopenharmony_ci false); 71962306a36Sopenharmony_ci ast_vhub_dev_resume(&p->dev); 72062306a36Sopenharmony_ci return std_req_complete; 72162306a36Sopenharmony_ci case USB_PORT_FEAT_POWER: 72262306a36Sopenharmony_ci /* We don't do power control */ 72362306a36Sopenharmony_ci return std_req_complete; 72462306a36Sopenharmony_ci case USB_PORT_FEAT_INDICATOR: 72562306a36Sopenharmony_ci /* We don't have indicators */ 72662306a36Sopenharmony_ci return std_req_complete; 72762306a36Sopenharmony_ci case USB_PORT_FEAT_C_CONNECTION: 72862306a36Sopenharmony_ci case USB_PORT_FEAT_C_ENABLE: 72962306a36Sopenharmony_ci case USB_PORT_FEAT_C_SUSPEND: 73062306a36Sopenharmony_ci case USB_PORT_FEAT_C_OVER_CURRENT: 73162306a36Sopenharmony_ci case USB_PORT_FEAT_C_RESET: 73262306a36Sopenharmony_ci /* Clear state-change feature */ 73362306a36Sopenharmony_ci p->change &= ~(1u << (feat - 16)); 73462306a36Sopenharmony_ci ast_vhub_update_hub_ep1(vhub, port); 73562306a36Sopenharmony_ci return std_req_complete; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci return std_req_stall; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic enum std_req_rc ast_vhub_get_port_stat(struct ast_vhub_ep *ep, 74162306a36Sopenharmony_ci u8 port) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 74462306a36Sopenharmony_ci u16 stat, chg; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (port == 0 || port > vhub->max_ports) 74762306a36Sopenharmony_ci return std_req_stall; 74862306a36Sopenharmony_ci port--; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci stat = vhub->ports[port].status; 75162306a36Sopenharmony_ci chg = vhub->ports[port].change; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* We always have power */ 75462306a36Sopenharmony_ci stat |= USB_PORT_STAT_POWER; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci EPDBG(ep, " port status=%04x change=%04x\n", stat, chg); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci return ast_vhub_simple_reply(ep, 75962306a36Sopenharmony_ci stat & 0xff, 76062306a36Sopenharmony_ci stat >> 8, 76162306a36Sopenharmony_ci chg & 0xff, 76262306a36Sopenharmony_ci chg >> 8); 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cienum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep, 76662306a36Sopenharmony_ci struct usb_ctrlrequest *crq) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci u16 wValue, wIndex, wLength; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci wValue = le16_to_cpu(crq->wValue); 77162306a36Sopenharmony_ci wIndex = le16_to_cpu(crq->wIndex); 77262306a36Sopenharmony_ci wLength = le16_to_cpu(crq->wLength); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci switch ((crq->bRequestType << 8) | crq->bRequest) { 77562306a36Sopenharmony_ci case GetHubStatus: 77662306a36Sopenharmony_ci EPDBG(ep, "GetHubStatus\n"); 77762306a36Sopenharmony_ci return ast_vhub_simple_reply(ep, 0, 0, 0, 0); 77862306a36Sopenharmony_ci case GetPortStatus: 77962306a36Sopenharmony_ci EPDBG(ep, "GetPortStatus(%d)\n", wIndex & 0xff); 78062306a36Sopenharmony_ci return ast_vhub_get_port_stat(ep, wIndex & 0xf); 78162306a36Sopenharmony_ci case GetHubDescriptor: 78262306a36Sopenharmony_ci if (wValue != (USB_DT_HUB << 8)) 78362306a36Sopenharmony_ci return std_req_stall; 78462306a36Sopenharmony_ci EPDBG(ep, "GetHubDescriptor(%d)\n", wIndex & 0xff); 78562306a36Sopenharmony_ci return ast_vhub_rep_desc(ep, USB_DT_HUB, wLength); 78662306a36Sopenharmony_ci case SetHubFeature: 78762306a36Sopenharmony_ci case ClearHubFeature: 78862306a36Sopenharmony_ci EPDBG(ep, "Get/SetHubFeature(%d)\n", wValue); 78962306a36Sopenharmony_ci /* No feature, just complete the requests */ 79062306a36Sopenharmony_ci if (wValue == C_HUB_LOCAL_POWER || 79162306a36Sopenharmony_ci wValue == C_HUB_OVER_CURRENT) 79262306a36Sopenharmony_ci return std_req_complete; 79362306a36Sopenharmony_ci return std_req_stall; 79462306a36Sopenharmony_ci case SetPortFeature: 79562306a36Sopenharmony_ci EPDBG(ep, "SetPortFeature(%d,%d)\n", wIndex & 0xf, wValue); 79662306a36Sopenharmony_ci return ast_vhub_set_port_feature(ep, wIndex & 0xf, wValue); 79762306a36Sopenharmony_ci case ClearPortFeature: 79862306a36Sopenharmony_ci EPDBG(ep, "ClearPortFeature(%d,%d)\n", wIndex & 0xf, wValue); 79962306a36Sopenharmony_ci return ast_vhub_clr_port_feature(ep, wIndex & 0xf, wValue); 80062306a36Sopenharmony_ci case ClearTTBuffer: 80162306a36Sopenharmony_ci case ResetTT: 80262306a36Sopenharmony_ci case StopTT: 80362306a36Sopenharmony_ci return std_req_complete; 80462306a36Sopenharmony_ci case GetTTState: 80562306a36Sopenharmony_ci return ast_vhub_simple_reply(ep, 0, 0, 0, 0); 80662306a36Sopenharmony_ci default: 80762306a36Sopenharmony_ci EPDBG(ep, "Unknown class request\n"); 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci return std_req_stall; 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_civoid ast_vhub_hub_suspend(struct ast_vhub *vhub) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci unsigned int i; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci UDCDBG(vhub, "USB bus suspend\n"); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (vhub->suspended) 81962306a36Sopenharmony_ci return; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci vhub->suspended = true; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci /* 82462306a36Sopenharmony_ci * Forward to unsuspended ports without changing 82562306a36Sopenharmony_ci * their connection status. 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_ci for (i = 0; i < vhub->max_ports; i++) { 82862306a36Sopenharmony_ci struct ast_vhub_port *p = &vhub->ports[i]; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (!(p->status & USB_PORT_STAT_SUSPEND)) 83162306a36Sopenharmony_ci ast_vhub_dev_suspend(&p->dev); 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_civoid ast_vhub_hub_resume(struct ast_vhub *vhub) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci unsigned int i; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci UDCDBG(vhub, "USB bus resume\n"); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (!vhub->suspended) 84262306a36Sopenharmony_ci return; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci vhub->suspended = false; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* 84762306a36Sopenharmony_ci * Forward to unsuspended ports without changing 84862306a36Sopenharmony_ci * their connection status. 84962306a36Sopenharmony_ci */ 85062306a36Sopenharmony_ci for (i = 0; i < vhub->max_ports; i++) { 85162306a36Sopenharmony_ci struct ast_vhub_port *p = &vhub->ports[i]; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (!(p->status & USB_PORT_STAT_SUSPEND)) 85462306a36Sopenharmony_ci ast_vhub_dev_resume(&p->dev); 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_civoid ast_vhub_hub_reset(struct ast_vhub *vhub) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci unsigned int i; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci UDCDBG(vhub, "USB bus reset\n"); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci /* 86562306a36Sopenharmony_ci * Is the speed known ? If not we don't care, we aren't 86662306a36Sopenharmony_ci * initialized yet and ports haven't been enabled. 86762306a36Sopenharmony_ci */ 86862306a36Sopenharmony_ci if (vhub->speed == USB_SPEED_UNKNOWN) 86962306a36Sopenharmony_ci return; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* We aren't suspended anymore obviously */ 87262306a36Sopenharmony_ci vhub->suspended = false; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* No speed set */ 87562306a36Sopenharmony_ci vhub->speed = USB_SPEED_UNKNOWN; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* Wakeup not enabled anymore */ 87862306a36Sopenharmony_ci vhub->wakeup_en = false; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* 88162306a36Sopenharmony_ci * Clear all port status, disable gadgets and "suspend" 88262306a36Sopenharmony_ci * them. They will be woken up by a port reset. 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_ci for (i = 0; i < vhub->max_ports; i++) { 88562306a36Sopenharmony_ci struct ast_vhub_port *p = &vhub->ports[i]; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* Only keep the connected flag */ 88862306a36Sopenharmony_ci p->status &= USB_PORT_STAT_CONNECTION; 88962306a36Sopenharmony_ci p->change = 0; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* Suspend the gadget if any */ 89262306a36Sopenharmony_ci ast_vhub_dev_suspend(&p->dev); 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* Cleanup HW */ 89662306a36Sopenharmony_ci writel(0, vhub->regs + AST_VHUB_CONF); 89762306a36Sopenharmony_ci writel(0, vhub->regs + AST_VHUB_EP0_CTRL); 89862306a36Sopenharmony_ci writel(VHUB_EP1_CTRL_RESET_TOGGLE | 89962306a36Sopenharmony_ci VHUB_EP1_CTRL_ENABLE, 90062306a36Sopenharmony_ci vhub->regs + AST_VHUB_EP1_CTRL); 90162306a36Sopenharmony_ci writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG); 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_cistatic void ast_vhub_of_parse_dev_desc(struct ast_vhub *vhub, 90562306a36Sopenharmony_ci const struct device_node *vhub_np) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci u16 id; 90862306a36Sopenharmony_ci u32 data; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (!of_property_read_u32(vhub_np, "vhub-vendor-id", &data)) { 91162306a36Sopenharmony_ci id = (u16)data; 91262306a36Sopenharmony_ci vhub->vhub_dev_desc.idVendor = cpu_to_le16(id); 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci if (!of_property_read_u32(vhub_np, "vhub-product-id", &data)) { 91562306a36Sopenharmony_ci id = (u16)data; 91662306a36Sopenharmony_ci vhub->vhub_dev_desc.idProduct = cpu_to_le16(id); 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci if (!of_property_read_u32(vhub_np, "vhub-device-revision", &data)) { 91962306a36Sopenharmony_ci id = (u16)data; 92062306a36Sopenharmony_ci vhub->vhub_dev_desc.bcdDevice = cpu_to_le16(id); 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic void ast_vhub_fixup_usb1_dev_desc(struct ast_vhub *vhub) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci vhub->vhub_dev_desc.bcdUSB = cpu_to_le16(0x0100); 92762306a36Sopenharmony_ci vhub->vhub_dev_desc.bDeviceProtocol = 0; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic struct usb_gadget_string_container* 93162306a36Sopenharmony_ciast_vhub_str_container_alloc(struct ast_vhub *vhub) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci unsigned int size; 93462306a36Sopenharmony_ci struct usb_string *str_array; 93562306a36Sopenharmony_ci struct usb_gadget_strings *lang_str; 93662306a36Sopenharmony_ci struct usb_gadget_string_container *container; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci size = sizeof(*container); 93962306a36Sopenharmony_ci size += sizeof(struct usb_gadget_strings); 94062306a36Sopenharmony_ci size += sizeof(struct usb_string) * AST_VHUB_STR_INDEX_MAX; 94162306a36Sopenharmony_ci container = devm_kzalloc(&vhub->pdev->dev, size, GFP_KERNEL); 94262306a36Sopenharmony_ci if (!container) 94362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci lang_str = ast_vhub_str_of_container(container); 94662306a36Sopenharmony_ci str_array = (struct usb_string *)(lang_str + 1); 94762306a36Sopenharmony_ci lang_str->strings = str_array; 94862306a36Sopenharmony_ci return container; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic void ast_vhub_str_deep_copy(struct usb_gadget_strings *dest, 95262306a36Sopenharmony_ci const struct usb_gadget_strings *src) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci struct usb_string *src_array = src->strings; 95562306a36Sopenharmony_ci struct usb_string *dest_array = dest->strings; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci dest->language = src->language; 95862306a36Sopenharmony_ci if (src_array && dest_array) { 95962306a36Sopenharmony_ci do { 96062306a36Sopenharmony_ci *dest_array = *src_array; 96162306a36Sopenharmony_ci dest_array++; 96262306a36Sopenharmony_ci src_array++; 96362306a36Sopenharmony_ci } while (src_array->s); 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci} 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic int ast_vhub_str_alloc_add(struct ast_vhub *vhub, 96862306a36Sopenharmony_ci const struct usb_gadget_strings *src_str) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci struct usb_gadget_strings *dest_str; 97162306a36Sopenharmony_ci struct usb_gadget_string_container *container; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci container = ast_vhub_str_container_alloc(vhub); 97462306a36Sopenharmony_ci if (IS_ERR(container)) 97562306a36Sopenharmony_ci return PTR_ERR(container); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci dest_str = ast_vhub_str_of_container(container); 97862306a36Sopenharmony_ci ast_vhub_str_deep_copy(dest_str, src_str); 97962306a36Sopenharmony_ci list_add_tail(&container->list, &vhub->vhub_str_desc); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci return 0; 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic const struct { 98562306a36Sopenharmony_ci const char *name; 98662306a36Sopenharmony_ci u8 id; 98762306a36Sopenharmony_ci} str_id_map[] = { 98862306a36Sopenharmony_ci {"manufacturer", AST_VHUB_STR_MANUF}, 98962306a36Sopenharmony_ci {"product", AST_VHUB_STR_PRODUCT}, 99062306a36Sopenharmony_ci {"serial-number", AST_VHUB_STR_SERIAL}, 99162306a36Sopenharmony_ci {}, 99262306a36Sopenharmony_ci}; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int ast_vhub_of_parse_str_desc(struct ast_vhub *vhub, 99562306a36Sopenharmony_ci const struct device_node *desc_np) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci u32 langid; 99862306a36Sopenharmony_ci int ret = 0; 99962306a36Sopenharmony_ci int i, offset; 100062306a36Sopenharmony_ci const char *str; 100162306a36Sopenharmony_ci struct device_node *child; 100262306a36Sopenharmony_ci struct usb_string str_array[AST_VHUB_STR_INDEX_MAX]; 100362306a36Sopenharmony_ci struct usb_gadget_strings lang_str = { 100462306a36Sopenharmony_ci .strings = (struct usb_string *)str_array, 100562306a36Sopenharmony_ci }; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci for_each_child_of_node(desc_np, child) { 100862306a36Sopenharmony_ci if (of_property_read_u32(child, "reg", &langid)) 100962306a36Sopenharmony_ci continue; /* no language identifier specified */ 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (!usb_validate_langid(langid)) 101262306a36Sopenharmony_ci continue; /* invalid language identifier */ 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci lang_str.language = langid; 101562306a36Sopenharmony_ci for (i = offset = 0; str_id_map[i].name; i++) { 101662306a36Sopenharmony_ci str = of_get_property(child, str_id_map[i].name, NULL); 101762306a36Sopenharmony_ci if (str) { 101862306a36Sopenharmony_ci str_array[offset].s = str; 101962306a36Sopenharmony_ci str_array[offset].id = str_id_map[i].id; 102062306a36Sopenharmony_ci offset++; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci str_array[offset].id = 0; 102462306a36Sopenharmony_ci str_array[offset].s = NULL; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci ret = ast_vhub_str_alloc_add(vhub, &lang_str); 102762306a36Sopenharmony_ci if (ret) { 102862306a36Sopenharmony_ci of_node_put(child); 102962306a36Sopenharmony_ci break; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci return ret; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic int ast_vhub_init_desc(struct ast_vhub *vhub) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci int ret; 103962306a36Sopenharmony_ci struct device_node *desc_np; 104062306a36Sopenharmony_ci const struct device_node *vhub_np = vhub->pdev->dev.of_node; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* Initialize vhub Device Descriptor. */ 104362306a36Sopenharmony_ci memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc, 104462306a36Sopenharmony_ci sizeof(vhub->vhub_dev_desc)); 104562306a36Sopenharmony_ci ast_vhub_of_parse_dev_desc(vhub, vhub_np); 104662306a36Sopenharmony_ci if (vhub->force_usb1) 104762306a36Sopenharmony_ci ast_vhub_fixup_usb1_dev_desc(vhub); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci /* Initialize vhub Configuration Descriptor. */ 105062306a36Sopenharmony_ci memcpy(&vhub->vhub_conf_desc, &ast_vhub_conf_desc, 105162306a36Sopenharmony_ci sizeof(vhub->vhub_conf_desc)); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* Initialize vhub Hub Descriptor. */ 105462306a36Sopenharmony_ci memcpy(&vhub->vhub_hub_desc, &ast_vhub_hub_desc, 105562306a36Sopenharmony_ci sizeof(vhub->vhub_hub_desc)); 105662306a36Sopenharmony_ci vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci /* Initialize vhub String Descriptors. */ 105962306a36Sopenharmony_ci INIT_LIST_HEAD(&vhub->vhub_str_desc); 106062306a36Sopenharmony_ci desc_np = of_get_child_by_name(vhub_np, "vhub-strings"); 106162306a36Sopenharmony_ci if (desc_np) { 106262306a36Sopenharmony_ci ret = ast_vhub_of_parse_str_desc(vhub, desc_np); 106362306a36Sopenharmony_ci of_node_put(desc_np); 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci else 106662306a36Sopenharmony_ci ret = ast_vhub_str_alloc_add(vhub, &ast_vhub_strings); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* Initialize vhub Qualifier Descriptor. */ 106962306a36Sopenharmony_ci memcpy(&vhub->vhub_qual_desc, &ast_vhub_qual_desc, 107062306a36Sopenharmony_ci sizeof(vhub->vhub_qual_desc)); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci return ret; 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ciint ast_vhub_init_hub(struct ast_vhub *vhub) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci vhub->speed = USB_SPEED_UNKNOWN; 107862306a36Sopenharmony_ci INIT_WORK(&vhub->wake_work, ast_vhub_wake_work); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return ast_vhub_init_desc(vhub); 108162306a36Sopenharmony_ci} 1082