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 * ep0.c - Endpoint 0 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 338c2ecf20Sopenharmony_ci#include "vhub.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciint ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct usb_request *req = &ep->ep0.req.req; 388c2ecf20Sopenharmony_ci int rc; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (WARN_ON(ep->d_idx != 0)) 418c2ecf20Sopenharmony_ci return std_req_stall; 428c2ecf20Sopenharmony_ci if (WARN_ON(!ep->ep0.dir_in)) 438c2ecf20Sopenharmony_ci return std_req_stall; 448c2ecf20Sopenharmony_ci if (WARN_ON(len > AST_VHUB_EP0_MAX_PACKET)) 458c2ecf20Sopenharmony_ci return std_req_stall; 468c2ecf20Sopenharmony_ci if (WARN_ON(req->status == -EINPROGRESS)) 478c2ecf20Sopenharmony_ci return std_req_stall; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci req->buf = ptr; 508c2ecf20Sopenharmony_ci req->length = len; 518c2ecf20Sopenharmony_ci req->complete = NULL; 528c2ecf20Sopenharmony_ci req->zero = true; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* 558c2ecf20Sopenharmony_ci * Call internal queue directly after dropping the lock. This is 568c2ecf20Sopenharmony_ci * safe to do as the reply is always the last thing done when 578c2ecf20Sopenharmony_ci * processing a SETUP packet, usually as a tail call 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci spin_unlock(&ep->vhub->lock); 608c2ecf20Sopenharmony_ci if (ep->ep.ops->queue(&ep->ep, req, GFP_ATOMIC)) 618c2ecf20Sopenharmony_ci rc = std_req_stall; 628c2ecf20Sopenharmony_ci else 638c2ecf20Sopenharmony_ci rc = std_req_data; 648c2ecf20Sopenharmony_ci spin_lock(&ep->vhub->lock); 658c2ecf20Sopenharmony_ci return rc; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciint __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci u8 *buffer = ep->buf; 718c2ecf20Sopenharmony_ci unsigned int i; 728c2ecf20Sopenharmony_ci va_list args; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci va_start(args, len); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* Copy data directly into EP buffer */ 778c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 788c2ecf20Sopenharmony_ci buffer[i] = va_arg(args, int); 798c2ecf20Sopenharmony_ci va_end(args); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* req->buf NULL means data is already there */ 828c2ecf20Sopenharmony_ci return ast_vhub_reply(ep, NULL, len); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_civoid ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct usb_ctrlrequest crq; 888c2ecf20Sopenharmony_ci enum std_req_rc std_req_rc; 898c2ecf20Sopenharmony_ci int rc = -ENODEV; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (WARN_ON(ep->d_idx != 0)) 928c2ecf20Sopenharmony_ci return; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci * Grab the setup packet from the chip and byteswap 968c2ecf20Sopenharmony_ci * interesting fields 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci memcpy_fromio(&crq, ep->ep0.setup, sizeof(crq)); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci EPDBG(ep, "SETUP packet %02x/%02x/%04x/%04x/%04x [%s] st=%d\n", 1018c2ecf20Sopenharmony_ci crq.bRequestType, crq.bRequest, 1028c2ecf20Sopenharmony_ci le16_to_cpu(crq.wValue), 1038c2ecf20Sopenharmony_ci le16_to_cpu(crq.wIndex), 1048c2ecf20Sopenharmony_ci le16_to_cpu(crq.wLength), 1058c2ecf20Sopenharmony_ci (crq.bRequestType & USB_DIR_IN) ? "in" : "out", 1068c2ecf20Sopenharmony_ci ep->ep0.state); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * Check our state, cancel pending requests if needed 1108c2ecf20Sopenharmony_ci * 1118c2ecf20Sopenharmony_ci * Note: Under some circumstances, we can get a new setup 1128c2ecf20Sopenharmony_ci * packet while waiting for the stall ack, just accept it. 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * In any case, a SETUP packet in wrong state should have 1158c2ecf20Sopenharmony_ci * reset the HW state machine, so let's just log, nuke 1168c2ecf20Sopenharmony_ci * requests, move on. 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci if (ep->ep0.state != ep0_state_token && 1198c2ecf20Sopenharmony_ci ep->ep0.state != ep0_state_stall) { 1208c2ecf20Sopenharmony_ci EPDBG(ep, "wrong state\n"); 1218c2ecf20Sopenharmony_ci ast_vhub_nuke(ep, -EIO); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Calculate next state for EP0 */ 1258c2ecf20Sopenharmony_ci ep->ep0.state = ep0_state_data; 1268c2ecf20Sopenharmony_ci ep->ep0.dir_in = !!(crq.bRequestType & USB_DIR_IN); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* If this is the vHub, we handle requests differently */ 1298c2ecf20Sopenharmony_ci std_req_rc = std_req_driver; 1308c2ecf20Sopenharmony_ci if (ep->dev == NULL) { 1318c2ecf20Sopenharmony_ci if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) 1328c2ecf20Sopenharmony_ci std_req_rc = ast_vhub_std_hub_request(ep, &crq); 1338c2ecf20Sopenharmony_ci else if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) 1348c2ecf20Sopenharmony_ci std_req_rc = ast_vhub_class_hub_request(ep, &crq); 1358c2ecf20Sopenharmony_ci else 1368c2ecf20Sopenharmony_ci std_req_rc = std_req_stall; 1378c2ecf20Sopenharmony_ci } else if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) 1388c2ecf20Sopenharmony_ci std_req_rc = ast_vhub_std_dev_request(ep, &crq); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Act upon result */ 1418c2ecf20Sopenharmony_ci switch(std_req_rc) { 1428c2ecf20Sopenharmony_ci case std_req_complete: 1438c2ecf20Sopenharmony_ci goto complete; 1448c2ecf20Sopenharmony_ci case std_req_stall: 1458c2ecf20Sopenharmony_ci goto stall; 1468c2ecf20Sopenharmony_ci case std_req_driver: 1478c2ecf20Sopenharmony_ci break; 1488c2ecf20Sopenharmony_ci case std_req_data: 1498c2ecf20Sopenharmony_ci return; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Pass request up to the gadget driver */ 1538c2ecf20Sopenharmony_ci if (WARN_ON(!ep->dev)) 1548c2ecf20Sopenharmony_ci goto stall; 1558c2ecf20Sopenharmony_ci if (ep->dev->driver) { 1568c2ecf20Sopenharmony_ci EPDBG(ep, "forwarding to gadget...\n"); 1578c2ecf20Sopenharmony_ci spin_unlock(&ep->vhub->lock); 1588c2ecf20Sopenharmony_ci rc = ep->dev->driver->setup(&ep->dev->gadget, &crq); 1598c2ecf20Sopenharmony_ci spin_lock(&ep->vhub->lock); 1608c2ecf20Sopenharmony_ci EPDBG(ep, "driver returned %d\n", rc); 1618c2ecf20Sopenharmony_ci } else { 1628c2ecf20Sopenharmony_ci EPDBG(ep, "no gadget for request !\n"); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci if (rc >= 0) 1658c2ecf20Sopenharmony_ci return; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci stall: 1688c2ecf20Sopenharmony_ci EPDBG(ep, "stalling\n"); 1698c2ecf20Sopenharmony_ci writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat); 1708c2ecf20Sopenharmony_ci ep->ep0.state = ep0_state_stall; 1718c2ecf20Sopenharmony_ci ep->ep0.dir_in = false; 1728c2ecf20Sopenharmony_ci return; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci complete: 1758c2ecf20Sopenharmony_ci EPVDBG(ep, "sending [in] status with no data\n"); 1768c2ecf20Sopenharmony_ci writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat); 1778c2ecf20Sopenharmony_ci ep->ep0.state = ep0_state_status; 1788c2ecf20Sopenharmony_ci ep->ep0.dir_in = false; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void ast_vhub_ep0_do_send(struct ast_vhub_ep *ep, 1838c2ecf20Sopenharmony_ci struct ast_vhub_req *req) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci unsigned int chunk; 1868c2ecf20Sopenharmony_ci u32 reg; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* If this is a 0-length request, it's the gadget trying to 1898c2ecf20Sopenharmony_ci * send a status on our behalf. We take it from here. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci if (req->req.length == 0) 1928c2ecf20Sopenharmony_ci req->last_desc = 1; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Are we done ? Complete request, otherwise wait for next interrupt */ 1958c2ecf20Sopenharmony_ci if (req->last_desc >= 0) { 1968c2ecf20Sopenharmony_ci EPVDBG(ep, "complete send %d/%d\n", 1978c2ecf20Sopenharmony_ci req->req.actual, req->req.length); 1988c2ecf20Sopenharmony_ci ep->ep0.state = ep0_state_status; 1998c2ecf20Sopenharmony_ci writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat); 2008c2ecf20Sopenharmony_ci ast_vhub_done(ep, req, 0); 2018c2ecf20Sopenharmony_ci return; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * Next chunk cropped to max packet size. Also check if this 2068c2ecf20Sopenharmony_ci * is the last packet 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci chunk = req->req.length - req->req.actual; 2098c2ecf20Sopenharmony_ci if (chunk > ep->ep.maxpacket) 2108c2ecf20Sopenharmony_ci chunk = ep->ep.maxpacket; 2118c2ecf20Sopenharmony_ci else if ((chunk < ep->ep.maxpacket) || !req->req.zero) 2128c2ecf20Sopenharmony_ci req->last_desc = 1; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci EPVDBG(ep, "send chunk=%d last=%d, req->act=%d mp=%d\n", 2158c2ecf20Sopenharmony_ci chunk, req->last_desc, req->req.actual, ep->ep.maxpacket); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* 2188c2ecf20Sopenharmony_ci * Copy data if any (internal requests already have data 2198c2ecf20Sopenharmony_ci * in the EP buffer) 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci if (chunk && req->req.buf) 2228c2ecf20Sopenharmony_ci memcpy(ep->buf, req->req.buf + req->req.actual, chunk); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci vhub_dma_workaround(ep->buf); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Remember chunk size and trigger send */ 2278c2ecf20Sopenharmony_ci reg = VHUB_EP0_SET_TX_LEN(chunk); 2288c2ecf20Sopenharmony_ci writel(reg, ep->ep0.ctlstat); 2298c2ecf20Sopenharmony_ci writel(reg | VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat); 2308c2ecf20Sopenharmony_ci req->req.actual += chunk; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic void ast_vhub_ep0_rx_prime(struct ast_vhub_ep *ep) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci EPVDBG(ep, "rx prime\n"); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* Prime endpoint for receiving data */ 2388c2ecf20Sopenharmony_ci writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void ast_vhub_ep0_do_receive(struct ast_vhub_ep *ep, struct ast_vhub_req *req, 2428c2ecf20Sopenharmony_ci unsigned int len) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci unsigned int remain; 2458c2ecf20Sopenharmony_ci int rc = 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* We are receiving... grab request */ 2488c2ecf20Sopenharmony_ci remain = req->req.length - req->req.actual; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci EPVDBG(ep, "receive got=%d remain=%d\n", len, remain); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Are we getting more than asked ? */ 2538c2ecf20Sopenharmony_ci if (len > remain) { 2548c2ecf20Sopenharmony_ci EPDBG(ep, "receiving too much (ovf: %d) !\n", 2558c2ecf20Sopenharmony_ci len - remain); 2568c2ecf20Sopenharmony_ci len = remain; 2578c2ecf20Sopenharmony_ci rc = -EOVERFLOW; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci if (len && req->req.buf) 2608c2ecf20Sopenharmony_ci memcpy(req->req.buf + req->req.actual, ep->buf, len); 2618c2ecf20Sopenharmony_ci req->req.actual += len; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Done ? */ 2648c2ecf20Sopenharmony_ci if (len < ep->ep.maxpacket || len == remain) { 2658c2ecf20Sopenharmony_ci ep->ep0.state = ep0_state_status; 2668c2ecf20Sopenharmony_ci writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat); 2678c2ecf20Sopenharmony_ci ast_vhub_done(ep, req, rc); 2688c2ecf20Sopenharmony_ci } else 2698c2ecf20Sopenharmony_ci ast_vhub_ep0_rx_prime(ep); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_civoid ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct ast_vhub_req *req; 2758c2ecf20Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 2768c2ecf20Sopenharmony_ci struct device *dev = &vhub->pdev->dev; 2778c2ecf20Sopenharmony_ci bool stall = false; 2788c2ecf20Sopenharmony_ci u32 stat; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Read EP0 status */ 2818c2ecf20Sopenharmony_ci stat = readl(ep->ep0.ctlstat); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Grab current request if any */ 2848c2ecf20Sopenharmony_ci req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci EPVDBG(ep, "ACK status=%08x,state=%d is_in=%d in_ack=%d req=%p\n", 2878c2ecf20Sopenharmony_ci stat, ep->ep0.state, ep->ep0.dir_in, in_ack, req); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci switch(ep->ep0.state) { 2908c2ecf20Sopenharmony_ci case ep0_state_token: 2918c2ecf20Sopenharmony_ci /* There should be no request queued in that state... */ 2928c2ecf20Sopenharmony_ci if (req) { 2938c2ecf20Sopenharmony_ci dev_warn(dev, "request present while in TOKEN state\n"); 2948c2ecf20Sopenharmony_ci ast_vhub_nuke(ep, -EINVAL); 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci dev_warn(dev, "ack while in TOKEN state\n"); 2978c2ecf20Sopenharmony_ci stall = true; 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci case ep0_state_data: 3008c2ecf20Sopenharmony_ci /* Check the state bits corresponding to our direction */ 3018c2ecf20Sopenharmony_ci if ((ep->ep0.dir_in && (stat & VHUB_EP0_TX_BUFF_RDY)) || 3028c2ecf20Sopenharmony_ci (!ep->ep0.dir_in && (stat & VHUB_EP0_RX_BUFF_RDY)) || 3038c2ecf20Sopenharmony_ci (ep->ep0.dir_in != in_ack)) { 3048c2ecf20Sopenharmony_ci /* In that case, ignore interrupt */ 3058c2ecf20Sopenharmony_ci dev_warn(dev, "irq state mismatch"); 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * We are in data phase and there's no request, something is 3108c2ecf20Sopenharmony_ci * wrong, stall 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci if (!req) { 3138c2ecf20Sopenharmony_ci dev_warn(dev, "data phase, no request\n"); 3148c2ecf20Sopenharmony_ci stall = true; 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* We have a request, handle data transfers */ 3198c2ecf20Sopenharmony_ci if (ep->ep0.dir_in) 3208c2ecf20Sopenharmony_ci ast_vhub_ep0_do_send(ep, req); 3218c2ecf20Sopenharmony_ci else 3228c2ecf20Sopenharmony_ci ast_vhub_ep0_do_receive(ep, req, VHUB_EP0_RX_LEN(stat)); 3238c2ecf20Sopenharmony_ci return; 3248c2ecf20Sopenharmony_ci case ep0_state_status: 3258c2ecf20Sopenharmony_ci /* Nuke stale requests */ 3268c2ecf20Sopenharmony_ci if (req) { 3278c2ecf20Sopenharmony_ci dev_warn(dev, "request present while in STATUS state\n"); 3288c2ecf20Sopenharmony_ci ast_vhub_nuke(ep, -EINVAL); 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* 3328c2ecf20Sopenharmony_ci * If the status phase completes with the wrong ack, stall 3338c2ecf20Sopenharmony_ci * the endpoint just in case, to abort whatever the host 3348c2ecf20Sopenharmony_ci * was doing. 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci if (ep->ep0.dir_in == in_ack) { 3378c2ecf20Sopenharmony_ci dev_warn(dev, "status direction mismatch\n"); 3388c2ecf20Sopenharmony_ci stall = true; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci case ep0_state_stall: 3428c2ecf20Sopenharmony_ci /* 3438c2ecf20Sopenharmony_ci * There shouldn't be any request left, but nuke just in case 3448c2ecf20Sopenharmony_ci * otherwise the stale request will block subsequent ones 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ci ast_vhub_nuke(ep, -EIO); 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Reset to token state or stall */ 3518c2ecf20Sopenharmony_ci if (stall) { 3528c2ecf20Sopenharmony_ci writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat); 3538c2ecf20Sopenharmony_ci ep->ep0.state = ep0_state_stall; 3548c2ecf20Sopenharmony_ci } else 3558c2ecf20Sopenharmony_ci ep->ep0.state = ep0_state_token; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req, 3598c2ecf20Sopenharmony_ci gfp_t gfp_flags) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct ast_vhub_req *req = to_ast_req(u_req); 3628c2ecf20Sopenharmony_ci struct ast_vhub_ep *ep = to_ast_ep(u_ep); 3638c2ecf20Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 3648c2ecf20Sopenharmony_ci struct device *dev = &vhub->pdev->dev; 3658c2ecf20Sopenharmony_ci unsigned long flags; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Paranoid cheks */ 3688c2ecf20Sopenharmony_ci if (!u_req || (!u_req->complete && !req->internal)) { 3698c2ecf20Sopenharmony_ci dev_warn(dev, "Bogus EP0 request ! u_req=%p\n", u_req); 3708c2ecf20Sopenharmony_ci if (u_req) { 3718c2ecf20Sopenharmony_ci dev_warn(dev, "complete=%p internal=%d\n", 3728c2ecf20Sopenharmony_ci u_req->complete, req->internal); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci return -EINVAL; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Not endpoint 0 ? */ 3788c2ecf20Sopenharmony_ci if (WARN_ON(ep->d_idx != 0)) 3798c2ecf20Sopenharmony_ci return -EINVAL; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* Disabled device */ 3828c2ecf20Sopenharmony_ci if (ep->dev && !ep->dev->enabled) 3838c2ecf20Sopenharmony_ci return -ESHUTDOWN; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* Data, no buffer and not internal ? */ 3868c2ecf20Sopenharmony_ci if (u_req->length && !u_req->buf && !req->internal) { 3878c2ecf20Sopenharmony_ci dev_warn(dev, "Request with no buffer !\n"); 3888c2ecf20Sopenharmony_ci return -EINVAL; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci EPVDBG(ep, "enqueue req @%p\n", req); 3928c2ecf20Sopenharmony_ci EPVDBG(ep, " l=%d zero=%d noshort=%d is_in=%d\n", 3938c2ecf20Sopenharmony_ci u_req->length, u_req->zero, 3948c2ecf20Sopenharmony_ci u_req->short_not_ok, ep->ep0.dir_in); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* Initialize request progress fields */ 3978c2ecf20Sopenharmony_ci u_req->status = -EINPROGRESS; 3988c2ecf20Sopenharmony_ci u_req->actual = 0; 3998c2ecf20Sopenharmony_ci req->last_desc = -1; 4008c2ecf20Sopenharmony_ci req->active = false; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhub->lock, flags); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* EP0 can only support a single request at a time */ 4058c2ecf20Sopenharmony_ci if (!list_empty(&ep->queue) || 4068c2ecf20Sopenharmony_ci ep->ep0.state == ep0_state_token || 4078c2ecf20Sopenharmony_ci ep->ep0.state == ep0_state_stall) { 4088c2ecf20Sopenharmony_ci dev_warn(dev, "EP0: Request in wrong state\n"); 4098c2ecf20Sopenharmony_ci EPVDBG(ep, "EP0: list_empty=%d state=%d\n", 4108c2ecf20Sopenharmony_ci list_empty(&ep->queue), ep->ep0.state); 4118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 4128c2ecf20Sopenharmony_ci return -EBUSY; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Add request to list and kick processing if empty */ 4168c2ecf20Sopenharmony_ci list_add_tail(&req->queue, &ep->queue); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (ep->ep0.dir_in) { 4198c2ecf20Sopenharmony_ci /* IN request, send data */ 4208c2ecf20Sopenharmony_ci ast_vhub_ep0_do_send(ep, req); 4218c2ecf20Sopenharmony_ci } else if (u_req->length == 0) { 4228c2ecf20Sopenharmony_ci /* 0-len request, send completion as rx */ 4238c2ecf20Sopenharmony_ci EPVDBG(ep, "0-length rx completion\n"); 4248c2ecf20Sopenharmony_ci ep->ep0.state = ep0_state_status; 4258c2ecf20Sopenharmony_ci writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat); 4268c2ecf20Sopenharmony_ci ast_vhub_done(ep, req, 0); 4278c2ecf20Sopenharmony_ci } else { 4288c2ecf20Sopenharmony_ci /* OUT request, start receiver */ 4298c2ecf20Sopenharmony_ci ast_vhub_ep0_rx_prime(ep); 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int ast_vhub_ep0_dequeue(struct usb_ep* u_ep, struct usb_request *u_req) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct ast_vhub_ep *ep = to_ast_ep(u_ep); 4408c2ecf20Sopenharmony_ci struct ast_vhub *vhub = ep->vhub; 4418c2ecf20Sopenharmony_ci struct ast_vhub_req *req; 4428c2ecf20Sopenharmony_ci unsigned long flags; 4438c2ecf20Sopenharmony_ci int rc = -EINVAL; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci spin_lock_irqsave(&vhub->lock, flags); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* Only one request can be in the queue */ 4488c2ecf20Sopenharmony_ci req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* Is it ours ? */ 4518c2ecf20Sopenharmony_ci if (req && u_req == &req->req) { 4528c2ecf20Sopenharmony_ci EPVDBG(ep, "dequeue req @%p\n", req); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* 4558c2ecf20Sopenharmony_ci * We don't have to deal with "active" as all 4568c2ecf20Sopenharmony_ci * DMAs go to the EP buffers, not the request. 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_ci ast_vhub_done(ep, req, -ECONNRESET); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* We do stall the EP to clean things up in HW */ 4618c2ecf20Sopenharmony_ci writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat); 4628c2ecf20Sopenharmony_ci ep->ep0.state = ep0_state_status; 4638c2ecf20Sopenharmony_ci ep->ep0.dir_in = false; 4648c2ecf20Sopenharmony_ci rc = 0; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vhub->lock, flags); 4678c2ecf20Sopenharmony_ci return rc; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic const struct usb_ep_ops ast_vhub_ep0_ops = { 4728c2ecf20Sopenharmony_ci .queue = ast_vhub_ep0_queue, 4738c2ecf20Sopenharmony_ci .dequeue = ast_vhub_ep0_dequeue, 4748c2ecf20Sopenharmony_ci .alloc_request = ast_vhub_alloc_request, 4758c2ecf20Sopenharmony_ci .free_request = ast_vhub_free_request, 4768c2ecf20Sopenharmony_ci}; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_civoid ast_vhub_reset_ep0(struct ast_vhub_dev *dev) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct ast_vhub_ep *ep = &dev->ep0; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ast_vhub_nuke(ep, -EIO); 4838c2ecf20Sopenharmony_ci ep->ep0.state = ep0_state_token; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_civoid ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep, 4888c2ecf20Sopenharmony_ci struct ast_vhub_dev *dev) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci memset(ep, 0, sizeof(*ep)); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ep->ep.ep_list); 4938c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ep->queue); 4948c2ecf20Sopenharmony_ci ep->ep.ops = &ast_vhub_ep0_ops; 4958c2ecf20Sopenharmony_ci ep->ep.name = "ep0"; 4968c2ecf20Sopenharmony_ci ep->ep.caps.type_control = true; 4978c2ecf20Sopenharmony_ci usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EP0_MAX_PACKET); 4988c2ecf20Sopenharmony_ci ep->d_idx = 0; 4998c2ecf20Sopenharmony_ci ep->dev = dev; 5008c2ecf20Sopenharmony_ci ep->vhub = vhub; 5018c2ecf20Sopenharmony_ci ep->ep0.state = ep0_state_token; 5028c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ep->ep0.req.queue); 5038c2ecf20Sopenharmony_ci ep->ep0.req.internal = true; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* Small difference between vHub and devices */ 5068c2ecf20Sopenharmony_ci if (dev) { 5078c2ecf20Sopenharmony_ci ep->ep0.ctlstat = dev->regs + AST_VHUB_DEV_EP0_CTRL; 5088c2ecf20Sopenharmony_ci ep->ep0.setup = vhub->regs + 5098c2ecf20Sopenharmony_ci AST_VHUB_SETUP0 + 8 * (dev->index + 1); 5108c2ecf20Sopenharmony_ci ep->buf = vhub->ep0_bufs + 5118c2ecf20Sopenharmony_ci AST_VHUB_EP0_MAX_PACKET * (dev->index + 1); 5128c2ecf20Sopenharmony_ci ep->buf_dma = vhub->ep0_bufs_dma + 5138c2ecf20Sopenharmony_ci AST_VHUB_EP0_MAX_PACKET * (dev->index + 1); 5148c2ecf20Sopenharmony_ci } else { 5158c2ecf20Sopenharmony_ci ep->ep0.ctlstat = vhub->regs + AST_VHUB_EP0_CTRL; 5168c2ecf20Sopenharmony_ci ep->ep0.setup = vhub->regs + AST_VHUB_SETUP0; 5178c2ecf20Sopenharmony_ci ep->buf = vhub->ep0_bufs; 5188c2ecf20Sopenharmony_ci ep->buf_dma = vhub->ep0_bufs_dma; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci} 521