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 * dev.c - Individual device/gadget management (ie, a port = a gadget) 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/usb.h> 338c2ecf20Sopenharmony_ci#include <linux/usb/hcd.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include "vhub.h" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_civoid ast_vhub_dev_irq(struct ast_vhub_dev *d) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci u32 istat = readl(d->regs + AST_VHUB_DEV_ISR); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci writel(istat, d->regs + AST_VHUB_DEV_ISR); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (istat & VHUV_DEV_IRQ_EP0_IN_ACK_STALL) 448c2ecf20Sopenharmony_ci ast_vhub_ep0_handle_ack(&d->ep0, true); 458c2ecf20Sopenharmony_ci if (istat & VHUV_DEV_IRQ_EP0_OUT_ACK_STALL) 468c2ecf20Sopenharmony_ci ast_vhub_ep0_handle_ack(&d->ep0, false); 478c2ecf20Sopenharmony_ci if (istat & VHUV_DEV_IRQ_EP0_SETUP) 488c2ecf20Sopenharmony_ci ast_vhub_ep0_handle_setup(&d->ep0); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void ast_vhub_dev_enable(struct ast_vhub_dev *d) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci u32 reg, hmsk, i; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (d->enabled) 568c2ecf20Sopenharmony_ci return; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Cleanup EP0 state */ 598c2ecf20Sopenharmony_ci ast_vhub_reset_ep0(d); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* Enable device and its EP0 interrupts */ 628c2ecf20Sopenharmony_ci reg = VHUB_DEV_EN_ENABLE_PORT | 638c2ecf20Sopenharmony_ci VHUB_DEV_EN_EP0_IN_ACK_IRQEN | 648c2ecf20Sopenharmony_ci VHUB_DEV_EN_EP0_OUT_ACK_IRQEN | 658c2ecf20Sopenharmony_ci VHUB_DEV_EN_EP0_SETUP_IRQEN; 668c2ecf20Sopenharmony_ci if (d->gadget.speed == USB_SPEED_HIGH) 678c2ecf20Sopenharmony_ci reg |= VHUB_DEV_EN_SPEED_SEL_HIGH; 688c2ecf20Sopenharmony_ci writel(reg, d->regs + AST_VHUB_DEV_EN_CTRL); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Enable device interrupt in the hub as well */ 718c2ecf20Sopenharmony_ci hmsk = VHUB_IRQ_DEVICE1 << d->index; 728c2ecf20Sopenharmony_ci reg = readl(d->vhub->regs + AST_VHUB_IER); 738c2ecf20Sopenharmony_ci reg |= hmsk; 748c2ecf20Sopenharmony_ci writel(reg, d->vhub->regs + AST_VHUB_IER); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* Set EP0 DMA buffer address */ 778c2ecf20Sopenharmony_ci writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Clear stall on all EPs */ 808c2ecf20Sopenharmony_ci for (i = 0; i < d->max_epns; i++) { 818c2ecf20Sopenharmony_ci struct ast_vhub_ep *ep = d->epns[i]; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (ep && (ep->epn.stalled || ep->epn.wedged)) { 848c2ecf20Sopenharmony_ci ep->epn.stalled = false; 858c2ecf20Sopenharmony_ci ep->epn.wedged = false; 868c2ecf20Sopenharmony_ci ast_vhub_update_epn_stall(ep); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Additional cleanups */ 918c2ecf20Sopenharmony_ci d->wakeup_en = false; 928c2ecf20Sopenharmony_ci d->enabled = true; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void ast_vhub_dev_disable(struct ast_vhub_dev *d) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci u32 reg, hmsk; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!d->enabled) 1008c2ecf20Sopenharmony_ci return; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Disable device interrupt in the hub */ 1038c2ecf20Sopenharmony_ci hmsk = VHUB_IRQ_DEVICE1 << d->index; 1048c2ecf20Sopenharmony_ci reg = readl(d->vhub->regs + AST_VHUB_IER); 1058c2ecf20Sopenharmony_ci reg &= ~hmsk; 1068c2ecf20Sopenharmony_ci writel(reg, d->vhub->regs + AST_VHUB_IER); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Then disable device */ 1098c2ecf20Sopenharmony_ci writel(0, d->regs + AST_VHUB_DEV_EN_CTRL); 1108c2ecf20Sopenharmony_ci d->gadget.speed = USB_SPEED_UNKNOWN; 1118c2ecf20Sopenharmony_ci d->enabled = false; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int ast_vhub_dev_feature(struct ast_vhub_dev *d, 1158c2ecf20Sopenharmony_ci u16 wIndex, u16 wValue, 1168c2ecf20Sopenharmony_ci bool is_set) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci DDBG(d, "%s_FEATURE(dev val=%02x)\n", 1198c2ecf20Sopenharmony_ci is_set ? "SET" : "CLEAR", wValue); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (wValue != USB_DEVICE_REMOTE_WAKEUP) 1228c2ecf20Sopenharmony_ci return std_req_driver; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci d->wakeup_en = is_set; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return std_req_complete; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int ast_vhub_ep_feature(struct ast_vhub_dev *d, 1308c2ecf20Sopenharmony_ci u16 wIndex, u16 wValue, bool is_set) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct ast_vhub_ep *ep; 1338c2ecf20Sopenharmony_ci int ep_num; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK; 1368c2ecf20Sopenharmony_ci DDBG(d, "%s_FEATURE(ep%d val=%02x)\n", 1378c2ecf20Sopenharmony_ci is_set ? "SET" : "CLEAR", ep_num, wValue); 1388c2ecf20Sopenharmony_ci if (ep_num == 0) 1398c2ecf20Sopenharmony_ci return std_req_complete; 1408c2ecf20Sopenharmony_ci if (ep_num >= d->max_epns || !d->epns[ep_num - 1]) 1418c2ecf20Sopenharmony_ci return std_req_stall; 1428c2ecf20Sopenharmony_ci if (wValue != USB_ENDPOINT_HALT) 1438c2ecf20Sopenharmony_ci return std_req_driver; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ep = d->epns[ep_num - 1]; 1468c2ecf20Sopenharmony_ci if (WARN_ON(!ep)) 1478c2ecf20Sopenharmony_ci return std_req_stall; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (!ep->epn.enabled || !ep->ep.desc || ep->epn.is_iso || 1508c2ecf20Sopenharmony_ci ep->epn.is_in != !!(wIndex & USB_DIR_IN)) 1518c2ecf20Sopenharmony_ci return std_req_stall; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci DDBG(d, "%s stall on EP %d\n", 1548c2ecf20Sopenharmony_ci is_set ? "setting" : "clearing", ep_num); 1558c2ecf20Sopenharmony_ci ep->epn.stalled = is_set; 1568c2ecf20Sopenharmony_ci ast_vhub_update_epn_stall(ep); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return std_req_complete; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int ast_vhub_dev_status(struct ast_vhub_dev *d, 1628c2ecf20Sopenharmony_ci u16 wIndex, u16 wValue) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci u8 st0; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci DDBG(d, "GET_STATUS(dev)\n"); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci st0 = d->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED; 1698c2ecf20Sopenharmony_ci if (d->wakeup_en) 1708c2ecf20Sopenharmony_ci st0 |= 1 << USB_DEVICE_REMOTE_WAKEUP; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return ast_vhub_simple_reply(&d->ep0, st0, 0); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int ast_vhub_ep_status(struct ast_vhub_dev *d, 1768c2ecf20Sopenharmony_ci u16 wIndex, u16 wValue) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci int ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK; 1798c2ecf20Sopenharmony_ci struct ast_vhub_ep *ep; 1808c2ecf20Sopenharmony_ci u8 st0 = 0; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci DDBG(d, "GET_STATUS(ep%d)\n", ep_num); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (ep_num >= d->max_epns) 1858c2ecf20Sopenharmony_ci return std_req_stall; 1868c2ecf20Sopenharmony_ci if (ep_num != 0) { 1878c2ecf20Sopenharmony_ci ep = d->epns[ep_num - 1]; 1888c2ecf20Sopenharmony_ci if (!ep) 1898c2ecf20Sopenharmony_ci return std_req_stall; 1908c2ecf20Sopenharmony_ci if (!ep->epn.enabled || !ep->ep.desc || ep->epn.is_iso || 1918c2ecf20Sopenharmony_ci ep->epn.is_in != !!(wIndex & USB_DIR_IN)) 1928c2ecf20Sopenharmony_ci return std_req_stall; 1938c2ecf20Sopenharmony_ci if (ep->epn.stalled) 1948c2ecf20Sopenharmony_ci st0 |= 1 << USB_ENDPOINT_HALT; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return ast_vhub_simple_reply(&d->ep0, st0, 0); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void ast_vhub_dev_set_address(struct ast_vhub_dev *d, u8 addr) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci u32 reg; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci DDBG(d, "SET_ADDRESS: Got address %x\n", addr); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci reg = readl(d->regs + AST_VHUB_DEV_EN_CTRL); 2078c2ecf20Sopenharmony_ci reg &= ~VHUB_DEV_EN_ADDR_MASK; 2088c2ecf20Sopenharmony_ci reg |= VHUB_DEV_EN_SET_ADDR(addr); 2098c2ecf20Sopenharmony_ci writel(reg, d->regs + AST_VHUB_DEV_EN_CTRL); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ciint ast_vhub_std_dev_request(struct ast_vhub_ep *ep, 2138c2ecf20Sopenharmony_ci struct usb_ctrlrequest *crq) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct ast_vhub_dev *d = ep->dev; 2168c2ecf20Sopenharmony_ci u16 wValue, wIndex; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* No driver, we shouldn't be enabled ... */ 2198c2ecf20Sopenharmony_ci if (!d->driver || !d->enabled) { 2208c2ecf20Sopenharmony_ci EPDBG(ep, 2218c2ecf20Sopenharmony_ci "Device is wrong state driver=%p enabled=%d\n", 2228c2ecf20Sopenharmony_ci d->driver, d->enabled); 2238c2ecf20Sopenharmony_ci return std_req_stall; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* 2278c2ecf20Sopenharmony_ci * Note: we used to reject/stall requests while suspended, 2288c2ecf20Sopenharmony_ci * we don't do that anymore as we seem to have cases of 2298c2ecf20Sopenharmony_ci * mass storage getting very upset. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* First packet, grab speed */ 2338c2ecf20Sopenharmony_ci if (d->gadget.speed == USB_SPEED_UNKNOWN) { 2348c2ecf20Sopenharmony_ci d->gadget.speed = ep->vhub->speed; 2358c2ecf20Sopenharmony_ci if (d->gadget.speed > d->driver->max_speed) 2368c2ecf20Sopenharmony_ci d->gadget.speed = d->driver->max_speed; 2378c2ecf20Sopenharmony_ci DDBG(d, "fist packet, captured speed %d\n", 2388c2ecf20Sopenharmony_ci d->gadget.speed); 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci wValue = le16_to_cpu(crq->wValue); 2428c2ecf20Sopenharmony_ci wIndex = le16_to_cpu(crq->wIndex); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci switch ((crq->bRequestType << 8) | crq->bRequest) { 2458c2ecf20Sopenharmony_ci /* SET_ADDRESS */ 2468c2ecf20Sopenharmony_ci case DeviceOutRequest | USB_REQ_SET_ADDRESS: 2478c2ecf20Sopenharmony_ci ast_vhub_dev_set_address(d, wValue); 2488c2ecf20Sopenharmony_ci return std_req_complete; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* GET_STATUS */ 2518c2ecf20Sopenharmony_ci case DeviceRequest | USB_REQ_GET_STATUS: 2528c2ecf20Sopenharmony_ci return ast_vhub_dev_status(d, wIndex, wValue); 2538c2ecf20Sopenharmony_ci case InterfaceRequest | USB_REQ_GET_STATUS: 2548c2ecf20Sopenharmony_ci return ast_vhub_simple_reply(ep, 0, 0); 2558c2ecf20Sopenharmony_ci case EndpointRequest | USB_REQ_GET_STATUS: 2568c2ecf20Sopenharmony_ci return ast_vhub_ep_status(d, wIndex, wValue); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* SET/CLEAR_FEATURE */ 2598c2ecf20Sopenharmony_ci case DeviceOutRequest | USB_REQ_SET_FEATURE: 2608c2ecf20Sopenharmony_ci return ast_vhub_dev_feature(d, wIndex, wValue, true); 2618c2ecf20Sopenharmony_ci case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: 2628c2ecf20Sopenharmony_ci return ast_vhub_dev_feature(d, wIndex, wValue, false); 2638c2ecf20Sopenharmony_ci case EndpointOutRequest | USB_REQ_SET_FEATURE: 2648c2ecf20Sopenharmony_ci return ast_vhub_ep_feature(d, wIndex, wValue, true); 2658c2ecf20Sopenharmony_ci case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: 2668c2ecf20Sopenharmony_ci return ast_vhub_ep_feature(d, wIndex, wValue, false); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci return std_req_driver; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int ast_vhub_udc_wakeup(struct usb_gadget* gadget) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct ast_vhub_dev *d = to_ast_dev(gadget); 2748c2ecf20Sopenharmony_ci unsigned long flags; 2758c2ecf20Sopenharmony_ci int rc = -EINVAL; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci spin_lock_irqsave(&d->vhub->lock, flags); 2788c2ecf20Sopenharmony_ci if (!d->wakeup_en) 2798c2ecf20Sopenharmony_ci goto err; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci DDBG(d, "Device initiated wakeup\n"); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Wakeup the host */ 2848c2ecf20Sopenharmony_ci ast_vhub_hub_wake_all(d->vhub); 2858c2ecf20Sopenharmony_ci rc = 0; 2868c2ecf20Sopenharmony_ci err: 2878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&d->vhub->lock, flags); 2888c2ecf20Sopenharmony_ci return rc; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int ast_vhub_udc_get_frame(struct usb_gadget* gadget) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct ast_vhub_dev *d = to_ast_dev(gadget); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return (readl(d->vhub->regs + AST_VHUB_USBSTS) >> 16) & 0x7ff; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void ast_vhub_dev_nuke(struct ast_vhub_dev *d) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci unsigned int i; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci for (i = 0; i < d->max_epns; i++) { 3038c2ecf20Sopenharmony_ci if (!d->epns[i]) 3048c2ecf20Sopenharmony_ci continue; 3058c2ecf20Sopenharmony_ci ast_vhub_nuke(d->epns[i], -ESHUTDOWN); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int ast_vhub_udc_pullup(struct usb_gadget* gadget, int on) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct ast_vhub_dev *d = to_ast_dev(gadget); 3128c2ecf20Sopenharmony_ci unsigned long flags; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci spin_lock_irqsave(&d->vhub->lock, flags); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci DDBG(d, "pullup(%d)\n", on); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Mark disconnected in the hub */ 3198c2ecf20Sopenharmony_ci ast_vhub_device_connect(d->vhub, d->index, on); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* 3228c2ecf20Sopenharmony_ci * If enabled, nuke all requests if any (there shouldn't be) 3238c2ecf20Sopenharmony_ci * and disable the port. This will clear the address too. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci if (d->enabled) { 3268c2ecf20Sopenharmony_ci ast_vhub_dev_nuke(d); 3278c2ecf20Sopenharmony_ci ast_vhub_dev_disable(d); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&d->vhub->lock, flags); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int ast_vhub_udc_start(struct usb_gadget *gadget, 3368c2ecf20Sopenharmony_ci struct usb_gadget_driver *driver) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct ast_vhub_dev *d = to_ast_dev(gadget); 3398c2ecf20Sopenharmony_ci unsigned long flags; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci spin_lock_irqsave(&d->vhub->lock, flags); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci DDBG(d, "start\n"); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* We don't do much more until the hub enables us */ 3468c2ecf20Sopenharmony_ci d->driver = driver; 3478c2ecf20Sopenharmony_ci d->gadget.is_selfpowered = 1; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&d->vhub->lock, flags); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic struct usb_ep *ast_vhub_udc_match_ep(struct usb_gadget *gadget, 3558c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *desc, 3568c2ecf20Sopenharmony_ci struct usb_ss_ep_comp_descriptor *ss) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct ast_vhub_dev *d = to_ast_dev(gadget); 3598c2ecf20Sopenharmony_ci struct ast_vhub_ep *ep; 3608c2ecf20Sopenharmony_ci struct usb_ep *u_ep; 3618c2ecf20Sopenharmony_ci unsigned int max, addr, i; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci DDBG(d, "Match EP type %d\n", usb_endpoint_type(desc)); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* 3668c2ecf20Sopenharmony_ci * First we need to look for an existing unclaimed EP as another 3678c2ecf20Sopenharmony_ci * configuration may have already associated a bunch of EPs with 3688c2ecf20Sopenharmony_ci * this gadget. This duplicates the code in usb_ep_autoconfig_ss() 3698c2ecf20Sopenharmony_ci * unfortunately. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci list_for_each_entry(u_ep, &gadget->ep_list, ep_list) { 3728c2ecf20Sopenharmony_ci if (usb_gadget_ep_match_desc(gadget, u_ep, desc, ss)) { 3738c2ecf20Sopenharmony_ci DDBG(d, " -> using existing EP%d\n", 3748c2ecf20Sopenharmony_ci to_ast_ep(u_ep)->d_idx); 3758c2ecf20Sopenharmony_ci return u_ep; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * We didn't find one, we need to grab one from the pool. 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * First let's do some sanity checking 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci switch(usb_endpoint_type(desc)) { 3858c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_CONTROL: 3868c2ecf20Sopenharmony_ci /* Only EP0 can be a control endpoint */ 3878c2ecf20Sopenharmony_ci return NULL; 3888c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_ISOC: 3898c2ecf20Sopenharmony_ci /* ISO: limit 1023 bytes full speed, 1024 high/super speed */ 3908c2ecf20Sopenharmony_ci if (gadget_is_dualspeed(gadget)) 3918c2ecf20Sopenharmony_ci max = 1024; 3928c2ecf20Sopenharmony_ci else 3938c2ecf20Sopenharmony_ci max = 1023; 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_BULK: 3968c2ecf20Sopenharmony_ci if (gadget_is_dualspeed(gadget)) 3978c2ecf20Sopenharmony_ci max = 512; 3988c2ecf20Sopenharmony_ci else 3998c2ecf20Sopenharmony_ci max = 64; 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_INT: 4028c2ecf20Sopenharmony_ci if (gadget_is_dualspeed(gadget)) 4038c2ecf20Sopenharmony_ci max = 1024; 4048c2ecf20Sopenharmony_ci else 4058c2ecf20Sopenharmony_ci max = 64; 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci if (usb_endpoint_maxp(desc) > max) 4098c2ecf20Sopenharmony_ci return NULL; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* 4128c2ecf20Sopenharmony_ci * Find a free EP address for that device. We can't 4138c2ecf20Sopenharmony_ci * let the generic code assign these as it would 4148c2ecf20Sopenharmony_ci * create overlapping numbers for IN and OUT which 4158c2ecf20Sopenharmony_ci * we don't support, so also create a suitable name 4168c2ecf20Sopenharmony_ci * that will allow the generic code to use our 4178c2ecf20Sopenharmony_ci * assigned address. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci for (i = 0; i < d->max_epns; i++) 4208c2ecf20Sopenharmony_ci if (d->epns[i] == NULL) 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci if (i >= d->max_epns) 4238c2ecf20Sopenharmony_ci return NULL; 4248c2ecf20Sopenharmony_ci addr = i + 1; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* 4278c2ecf20Sopenharmony_ci * Now grab an EP from the shared pool and associate 4288c2ecf20Sopenharmony_ci * it with our device 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci ep = ast_vhub_alloc_epn(d, addr); 4318c2ecf20Sopenharmony_ci if (!ep) 4328c2ecf20Sopenharmony_ci return NULL; 4338c2ecf20Sopenharmony_ci DDBG(d, "Allocated epn#%d for port EP%d\n", 4348c2ecf20Sopenharmony_ci ep->epn.g_idx, addr); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return &ep->ep; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic int ast_vhub_udc_stop(struct usb_gadget *gadget) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct ast_vhub_dev *d = to_ast_dev(gadget); 4428c2ecf20Sopenharmony_ci unsigned long flags; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci spin_lock_irqsave(&d->vhub->lock, flags); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci DDBG(d, "stop\n"); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci d->driver = NULL; 4498c2ecf20Sopenharmony_ci d->gadget.speed = USB_SPEED_UNKNOWN; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci ast_vhub_dev_nuke(d); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (d->enabled) 4548c2ecf20Sopenharmony_ci ast_vhub_dev_disable(d); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&d->vhub->lock, flags); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic const struct usb_gadget_ops ast_vhub_udc_ops = { 4628c2ecf20Sopenharmony_ci .get_frame = ast_vhub_udc_get_frame, 4638c2ecf20Sopenharmony_ci .wakeup = ast_vhub_udc_wakeup, 4648c2ecf20Sopenharmony_ci .pullup = ast_vhub_udc_pullup, 4658c2ecf20Sopenharmony_ci .udc_start = ast_vhub_udc_start, 4668c2ecf20Sopenharmony_ci .udc_stop = ast_vhub_udc_stop, 4678c2ecf20Sopenharmony_ci .match_ep = ast_vhub_udc_match_ep, 4688c2ecf20Sopenharmony_ci}; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_civoid ast_vhub_dev_suspend(struct ast_vhub_dev *d) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci if (d->driver && d->driver->suspend) { 4738c2ecf20Sopenharmony_ci spin_unlock(&d->vhub->lock); 4748c2ecf20Sopenharmony_ci d->driver->suspend(&d->gadget); 4758c2ecf20Sopenharmony_ci spin_lock(&d->vhub->lock); 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_civoid ast_vhub_dev_resume(struct ast_vhub_dev *d) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci if (d->driver && d->driver->resume) { 4828c2ecf20Sopenharmony_ci spin_unlock(&d->vhub->lock); 4838c2ecf20Sopenharmony_ci d->driver->resume(&d->gadget); 4848c2ecf20Sopenharmony_ci spin_lock(&d->vhub->lock); 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_civoid ast_vhub_dev_reset(struct ast_vhub_dev *d) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci /* No driver, just disable the device and return */ 4918c2ecf20Sopenharmony_ci if (!d->driver) { 4928c2ecf20Sopenharmony_ci ast_vhub_dev_disable(d); 4938c2ecf20Sopenharmony_ci return; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* If the port isn't enabled, just enable it */ 4978c2ecf20Sopenharmony_ci if (!d->enabled) { 4988c2ecf20Sopenharmony_ci DDBG(d, "Reset of disabled device, enabling...\n"); 4998c2ecf20Sopenharmony_ci ast_vhub_dev_enable(d); 5008c2ecf20Sopenharmony_ci } else { 5018c2ecf20Sopenharmony_ci DDBG(d, "Reset of enabled device, resetting...\n"); 5028c2ecf20Sopenharmony_ci spin_unlock(&d->vhub->lock); 5038c2ecf20Sopenharmony_ci usb_gadget_udc_reset(&d->gadget, d->driver); 5048c2ecf20Sopenharmony_ci spin_lock(&d->vhub->lock); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* 5078c2ecf20Sopenharmony_ci * Disable and maybe re-enable HW, this will clear the address 5088c2ecf20Sopenharmony_ci * and speed setting. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci ast_vhub_dev_disable(d); 5118c2ecf20Sopenharmony_ci ast_vhub_dev_enable(d); 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_civoid ast_vhub_del_dev(struct ast_vhub_dev *d) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci unsigned long flags; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci spin_lock_irqsave(&d->vhub->lock, flags); 5208c2ecf20Sopenharmony_ci if (!d->registered) { 5218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&d->vhub->lock, flags); 5228c2ecf20Sopenharmony_ci return; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci d->registered = false; 5258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&d->vhub->lock, flags); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci usb_del_gadget_udc(&d->gadget); 5288c2ecf20Sopenharmony_ci device_unregister(d->port_dev); 5298c2ecf20Sopenharmony_ci kfree(d->epns); 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic void ast_vhub_dev_release(struct device *dev) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci kfree(dev); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ciint ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct ast_vhub_dev *d = &vhub->ports[idx].dev; 5408c2ecf20Sopenharmony_ci struct device *parent = &vhub->pdev->dev; 5418c2ecf20Sopenharmony_ci int rc; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci d->vhub = vhub; 5448c2ecf20Sopenharmony_ci d->index = idx; 5458c2ecf20Sopenharmony_ci d->name = devm_kasprintf(parent, GFP_KERNEL, "port%d", idx+1); 5468c2ecf20Sopenharmony_ci d->regs = vhub->regs + 0x100 + 0x10 * idx; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci ast_vhub_init_ep0(vhub, &d->ep0, d); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* 5518c2ecf20Sopenharmony_ci * A USB device can have up to 30 endpoints besides control 5528c2ecf20Sopenharmony_ci * endpoint 0. 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_ci d->max_epns = min_t(u32, vhub->max_epns, 30); 5558c2ecf20Sopenharmony_ci d->epns = kcalloc(d->max_epns, sizeof(*d->epns), GFP_KERNEL); 5568c2ecf20Sopenharmony_ci if (!d->epns) 5578c2ecf20Sopenharmony_ci return -ENOMEM; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* 5608c2ecf20Sopenharmony_ci * The UDC core really needs us to have separate and uniquely 5618c2ecf20Sopenharmony_ci * named "parent" devices for each port so we create a sub device 5628c2ecf20Sopenharmony_ci * here for that purpose 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_ci d->port_dev = kzalloc(sizeof(struct device), GFP_KERNEL); 5658c2ecf20Sopenharmony_ci if (!d->port_dev) { 5668c2ecf20Sopenharmony_ci rc = -ENOMEM; 5678c2ecf20Sopenharmony_ci goto fail_alloc; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci device_initialize(d->port_dev); 5708c2ecf20Sopenharmony_ci d->port_dev->release = ast_vhub_dev_release; 5718c2ecf20Sopenharmony_ci d->port_dev->parent = parent; 5728c2ecf20Sopenharmony_ci dev_set_name(d->port_dev, "%s:p%d", dev_name(parent), idx + 1); 5738c2ecf20Sopenharmony_ci rc = device_add(d->port_dev); 5748c2ecf20Sopenharmony_ci if (rc) 5758c2ecf20Sopenharmony_ci goto fail_add; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* Populate gadget */ 5788c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&d->gadget.ep_list); 5798c2ecf20Sopenharmony_ci d->gadget.ops = &ast_vhub_udc_ops; 5808c2ecf20Sopenharmony_ci d->gadget.ep0 = &d->ep0.ep; 5818c2ecf20Sopenharmony_ci d->gadget.name = KBUILD_MODNAME; 5828c2ecf20Sopenharmony_ci if (vhub->force_usb1) 5838c2ecf20Sopenharmony_ci d->gadget.max_speed = USB_SPEED_FULL; 5848c2ecf20Sopenharmony_ci else 5858c2ecf20Sopenharmony_ci d->gadget.max_speed = USB_SPEED_HIGH; 5868c2ecf20Sopenharmony_ci d->gadget.speed = USB_SPEED_UNKNOWN; 5878c2ecf20Sopenharmony_ci d->gadget.dev.of_node = vhub->pdev->dev.of_node; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci rc = usb_add_gadget_udc(d->port_dev, &d->gadget); 5908c2ecf20Sopenharmony_ci if (rc != 0) 5918c2ecf20Sopenharmony_ci goto fail_udc; 5928c2ecf20Sopenharmony_ci d->registered = true; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci fail_udc: 5968c2ecf20Sopenharmony_ci device_del(d->port_dev); 5978c2ecf20Sopenharmony_ci fail_add: 5988c2ecf20Sopenharmony_ci put_device(d->port_dev); 5998c2ecf20Sopenharmony_ci fail_alloc: 6008c2ecf20Sopenharmony_ci kfree(d->epns); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci return rc; 6038c2ecf20Sopenharmony_ci} 604