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 * ep0.c - Endpoint 0 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
2762306a36Sopenharmony_ci#include "vhub.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ciint ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct usb_request *req = &ep->ep0.req.req;
3262306a36Sopenharmony_ci	int rc;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (WARN_ON(ep->d_idx != 0))
3562306a36Sopenharmony_ci		return std_req_stall;
3662306a36Sopenharmony_ci	if (WARN_ON(!ep->ep0.dir_in))
3762306a36Sopenharmony_ci		return std_req_stall;
3862306a36Sopenharmony_ci	if (WARN_ON(len > AST_VHUB_EP0_MAX_PACKET))
3962306a36Sopenharmony_ci		return std_req_stall;
4062306a36Sopenharmony_ci	if (WARN_ON(req->status == -EINPROGRESS))
4162306a36Sopenharmony_ci		return std_req_stall;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	req->buf = ptr;
4462306a36Sopenharmony_ci	req->length = len;
4562306a36Sopenharmony_ci	req->complete = NULL;
4662306a36Sopenharmony_ci	req->zero = true;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/*
4962306a36Sopenharmony_ci	 * Call internal queue directly after dropping the lock. This is
5062306a36Sopenharmony_ci	 * safe to do as the reply is always the last thing done when
5162306a36Sopenharmony_ci	 * processing a SETUP packet, usually as a tail call
5262306a36Sopenharmony_ci	 */
5362306a36Sopenharmony_ci	spin_unlock(&ep->vhub->lock);
5462306a36Sopenharmony_ci	if (ep->ep.ops->queue(&ep->ep, req, GFP_ATOMIC))
5562306a36Sopenharmony_ci		rc = std_req_stall;
5662306a36Sopenharmony_ci	else
5762306a36Sopenharmony_ci		rc = std_req_data;
5862306a36Sopenharmony_ci	spin_lock(&ep->vhub->lock);
5962306a36Sopenharmony_ci	return rc;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciint __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	u8 *buffer = ep->buf;
6562306a36Sopenharmony_ci	unsigned int i;
6662306a36Sopenharmony_ci	va_list args;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	va_start(args, len);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* Copy data directly into EP buffer */
7162306a36Sopenharmony_ci	for (i = 0; i < len; i++)
7262306a36Sopenharmony_ci		buffer[i] = va_arg(args, int);
7362306a36Sopenharmony_ci	va_end(args);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* req->buf NULL means data is already there */
7662306a36Sopenharmony_ci	return ast_vhub_reply(ep, NULL, len);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_civoid ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct usb_ctrlrequest crq;
8262306a36Sopenharmony_ci	enum std_req_rc std_req_rc;
8362306a36Sopenharmony_ci	int rc = -ENODEV;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (WARN_ON(ep->d_idx != 0))
8662306a36Sopenharmony_ci		return;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * Grab the setup packet from the chip and byteswap
9062306a36Sopenharmony_ci	 * interesting fields
9162306a36Sopenharmony_ci	 */
9262306a36Sopenharmony_ci	memcpy_fromio(&crq, ep->ep0.setup, sizeof(crq));
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	EPDBG(ep, "SETUP packet %02x/%02x/%04x/%04x/%04x [%s] st=%d\n",
9562306a36Sopenharmony_ci	      crq.bRequestType, crq.bRequest,
9662306a36Sopenharmony_ci	       le16_to_cpu(crq.wValue),
9762306a36Sopenharmony_ci	       le16_to_cpu(crq.wIndex),
9862306a36Sopenharmony_ci	       le16_to_cpu(crq.wLength),
9962306a36Sopenharmony_ci	       (crq.bRequestType & USB_DIR_IN) ? "in" : "out",
10062306a36Sopenharmony_ci	       ep->ep0.state);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/*
10362306a36Sopenharmony_ci	 * Check our state, cancel pending requests if needed
10462306a36Sopenharmony_ci	 *
10562306a36Sopenharmony_ci	 * Note: Under some circumstances, we can get a new setup
10662306a36Sopenharmony_ci	 * packet while waiting for the stall ack, just accept it.
10762306a36Sopenharmony_ci	 *
10862306a36Sopenharmony_ci	 * In any case, a SETUP packet in wrong state should have
10962306a36Sopenharmony_ci	 * reset the HW state machine, so let's just log, nuke
11062306a36Sopenharmony_ci	 * requests, move on.
11162306a36Sopenharmony_ci	 */
11262306a36Sopenharmony_ci	if (ep->ep0.state != ep0_state_token &&
11362306a36Sopenharmony_ci	    ep->ep0.state != ep0_state_stall) {
11462306a36Sopenharmony_ci		EPDBG(ep, "wrong state\n");
11562306a36Sopenharmony_ci		ast_vhub_nuke(ep, -EIO);
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* Calculate next state for EP0 */
11962306a36Sopenharmony_ci	ep->ep0.state = ep0_state_data;
12062306a36Sopenharmony_ci	ep->ep0.dir_in = !!(crq.bRequestType & USB_DIR_IN);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* If this is the vHub, we handle requests differently */
12362306a36Sopenharmony_ci	std_req_rc = std_req_driver;
12462306a36Sopenharmony_ci	if (ep->dev == NULL) {
12562306a36Sopenharmony_ci		if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
12662306a36Sopenharmony_ci			std_req_rc = ast_vhub_std_hub_request(ep, &crq);
12762306a36Sopenharmony_ci		else if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS)
12862306a36Sopenharmony_ci			std_req_rc = ast_vhub_class_hub_request(ep, &crq);
12962306a36Sopenharmony_ci		else
13062306a36Sopenharmony_ci			std_req_rc = std_req_stall;
13162306a36Sopenharmony_ci	} else if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
13262306a36Sopenharmony_ci		std_req_rc = ast_vhub_std_dev_request(ep, &crq);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* Act upon result */
13562306a36Sopenharmony_ci	switch(std_req_rc) {
13662306a36Sopenharmony_ci	case std_req_complete:
13762306a36Sopenharmony_ci		goto complete;
13862306a36Sopenharmony_ci	case std_req_stall:
13962306a36Sopenharmony_ci		goto stall;
14062306a36Sopenharmony_ci	case std_req_driver:
14162306a36Sopenharmony_ci		break;
14262306a36Sopenharmony_ci	case std_req_data:
14362306a36Sopenharmony_ci		return;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* Pass request up to the gadget driver */
14762306a36Sopenharmony_ci	if (WARN_ON(!ep->dev))
14862306a36Sopenharmony_ci		goto stall;
14962306a36Sopenharmony_ci	if (ep->dev->driver) {
15062306a36Sopenharmony_ci		EPDBG(ep, "forwarding to gadget...\n");
15162306a36Sopenharmony_ci		spin_unlock(&ep->vhub->lock);
15262306a36Sopenharmony_ci		rc = ep->dev->driver->setup(&ep->dev->gadget, &crq);
15362306a36Sopenharmony_ci		spin_lock(&ep->vhub->lock);
15462306a36Sopenharmony_ci		EPDBG(ep, "driver returned %d\n", rc);
15562306a36Sopenharmony_ci	} else {
15662306a36Sopenharmony_ci		EPDBG(ep, "no gadget for request !\n");
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci	if (rc >= 0)
15962306a36Sopenharmony_ci		return;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci stall:
16262306a36Sopenharmony_ci	EPDBG(ep, "stalling\n");
16362306a36Sopenharmony_ci	writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
16462306a36Sopenharmony_ci	ep->ep0.state = ep0_state_stall;
16562306a36Sopenharmony_ci	ep->ep0.dir_in = false;
16662306a36Sopenharmony_ci	return;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci complete:
16962306a36Sopenharmony_ci	EPVDBG(ep, "sending [in] status with no data\n");
17062306a36Sopenharmony_ci	writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
17162306a36Sopenharmony_ci	ep->ep0.state = ep0_state_status;
17262306a36Sopenharmony_ci	ep->ep0.dir_in = false;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic void ast_vhub_ep0_do_send(struct ast_vhub_ep *ep,
17762306a36Sopenharmony_ci				 struct ast_vhub_req *req)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	unsigned int chunk;
18062306a36Sopenharmony_ci	u32 reg;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* If this is a 0-length request, it's the gadget trying to
18362306a36Sopenharmony_ci	 * send a status on our behalf. We take it from here.
18462306a36Sopenharmony_ci	 */
18562306a36Sopenharmony_ci	if (req->req.length == 0)
18662306a36Sopenharmony_ci		req->last_desc = 1;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Are we done ? Complete request, otherwise wait for next interrupt */
18962306a36Sopenharmony_ci	if (req->last_desc >= 0) {
19062306a36Sopenharmony_ci		EPVDBG(ep, "complete send %d/%d\n",
19162306a36Sopenharmony_ci		       req->req.actual, req->req.length);
19262306a36Sopenharmony_ci		ep->ep0.state = ep0_state_status;
19362306a36Sopenharmony_ci		writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat);
19462306a36Sopenharmony_ci		ast_vhub_done(ep, req, 0);
19562306a36Sopenharmony_ci		return;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/*
19962306a36Sopenharmony_ci	 * Next chunk cropped to max packet size. Also check if this
20062306a36Sopenharmony_ci	 * is the last packet
20162306a36Sopenharmony_ci	 */
20262306a36Sopenharmony_ci	chunk = req->req.length - req->req.actual;
20362306a36Sopenharmony_ci	if (chunk > ep->ep.maxpacket)
20462306a36Sopenharmony_ci		chunk = ep->ep.maxpacket;
20562306a36Sopenharmony_ci	else if ((chunk < ep->ep.maxpacket) || !req->req.zero)
20662306a36Sopenharmony_ci		req->last_desc = 1;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	EPVDBG(ep, "send chunk=%d last=%d, req->act=%d mp=%d\n",
20962306a36Sopenharmony_ci	       chunk, req->last_desc, req->req.actual, ep->ep.maxpacket);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/*
21262306a36Sopenharmony_ci	 * Copy data if any (internal requests already have data
21362306a36Sopenharmony_ci	 * in the EP buffer)
21462306a36Sopenharmony_ci	 */
21562306a36Sopenharmony_ci	if (chunk && req->req.buf)
21662306a36Sopenharmony_ci		memcpy(ep->buf, req->req.buf + req->req.actual, chunk);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	vhub_dma_workaround(ep->buf);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Remember chunk size and trigger send */
22162306a36Sopenharmony_ci	reg = VHUB_EP0_SET_TX_LEN(chunk);
22262306a36Sopenharmony_ci	writel(reg, ep->ep0.ctlstat);
22362306a36Sopenharmony_ci	writel(reg | VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
22462306a36Sopenharmony_ci	req->req.actual += chunk;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void ast_vhub_ep0_rx_prime(struct ast_vhub_ep *ep)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	EPVDBG(ep, "rx prime\n");
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* Prime endpoint for receiving data */
23262306a36Sopenharmony_ci	writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic void ast_vhub_ep0_do_receive(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
23662306a36Sopenharmony_ci				    unsigned int len)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	unsigned int remain;
23962306a36Sopenharmony_ci	int rc = 0;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* We are receiving... grab request */
24262306a36Sopenharmony_ci	remain = req->req.length - req->req.actual;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	EPVDBG(ep, "receive got=%d remain=%d\n", len, remain);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* Are we getting more than asked ? */
24762306a36Sopenharmony_ci	if (len > remain) {
24862306a36Sopenharmony_ci		EPDBG(ep, "receiving too much (ovf: %d) !\n",
24962306a36Sopenharmony_ci		      len - remain);
25062306a36Sopenharmony_ci		len = remain;
25162306a36Sopenharmony_ci		rc = -EOVERFLOW;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Hardware return wrong data len */
25562306a36Sopenharmony_ci	if (len < ep->ep.maxpacket && len != remain) {
25662306a36Sopenharmony_ci		EPDBG(ep, "using expected data len instead\n");
25762306a36Sopenharmony_ci		len = remain;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (len && req->req.buf)
26162306a36Sopenharmony_ci		memcpy(req->req.buf + req->req.actual, ep->buf, len);
26262306a36Sopenharmony_ci	req->req.actual += len;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* Done ? */
26562306a36Sopenharmony_ci	if (len < ep->ep.maxpacket || len == remain) {
26662306a36Sopenharmony_ci		ep->ep0.state = ep0_state_status;
26762306a36Sopenharmony_ci		writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
26862306a36Sopenharmony_ci		ast_vhub_done(ep, req, rc);
26962306a36Sopenharmony_ci	} else
27062306a36Sopenharmony_ci		ast_vhub_ep0_rx_prime(ep);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_civoid ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct ast_vhub_req *req;
27662306a36Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
27762306a36Sopenharmony_ci	struct device *dev = &vhub->pdev->dev;
27862306a36Sopenharmony_ci	bool stall = false;
27962306a36Sopenharmony_ci	u32 stat;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Read EP0 status */
28262306a36Sopenharmony_ci	stat = readl(ep->ep0.ctlstat);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* Grab current request if any */
28562306a36Sopenharmony_ci	req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	EPVDBG(ep, "ACK status=%08x,state=%d is_in=%d in_ack=%d req=%p\n",
28862306a36Sopenharmony_ci		stat, ep->ep0.state, ep->ep0.dir_in, in_ack, req);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	switch(ep->ep0.state) {
29162306a36Sopenharmony_ci	case ep0_state_token:
29262306a36Sopenharmony_ci		/* There should be no request queued in that state... */
29362306a36Sopenharmony_ci		if (req) {
29462306a36Sopenharmony_ci			dev_warn(dev, "request present while in TOKEN state\n");
29562306a36Sopenharmony_ci			ast_vhub_nuke(ep, -EINVAL);
29662306a36Sopenharmony_ci		}
29762306a36Sopenharmony_ci		dev_warn(dev, "ack while in TOKEN state\n");
29862306a36Sopenharmony_ci		stall = true;
29962306a36Sopenharmony_ci		break;
30062306a36Sopenharmony_ci	case ep0_state_data:
30162306a36Sopenharmony_ci		/* Check the state bits corresponding to our direction */
30262306a36Sopenharmony_ci		if ((ep->ep0.dir_in && (stat & VHUB_EP0_TX_BUFF_RDY)) ||
30362306a36Sopenharmony_ci		    (!ep->ep0.dir_in && (stat & VHUB_EP0_RX_BUFF_RDY)) ||
30462306a36Sopenharmony_ci		    (ep->ep0.dir_in != in_ack)) {
30562306a36Sopenharmony_ci			/* In that case, ignore interrupt */
30662306a36Sopenharmony_ci			dev_warn(dev, "irq state mismatch");
30762306a36Sopenharmony_ci			break;
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci		/*
31062306a36Sopenharmony_ci		 * We are in data phase and there's no request, something is
31162306a36Sopenharmony_ci		 * wrong, stall
31262306a36Sopenharmony_ci		 */
31362306a36Sopenharmony_ci		if (!req) {
31462306a36Sopenharmony_ci			dev_warn(dev, "data phase, no request\n");
31562306a36Sopenharmony_ci			stall = true;
31662306a36Sopenharmony_ci			break;
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		/* We have a request, handle data transfers */
32062306a36Sopenharmony_ci		if (ep->ep0.dir_in)
32162306a36Sopenharmony_ci			ast_vhub_ep0_do_send(ep, req);
32262306a36Sopenharmony_ci		else
32362306a36Sopenharmony_ci			ast_vhub_ep0_do_receive(ep, req, VHUB_EP0_RX_LEN(stat));
32462306a36Sopenharmony_ci		return;
32562306a36Sopenharmony_ci	case ep0_state_status:
32662306a36Sopenharmony_ci		/* Nuke stale requests */
32762306a36Sopenharmony_ci		if (req) {
32862306a36Sopenharmony_ci			dev_warn(dev, "request present while in STATUS state\n");
32962306a36Sopenharmony_ci			ast_vhub_nuke(ep, -EINVAL);
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		/*
33362306a36Sopenharmony_ci		 * If the status phase completes with the wrong ack, stall
33462306a36Sopenharmony_ci		 * the endpoint just in case, to abort whatever the host
33562306a36Sopenharmony_ci		 * was doing.
33662306a36Sopenharmony_ci		 */
33762306a36Sopenharmony_ci		if (ep->ep0.dir_in == in_ack) {
33862306a36Sopenharmony_ci			dev_warn(dev, "status direction mismatch\n");
33962306a36Sopenharmony_ci			stall = true;
34062306a36Sopenharmony_ci		}
34162306a36Sopenharmony_ci		break;
34262306a36Sopenharmony_ci	case ep0_state_stall:
34362306a36Sopenharmony_ci		/*
34462306a36Sopenharmony_ci		 * There shouldn't be any request left, but nuke just in case
34562306a36Sopenharmony_ci		 * otherwise the stale request will block subsequent ones
34662306a36Sopenharmony_ci		 */
34762306a36Sopenharmony_ci		ast_vhub_nuke(ep, -EIO);
34862306a36Sopenharmony_ci		break;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* Reset to token state or stall */
35262306a36Sopenharmony_ci	if (stall) {
35362306a36Sopenharmony_ci		writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
35462306a36Sopenharmony_ci		ep->ep0.state = ep0_state_stall;
35562306a36Sopenharmony_ci	} else
35662306a36Sopenharmony_ci		ep->ep0.state = ep0_state_token;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
36062306a36Sopenharmony_ci			      gfp_t gfp_flags)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct ast_vhub_req *req = to_ast_req(u_req);
36362306a36Sopenharmony_ci	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
36462306a36Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
36562306a36Sopenharmony_ci	struct device *dev = &vhub->pdev->dev;
36662306a36Sopenharmony_ci	unsigned long flags;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* Paranoid cheks */
36962306a36Sopenharmony_ci	if (!u_req || (!u_req->complete && !req->internal)) {
37062306a36Sopenharmony_ci		dev_warn(dev, "Bogus EP0 request ! u_req=%p\n", u_req);
37162306a36Sopenharmony_ci		if (u_req) {
37262306a36Sopenharmony_ci			dev_warn(dev, "complete=%p internal=%d\n",
37362306a36Sopenharmony_ci				 u_req->complete, req->internal);
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci		return -EINVAL;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* Not endpoint 0 ? */
37962306a36Sopenharmony_ci	if (WARN_ON(ep->d_idx != 0))
38062306a36Sopenharmony_ci		return -EINVAL;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	/* Disabled device */
38362306a36Sopenharmony_ci	if (ep->dev && !ep->dev->enabled)
38462306a36Sopenharmony_ci		return -ESHUTDOWN;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* Data, no buffer and not internal ? */
38762306a36Sopenharmony_ci	if (u_req->length && !u_req->buf && !req->internal) {
38862306a36Sopenharmony_ci		dev_warn(dev, "Request with no buffer !\n");
38962306a36Sopenharmony_ci		return -EINVAL;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	EPVDBG(ep, "enqueue req @%p\n", req);
39362306a36Sopenharmony_ci	EPVDBG(ep, "  l=%d zero=%d noshort=%d is_in=%d\n",
39462306a36Sopenharmony_ci	       u_req->length, u_req->zero,
39562306a36Sopenharmony_ci	       u_req->short_not_ok, ep->ep0.dir_in);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/* Initialize request progress fields */
39862306a36Sopenharmony_ci	u_req->status = -EINPROGRESS;
39962306a36Sopenharmony_ci	u_req->actual = 0;
40062306a36Sopenharmony_ci	req->last_desc = -1;
40162306a36Sopenharmony_ci	req->active = false;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	spin_lock_irqsave(&vhub->lock, flags);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* EP0 can only support a single request at a time */
40662306a36Sopenharmony_ci	if (!list_empty(&ep->queue) ||
40762306a36Sopenharmony_ci	    ep->ep0.state == ep0_state_token ||
40862306a36Sopenharmony_ci	    ep->ep0.state == ep0_state_stall) {
40962306a36Sopenharmony_ci		dev_warn(dev, "EP0: Request in wrong state\n");
41062306a36Sopenharmony_ci	        EPVDBG(ep, "EP0: list_empty=%d state=%d\n",
41162306a36Sopenharmony_ci		       list_empty(&ep->queue), ep->ep0.state);
41262306a36Sopenharmony_ci		spin_unlock_irqrestore(&vhub->lock, flags);
41362306a36Sopenharmony_ci		return -EBUSY;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* Add request to list and kick processing if empty */
41762306a36Sopenharmony_ci	list_add_tail(&req->queue, &ep->queue);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (ep->ep0.dir_in) {
42062306a36Sopenharmony_ci		/* IN request, send data */
42162306a36Sopenharmony_ci		ast_vhub_ep0_do_send(ep, req);
42262306a36Sopenharmony_ci	} else if (u_req->length == 0) {
42362306a36Sopenharmony_ci		/* 0-len request, send completion as rx */
42462306a36Sopenharmony_ci		EPVDBG(ep, "0-length rx completion\n");
42562306a36Sopenharmony_ci		ep->ep0.state = ep0_state_status;
42662306a36Sopenharmony_ci		writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
42762306a36Sopenharmony_ci		ast_vhub_done(ep, req, 0);
42862306a36Sopenharmony_ci	} else {
42962306a36Sopenharmony_ci		/* OUT request, start receiver */
43062306a36Sopenharmony_ci		ast_vhub_ep0_rx_prime(ep);
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	spin_unlock_irqrestore(&vhub->lock, flags);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	return 0;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic int ast_vhub_ep0_dequeue(struct usb_ep* u_ep, struct usb_request *u_req)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
44162306a36Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
44262306a36Sopenharmony_ci	struct ast_vhub_req *req;
44362306a36Sopenharmony_ci	unsigned long flags;
44462306a36Sopenharmony_ci	int rc = -EINVAL;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	spin_lock_irqsave(&vhub->lock, flags);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Only one request can be in the queue */
44962306a36Sopenharmony_ci	req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* Is it ours ? */
45262306a36Sopenharmony_ci	if (req && u_req == &req->req) {
45362306a36Sopenharmony_ci		EPVDBG(ep, "dequeue req @%p\n", req);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		/*
45662306a36Sopenharmony_ci		 * We don't have to deal with "active" as all
45762306a36Sopenharmony_ci		 * DMAs go to the EP buffers, not the request.
45862306a36Sopenharmony_ci		 */
45962306a36Sopenharmony_ci		ast_vhub_done(ep, req, -ECONNRESET);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		/* We do stall the EP to clean things up in HW */
46262306a36Sopenharmony_ci		writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
46362306a36Sopenharmony_ci		ep->ep0.state = ep0_state_status;
46462306a36Sopenharmony_ci		ep->ep0.dir_in = false;
46562306a36Sopenharmony_ci		rc = 0;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci	spin_unlock_irqrestore(&vhub->lock, flags);
46862306a36Sopenharmony_ci	return rc;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic const struct usb_ep_ops ast_vhub_ep0_ops = {
47362306a36Sopenharmony_ci	.queue		= ast_vhub_ep0_queue,
47462306a36Sopenharmony_ci	.dequeue	= ast_vhub_ep0_dequeue,
47562306a36Sopenharmony_ci	.alloc_request	= ast_vhub_alloc_request,
47662306a36Sopenharmony_ci	.free_request	= ast_vhub_free_request,
47762306a36Sopenharmony_ci};
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_civoid ast_vhub_reset_ep0(struct ast_vhub_dev *dev)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	struct ast_vhub_ep *ep = &dev->ep0;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	ast_vhub_nuke(ep, -EIO);
48462306a36Sopenharmony_ci	ep->ep0.state = ep0_state_token;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_civoid ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
48962306a36Sopenharmony_ci		       struct ast_vhub_dev *dev)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	memset(ep, 0, sizeof(*ep));
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ep->ep.ep_list);
49462306a36Sopenharmony_ci	INIT_LIST_HEAD(&ep->queue);
49562306a36Sopenharmony_ci	ep->ep.ops = &ast_vhub_ep0_ops;
49662306a36Sopenharmony_ci	ep->ep.name = "ep0";
49762306a36Sopenharmony_ci	ep->ep.caps.type_control = true;
49862306a36Sopenharmony_ci	usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EP0_MAX_PACKET);
49962306a36Sopenharmony_ci	ep->d_idx = 0;
50062306a36Sopenharmony_ci	ep->dev = dev;
50162306a36Sopenharmony_ci	ep->vhub = vhub;
50262306a36Sopenharmony_ci	ep->ep0.state = ep0_state_token;
50362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ep->ep0.req.queue);
50462306a36Sopenharmony_ci	ep->ep0.req.internal = true;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/* Small difference between vHub and devices */
50762306a36Sopenharmony_ci	if (dev) {
50862306a36Sopenharmony_ci		ep->ep0.ctlstat = dev->regs + AST_VHUB_DEV_EP0_CTRL;
50962306a36Sopenharmony_ci		ep->ep0.setup = vhub->regs +
51062306a36Sopenharmony_ci			AST_VHUB_SETUP0 + 8 * (dev->index + 1);
51162306a36Sopenharmony_ci		ep->buf = vhub->ep0_bufs +
51262306a36Sopenharmony_ci			AST_VHUB_EP0_MAX_PACKET * (dev->index + 1);
51362306a36Sopenharmony_ci		ep->buf_dma = vhub->ep0_bufs_dma +
51462306a36Sopenharmony_ci			AST_VHUB_EP0_MAX_PACKET * (dev->index + 1);
51562306a36Sopenharmony_ci	} else {
51662306a36Sopenharmony_ci		ep->ep0.ctlstat = vhub->regs + AST_VHUB_EP0_CTRL;
51762306a36Sopenharmony_ci		ep->ep0.setup = vhub->regs + AST_VHUB_SETUP0;
51862306a36Sopenharmony_ci		ep->buf = vhub->ep0_bufs;
51962306a36Sopenharmony_ci		ep->buf_dma = vhub->ep0_bufs_dma;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci}
522