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