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 * epn.c - Generic endpoints management
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_ci#define EXTRA_CHECKS
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#ifdef EXTRA_CHECKS
388c2ecf20Sopenharmony_ci#define CHECK(ep, expr, fmt...)					\
398c2ecf20Sopenharmony_ci	do {							\
408c2ecf20Sopenharmony_ci		if (!(expr)) EPDBG(ep, "CHECK:" fmt);		\
418c2ecf20Sopenharmony_ci	} while(0)
428c2ecf20Sopenharmony_ci#else
438c2ecf20Sopenharmony_ci#define CHECK(ep, expr, fmt...)	do { } while(0)
448c2ecf20Sopenharmony_ci#endif
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic void ast_vhub_epn_kick(struct ast_vhub_ep *ep, struct ast_vhub_req *req)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	unsigned int act = req->req.actual;
498c2ecf20Sopenharmony_ci	unsigned int len = req->req.length;
508c2ecf20Sopenharmony_ci	unsigned int chunk;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* There should be no DMA ongoing */
538c2ecf20Sopenharmony_ci	WARN_ON(req->active);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/* Calculate next chunk size */
568c2ecf20Sopenharmony_ci	chunk = len - act;
578c2ecf20Sopenharmony_ci	if (chunk > ep->ep.maxpacket)
588c2ecf20Sopenharmony_ci		chunk = ep->ep.maxpacket;
598c2ecf20Sopenharmony_ci	else if ((chunk < ep->ep.maxpacket) || !req->req.zero)
608c2ecf20Sopenharmony_ci		req->last_desc = 1;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	EPVDBG(ep, "kick req %p act=%d/%d chunk=%d last=%d\n",
638c2ecf20Sopenharmony_ci	       req, act, len, chunk, req->last_desc);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* If DMA unavailable, using staging EP buffer */
668c2ecf20Sopenharmony_ci	if (!req->req.dma) {
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		/* For IN transfers, copy data over first */
698c2ecf20Sopenharmony_ci		if (ep->epn.is_in) {
708c2ecf20Sopenharmony_ci			memcpy(ep->buf, req->req.buf + act, chunk);
718c2ecf20Sopenharmony_ci			vhub_dma_workaround(ep->buf);
728c2ecf20Sopenharmony_ci		}
738c2ecf20Sopenharmony_ci		writel(ep->buf_dma, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
748c2ecf20Sopenharmony_ci	} else {
758c2ecf20Sopenharmony_ci		if (ep->epn.is_in)
768c2ecf20Sopenharmony_ci			vhub_dma_workaround(req->req.buf);
778c2ecf20Sopenharmony_ci		writel(req->req.dma + act, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* Start DMA */
818c2ecf20Sopenharmony_ci	req->active = true;
828c2ecf20Sopenharmony_ci	writel(VHUB_EP_DMA_SET_TX_SIZE(chunk),
838c2ecf20Sopenharmony_ci	       ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
848c2ecf20Sopenharmony_ci	writel(VHUB_EP_DMA_SET_TX_SIZE(chunk) | VHUB_EP_DMA_SINGLE_KICK,
858c2ecf20Sopenharmony_ci	       ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void ast_vhub_epn_handle_ack(struct ast_vhub_ep *ep)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct ast_vhub_req *req;
918c2ecf20Sopenharmony_ci	unsigned int len;
928c2ecf20Sopenharmony_ci	u32 stat;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Read EP status */
958c2ecf20Sopenharmony_ci	stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* Grab current request if any */
988c2ecf20Sopenharmony_ci	req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	EPVDBG(ep, "ACK status=%08x is_in=%d, req=%p (active=%d)\n",
1018c2ecf20Sopenharmony_ci	       stat, ep->epn.is_in, req, req ? req->active : 0);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	/* In absence of a request, bail out, must have been dequeued */
1048c2ecf20Sopenharmony_ci	if (!req)
1058c2ecf20Sopenharmony_ci		return;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/*
1088c2ecf20Sopenharmony_ci	 * Request not active, move on to processing queue, active request
1098c2ecf20Sopenharmony_ci	 * was probably dequeued
1108c2ecf20Sopenharmony_ci	 */
1118c2ecf20Sopenharmony_ci	if (!req->active)
1128c2ecf20Sopenharmony_ci		goto next_chunk;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* Check if HW has moved on */
1158c2ecf20Sopenharmony_ci	if (VHUB_EP_DMA_RPTR(stat) != 0) {
1168c2ecf20Sopenharmony_ci		EPDBG(ep, "DMA read pointer not 0 !\n");
1178c2ecf20Sopenharmony_ci		return;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* No current DMA ongoing */
1218c2ecf20Sopenharmony_ci	req->active = false;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* Grab length out of HW */
1248c2ecf20Sopenharmony_ci	len = VHUB_EP_DMA_TX_SIZE(stat);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* If not using DMA, copy data out if needed */
1278c2ecf20Sopenharmony_ci	if (!req->req.dma && !ep->epn.is_in && len)
1288c2ecf20Sopenharmony_ci		memcpy(req->req.buf + req->req.actual, ep->buf, len);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* Adjust size */
1318c2ecf20Sopenharmony_ci	req->req.actual += len;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* Check for short packet */
1348c2ecf20Sopenharmony_ci	if (len < ep->ep.maxpacket)
1358c2ecf20Sopenharmony_ci		req->last_desc = 1;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* That's it ? complete the request and pick a new one */
1388c2ecf20Sopenharmony_ci	if (req->last_desc >= 0) {
1398c2ecf20Sopenharmony_ci		ast_vhub_done(ep, req, 0);
1408c2ecf20Sopenharmony_ci		req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req,
1418c2ecf20Sopenharmony_ci					       queue);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		/*
1448c2ecf20Sopenharmony_ci		 * Due to lock dropping inside "done" the next request could
1458c2ecf20Sopenharmony_ci		 * already be active, so check for that and bail if needed.
1468c2ecf20Sopenharmony_ci		 */
1478c2ecf20Sopenharmony_ci		if (!req || req->active)
1488c2ecf20Sopenharmony_ci			return;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci next_chunk:
1528c2ecf20Sopenharmony_ci	ast_vhub_epn_kick(ep, req);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic inline unsigned int ast_vhub_count_free_descs(struct ast_vhub_ep *ep)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	/*
1588c2ecf20Sopenharmony_ci	 * d_next == d_last means descriptor list empty to HW,
1598c2ecf20Sopenharmony_ci	 * thus we can only have AST_VHUB_DESCS_COUNT-1 descriptors
1608c2ecf20Sopenharmony_ci	 * in the list
1618c2ecf20Sopenharmony_ci	 */
1628c2ecf20Sopenharmony_ci	return (ep->epn.d_last + AST_VHUB_DESCS_COUNT - ep->epn.d_next - 1) &
1638c2ecf20Sopenharmony_ci		(AST_VHUB_DESCS_COUNT - 1);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic void ast_vhub_epn_kick_desc(struct ast_vhub_ep *ep,
1678c2ecf20Sopenharmony_ci				   struct ast_vhub_req *req)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct ast_vhub_desc *desc = NULL;
1708c2ecf20Sopenharmony_ci	unsigned int act = req->act_count;
1718c2ecf20Sopenharmony_ci	unsigned int len = req->req.length;
1728c2ecf20Sopenharmony_ci	unsigned int chunk;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* Mark request active if not already */
1758c2ecf20Sopenharmony_ci	req->active = true;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* If the request was already completely written, do nothing */
1788c2ecf20Sopenharmony_ci	if (req->last_desc >= 0)
1798c2ecf20Sopenharmony_ci		return;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	EPVDBG(ep, "kick act=%d/%d chunk_max=%d free_descs=%d\n",
1828c2ecf20Sopenharmony_ci	       act, len, ep->epn.chunk_max, ast_vhub_count_free_descs(ep));
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* While we can create descriptors */
1858c2ecf20Sopenharmony_ci	while (ast_vhub_count_free_descs(ep) && req->last_desc < 0) {
1868c2ecf20Sopenharmony_ci		unsigned int d_num;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		/* Grab next free descriptor */
1898c2ecf20Sopenharmony_ci		d_num = ep->epn.d_next;
1908c2ecf20Sopenharmony_ci		desc = &ep->epn.descs[d_num];
1918c2ecf20Sopenharmony_ci		ep->epn.d_next = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		/* Calculate next chunk size */
1948c2ecf20Sopenharmony_ci		chunk = len - act;
1958c2ecf20Sopenharmony_ci		if (chunk <= ep->epn.chunk_max) {
1968c2ecf20Sopenharmony_ci			/*
1978c2ecf20Sopenharmony_ci			 * Is this the last packet ? Because of having up to 8
1988c2ecf20Sopenharmony_ci			 * packets in a descriptor we can't just compare "chunk"
1998c2ecf20Sopenharmony_ci			 * with ep.maxpacket. We have to see if it's a multiple
2008c2ecf20Sopenharmony_ci			 * of it to know if we have to send a zero packet.
2018c2ecf20Sopenharmony_ci			 * Sadly that involves a modulo which is a bit expensive
2028c2ecf20Sopenharmony_ci			 * but probably still better than not doing it.
2038c2ecf20Sopenharmony_ci			 */
2048c2ecf20Sopenharmony_ci			if (!chunk || !req->req.zero || (chunk % ep->ep.maxpacket) != 0)
2058c2ecf20Sopenharmony_ci				req->last_desc = d_num;
2068c2ecf20Sopenharmony_ci		} else {
2078c2ecf20Sopenharmony_ci			chunk = ep->epn.chunk_max;
2088c2ecf20Sopenharmony_ci		}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		EPVDBG(ep, " chunk: act=%d/%d chunk=%d last=%d desc=%d free=%d\n",
2118c2ecf20Sopenharmony_ci		       act, len, chunk, req->last_desc, d_num,
2128c2ecf20Sopenharmony_ci		       ast_vhub_count_free_descs(ep));
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		/* Populate descriptor */
2158c2ecf20Sopenharmony_ci		desc->w0 = cpu_to_le32(req->req.dma + act);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		/* Interrupt if end of request or no more descriptors */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		/*
2208c2ecf20Sopenharmony_ci		 * TODO: Be smarter about it, if we don't have enough
2218c2ecf20Sopenharmony_ci		 * descriptors request an interrupt before queue empty
2228c2ecf20Sopenharmony_ci		 * or so in order to be able to populate more before
2238c2ecf20Sopenharmony_ci		 * the HW runs out. This isn't a problem at the moment
2248c2ecf20Sopenharmony_ci		 * as we use 256 descriptors and only put at most one
2258c2ecf20Sopenharmony_ci		 * request in the ring.
2268c2ecf20Sopenharmony_ci		 */
2278c2ecf20Sopenharmony_ci		desc->w1 = cpu_to_le32(VHUB_DSC1_IN_SET_LEN(chunk));
2288c2ecf20Sopenharmony_ci		if (req->last_desc >= 0 || !ast_vhub_count_free_descs(ep))
2298c2ecf20Sopenharmony_ci			desc->w1 |= cpu_to_le32(VHUB_DSC1_IN_INTERRUPT);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		/* Account packet */
2328c2ecf20Sopenharmony_ci		req->act_count = act = act + chunk;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (likely(desc))
2368c2ecf20Sopenharmony_ci		vhub_dma_workaround(desc);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* Tell HW about new descriptors */
2398c2ecf20Sopenharmony_ci	writel(VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next),
2408c2ecf20Sopenharmony_ci	       ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	EPVDBG(ep, "HW kicked, d_next=%d dstat=%08x\n",
2438c2ecf20Sopenharmony_ci	       ep->epn.d_next, readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS));
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic void ast_vhub_epn_handle_ack_desc(struct ast_vhub_ep *ep)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct ast_vhub_req *req;
2498c2ecf20Sopenharmony_ci	unsigned int len, d_last;
2508c2ecf20Sopenharmony_ci	u32 stat, stat1;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* Read EP status, workaround HW race */
2538c2ecf20Sopenharmony_ci	do {
2548c2ecf20Sopenharmony_ci		stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
2558c2ecf20Sopenharmony_ci		stat1 = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
2568c2ecf20Sopenharmony_ci	} while(stat != stat1);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* Extract RPTR */
2598c2ecf20Sopenharmony_ci	d_last = VHUB_EP_DMA_RPTR(stat);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/* Grab current request if any */
2628c2ecf20Sopenharmony_ci	req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	EPVDBG(ep, "ACK status=%08x is_in=%d ep->d_last=%d..%d\n",
2658c2ecf20Sopenharmony_ci	       stat, ep->epn.is_in, ep->epn.d_last, d_last);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/* Check all completed descriptors */
2688c2ecf20Sopenharmony_ci	while (ep->epn.d_last != d_last) {
2698c2ecf20Sopenharmony_ci		struct ast_vhub_desc *desc;
2708c2ecf20Sopenharmony_ci		unsigned int d_num;
2718c2ecf20Sopenharmony_ci		bool is_last_desc;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		/* Grab next completed descriptor */
2748c2ecf20Sopenharmony_ci		d_num = ep->epn.d_last;
2758c2ecf20Sopenharmony_ci		desc = &ep->epn.descs[d_num];
2768c2ecf20Sopenharmony_ci		ep->epn.d_last = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		/* Grab len out of descriptor */
2798c2ecf20Sopenharmony_ci		len = VHUB_DSC1_IN_LEN(le32_to_cpu(desc->w1));
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		EPVDBG(ep, " desc %d len=%d req=%p (act=%d)\n",
2828c2ecf20Sopenharmony_ci		       d_num, len, req, req ? req->active : 0);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		/* If no active request pending, move on */
2858c2ecf20Sopenharmony_ci		if (!req || !req->active)
2868c2ecf20Sopenharmony_ci			continue;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		/* Adjust size */
2898c2ecf20Sopenharmony_ci		req->req.actual += len;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci		/* Is that the last chunk ? */
2928c2ecf20Sopenharmony_ci		is_last_desc = req->last_desc == d_num;
2938c2ecf20Sopenharmony_ci		CHECK(ep, is_last_desc == (len < ep->ep.maxpacket ||
2948c2ecf20Sopenharmony_ci					   (req->req.actual >= req->req.length &&
2958c2ecf20Sopenharmony_ci					    !req->req.zero)),
2968c2ecf20Sopenharmony_ci		      "Last packet discrepancy: last_desc=%d len=%d r.act=%d "
2978c2ecf20Sopenharmony_ci		      "r.len=%d r.zero=%d mp=%d\n",
2988c2ecf20Sopenharmony_ci		      is_last_desc, len, req->req.actual, req->req.length,
2998c2ecf20Sopenharmony_ci		      req->req.zero, ep->ep.maxpacket);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		if (is_last_desc) {
3028c2ecf20Sopenharmony_ci			/*
3038c2ecf20Sopenharmony_ci			 * Because we can only have one request at a time
3048c2ecf20Sopenharmony_ci			 * in our descriptor list in this implementation,
3058c2ecf20Sopenharmony_ci			 * d_last and ep->d_last should now be equal
3068c2ecf20Sopenharmony_ci			 */
3078c2ecf20Sopenharmony_ci			CHECK(ep, d_last == ep->epn.d_last,
3088c2ecf20Sopenharmony_ci			      "DMA read ptr mismatch %d vs %d\n",
3098c2ecf20Sopenharmony_ci			      d_last, ep->epn.d_last);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci			/* Note: done will drop and re-acquire the lock */
3128c2ecf20Sopenharmony_ci			ast_vhub_done(ep, req, 0);
3138c2ecf20Sopenharmony_ci			req = list_first_entry_or_null(&ep->queue,
3148c2ecf20Sopenharmony_ci						       struct ast_vhub_req,
3158c2ecf20Sopenharmony_ci						       queue);
3168c2ecf20Sopenharmony_ci			break;
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* More work ? */
3218c2ecf20Sopenharmony_ci	if (req)
3228c2ecf20Sopenharmony_ci		ast_vhub_epn_kick_desc(ep, req);
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_civoid ast_vhub_epn_ack_irq(struct ast_vhub_ep *ep)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	if (ep->epn.desc_mode)
3288c2ecf20Sopenharmony_ci		ast_vhub_epn_handle_ack_desc(ep);
3298c2ecf20Sopenharmony_ci	else
3308c2ecf20Sopenharmony_ci		ast_vhub_epn_handle_ack(ep);
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req,
3348c2ecf20Sopenharmony_ci			      gfp_t gfp_flags)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct ast_vhub_req *req = to_ast_req(u_req);
3378c2ecf20Sopenharmony_ci	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
3388c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
3398c2ecf20Sopenharmony_ci	unsigned long flags;
3408c2ecf20Sopenharmony_ci	bool empty;
3418c2ecf20Sopenharmony_ci	int rc;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* Paranoid checks */
3448c2ecf20Sopenharmony_ci	if (!u_req || !u_req->complete || !u_req->buf) {
3458c2ecf20Sopenharmony_ci		dev_warn(&vhub->pdev->dev, "Bogus EPn request ! u_req=%p\n", u_req);
3468c2ecf20Sopenharmony_ci		if (u_req) {
3478c2ecf20Sopenharmony_ci			dev_warn(&vhub->pdev->dev, "complete=%p internal=%d\n",
3488c2ecf20Sopenharmony_ci				 u_req->complete, req->internal);
3498c2ecf20Sopenharmony_ci		}
3508c2ecf20Sopenharmony_ci		return -EINVAL;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* Endpoint enabled ? */
3548c2ecf20Sopenharmony_ci	if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx ||
3558c2ecf20Sopenharmony_ci	    !ep->dev->enabled) {
3568c2ecf20Sopenharmony_ci		EPDBG(ep, "Enqueuing request on wrong or disabled EP\n");
3578c2ecf20Sopenharmony_ci		return -ESHUTDOWN;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	/* Map request for DMA if possible. For now, the rule for DMA is
3618c2ecf20Sopenharmony_ci	 * that:
3628c2ecf20Sopenharmony_ci	 *
3638c2ecf20Sopenharmony_ci	 *  * For single stage mode (no descriptors):
3648c2ecf20Sopenharmony_ci	 *
3658c2ecf20Sopenharmony_ci	 *   - The buffer is aligned to a 8 bytes boundary (HW requirement)
3668c2ecf20Sopenharmony_ci	 *   - For a OUT endpoint, the request size is a multiple of the EP
3678c2ecf20Sopenharmony_ci	 *     packet size (otherwise the controller will DMA past the end
3688c2ecf20Sopenharmony_ci	 *     of the buffer if the host is sending a too long packet).
3698c2ecf20Sopenharmony_ci	 *
3708c2ecf20Sopenharmony_ci	 *  * For descriptor mode (tx only for now), always.
3718c2ecf20Sopenharmony_ci	 *
3728c2ecf20Sopenharmony_ci	 * We could relax the latter by making the decision to use the bounce
3738c2ecf20Sopenharmony_ci	 * buffer based on the size of a given *segment* of the request rather
3748c2ecf20Sopenharmony_ci	 * than the whole request.
3758c2ecf20Sopenharmony_ci	 */
3768c2ecf20Sopenharmony_ci	if (ep->epn.desc_mode ||
3778c2ecf20Sopenharmony_ci	    ((((unsigned long)u_req->buf & 7) == 0) &&
3788c2ecf20Sopenharmony_ci	     (ep->epn.is_in || !(u_req->length & (u_ep->maxpacket - 1))))) {
3798c2ecf20Sopenharmony_ci		rc = usb_gadget_map_request_by_dev(&vhub->pdev->dev, u_req,
3808c2ecf20Sopenharmony_ci					    ep->epn.is_in);
3818c2ecf20Sopenharmony_ci		if (rc) {
3828c2ecf20Sopenharmony_ci			dev_warn(&vhub->pdev->dev,
3838c2ecf20Sopenharmony_ci				 "Request mapping failure %d\n", rc);
3848c2ecf20Sopenharmony_ci			return rc;
3858c2ecf20Sopenharmony_ci		}
3868c2ecf20Sopenharmony_ci	} else
3878c2ecf20Sopenharmony_ci		u_req->dma = 0;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	EPVDBG(ep, "enqueue req @%p\n", req);
3908c2ecf20Sopenharmony_ci	EPVDBG(ep, " l=%d dma=0x%x zero=%d noshort=%d noirq=%d is_in=%d\n",
3918c2ecf20Sopenharmony_ci	       u_req->length, (u32)u_req->dma, u_req->zero,
3928c2ecf20Sopenharmony_ci	       u_req->short_not_ok, u_req->no_interrupt,
3938c2ecf20Sopenharmony_ci	       ep->epn.is_in);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/* Initialize request progress fields */
3968c2ecf20Sopenharmony_ci	u_req->status = -EINPROGRESS;
3978c2ecf20Sopenharmony_ci	u_req->actual = 0;
3988c2ecf20Sopenharmony_ci	req->act_count = 0;
3998c2ecf20Sopenharmony_ci	req->active = false;
4008c2ecf20Sopenharmony_ci	req->last_desc = -1;
4018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vhub->lock, flags);
4028c2ecf20Sopenharmony_ci	empty = list_empty(&ep->queue);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* Add request to list and kick processing if empty */
4058c2ecf20Sopenharmony_ci	list_add_tail(&req->queue, &ep->queue);
4068c2ecf20Sopenharmony_ci	if (empty) {
4078c2ecf20Sopenharmony_ci		if (ep->epn.desc_mode)
4088c2ecf20Sopenharmony_ci			ast_vhub_epn_kick_desc(ep, req);
4098c2ecf20Sopenharmony_ci		else
4108c2ecf20Sopenharmony_ci			ast_vhub_epn_kick(ep, req);
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vhub->lock, flags);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	return 0;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic void ast_vhub_stop_active_req(struct ast_vhub_ep *ep,
4188c2ecf20Sopenharmony_ci				     bool restart_ep)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	u32 state, reg, loops;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* Stop DMA activity */
4238c2ecf20Sopenharmony_ci	if (ep->epn.desc_mode)
4248c2ecf20Sopenharmony_ci		writel(VHUB_EP_DMA_CTRL_RESET, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
4258c2ecf20Sopenharmony_ci	else
4268c2ecf20Sopenharmony_ci		writel(0, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	/* Wait for it to complete */
4298c2ecf20Sopenharmony_ci	for (loops = 0; loops < 1000; loops++) {
4308c2ecf20Sopenharmony_ci		state = readl(ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
4318c2ecf20Sopenharmony_ci		state = VHUB_EP_DMA_PROC_STATUS(state);
4328c2ecf20Sopenharmony_ci		if (state == EP_DMA_PROC_RX_IDLE ||
4338c2ecf20Sopenharmony_ci		    state == EP_DMA_PROC_TX_IDLE)
4348c2ecf20Sopenharmony_ci			break;
4358c2ecf20Sopenharmony_ci		udelay(1);
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci	if (loops >= 1000)
4388c2ecf20Sopenharmony_ci		dev_warn(&ep->vhub->pdev->dev, "Timeout waiting for DMA\n");
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	/* If we don't have to restart the endpoint, that's it */
4418c2ecf20Sopenharmony_ci	if (!restart_ep)
4428c2ecf20Sopenharmony_ci		return;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	/* Restart the endpoint */
4458c2ecf20Sopenharmony_ci	if (ep->epn.desc_mode) {
4468c2ecf20Sopenharmony_ci		/*
4478c2ecf20Sopenharmony_ci		 * Take out descriptors by resetting the DMA read
4488c2ecf20Sopenharmony_ci		 * pointer to be equal to the CPU write pointer.
4498c2ecf20Sopenharmony_ci		 *
4508c2ecf20Sopenharmony_ci		 * Note: If we ever support creating descriptors for
4518c2ecf20Sopenharmony_ci		 * requests that aren't the head of the queue, we
4528c2ecf20Sopenharmony_ci		 * may have to do something more complex here,
4538c2ecf20Sopenharmony_ci		 * especially if the request being taken out is
4548c2ecf20Sopenharmony_ci		 * not the current head descriptors.
4558c2ecf20Sopenharmony_ci		 */
4568c2ecf20Sopenharmony_ci		reg = VHUB_EP_DMA_SET_RPTR(ep->epn.d_next) |
4578c2ecf20Sopenharmony_ci			VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next);
4588c2ecf20Sopenharmony_ci		writel(reg, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		/* Then turn it back on */
4618c2ecf20Sopenharmony_ci		writel(ep->epn.dma_conf,
4628c2ecf20Sopenharmony_ci		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
4638c2ecf20Sopenharmony_ci	} else {
4648c2ecf20Sopenharmony_ci		/* Single mode: just turn it back on */
4658c2ecf20Sopenharmony_ci		writel(ep->epn.dma_conf,
4668c2ecf20Sopenharmony_ci		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic int ast_vhub_epn_dequeue(struct usb_ep* u_ep, struct usb_request *u_req)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
4738c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
4748c2ecf20Sopenharmony_ci	struct ast_vhub_req *req;
4758c2ecf20Sopenharmony_ci	unsigned long flags;
4768c2ecf20Sopenharmony_ci	int rc = -EINVAL;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vhub->lock, flags);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	/* Make sure it's actually queued on this endpoint */
4818c2ecf20Sopenharmony_ci	list_for_each_entry (req, &ep->queue, queue) {
4828c2ecf20Sopenharmony_ci		if (&req->req == u_req)
4838c2ecf20Sopenharmony_ci			break;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (&req->req == u_req) {
4878c2ecf20Sopenharmony_ci		EPVDBG(ep, "dequeue req @%p active=%d\n",
4888c2ecf20Sopenharmony_ci		       req, req->active);
4898c2ecf20Sopenharmony_ci		if (req->active)
4908c2ecf20Sopenharmony_ci			ast_vhub_stop_active_req(ep, true);
4918c2ecf20Sopenharmony_ci		ast_vhub_done(ep, req, -ECONNRESET);
4928c2ecf20Sopenharmony_ci		rc = 0;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vhub->lock, flags);
4968c2ecf20Sopenharmony_ci	return rc;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_civoid ast_vhub_update_epn_stall(struct ast_vhub_ep *ep)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	u32 reg;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (WARN_ON(ep->d_idx == 0))
5048c2ecf20Sopenharmony_ci		return;
5058c2ecf20Sopenharmony_ci	reg = readl(ep->epn.regs + AST_VHUB_EP_CONFIG);
5068c2ecf20Sopenharmony_ci	if (ep->epn.stalled || ep->epn.wedged)
5078c2ecf20Sopenharmony_ci		reg |= VHUB_EP_CFG_STALL_CTRL;
5088c2ecf20Sopenharmony_ci	else
5098c2ecf20Sopenharmony_ci		reg &= ~VHUB_EP_CFG_STALL_CTRL;
5108c2ecf20Sopenharmony_ci	writel(reg, ep->epn.regs + AST_VHUB_EP_CONFIG);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (!ep->epn.stalled && !ep->epn.wedged)
5138c2ecf20Sopenharmony_ci		writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx),
5148c2ecf20Sopenharmony_ci		       ep->vhub->regs + AST_VHUB_EP_TOGGLE);
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic int ast_vhub_set_halt_and_wedge(struct usb_ep* u_ep, bool halt,
5188c2ecf20Sopenharmony_ci				      bool wedge)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
5218c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
5228c2ecf20Sopenharmony_ci	unsigned long flags;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	EPDBG(ep, "Set halt (%d) & wedge (%d)\n", halt, wedge);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	if (!u_ep || !u_ep->desc)
5278c2ecf20Sopenharmony_ci		return -EINVAL;
5288c2ecf20Sopenharmony_ci	if (ep->d_idx == 0)
5298c2ecf20Sopenharmony_ci		return 0;
5308c2ecf20Sopenharmony_ci	if (ep->epn.is_iso)
5318c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vhub->lock, flags);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/* Fail with still-busy IN endpoints */
5368c2ecf20Sopenharmony_ci	if (halt && ep->epn.is_in && !list_empty(&ep->queue)) {
5378c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vhub->lock, flags);
5388c2ecf20Sopenharmony_ci		return -EAGAIN;
5398c2ecf20Sopenharmony_ci	}
5408c2ecf20Sopenharmony_ci	ep->epn.stalled = halt;
5418c2ecf20Sopenharmony_ci	ep->epn.wedged = wedge;
5428c2ecf20Sopenharmony_ci	ast_vhub_update_epn_stall(ep);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vhub->lock, flags);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	return 0;
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_cistatic int ast_vhub_epn_set_halt(struct usb_ep *u_ep, int value)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	return ast_vhub_set_halt_and_wedge(u_ep, value != 0, false);
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic int ast_vhub_epn_set_wedge(struct usb_ep *u_ep)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	return ast_vhub_set_halt_and_wedge(u_ep, true, true);
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic int ast_vhub_epn_disable(struct usb_ep* u_ep)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
5628c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = ep->vhub;
5638c2ecf20Sopenharmony_ci	unsigned long flags;
5648c2ecf20Sopenharmony_ci	u32 imask, ep_ier;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	EPDBG(ep, "Disabling !\n");
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vhub->lock, flags);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	ep->epn.enabled = false;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* Stop active DMA if any */
5738c2ecf20Sopenharmony_ci	ast_vhub_stop_active_req(ep, false);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	/* Disable endpoint */
5768c2ecf20Sopenharmony_ci	writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/* Disable ACK interrupt */
5798c2ecf20Sopenharmony_ci	imask = VHUB_EP_IRQ(ep->epn.g_idx);
5808c2ecf20Sopenharmony_ci	ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER);
5818c2ecf20Sopenharmony_ci	ep_ier &= ~imask;
5828c2ecf20Sopenharmony_ci	writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER);
5838c2ecf20Sopenharmony_ci	writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	/* Nuke all pending requests */
5868c2ecf20Sopenharmony_ci	ast_vhub_nuke(ep, -ESHUTDOWN);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	/* No more descriptor associated with request */
5898c2ecf20Sopenharmony_ci	ep->ep.desc = NULL;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vhub->lock, flags);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	return 0;
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic int ast_vhub_epn_enable(struct usb_ep* u_ep,
5978c2ecf20Sopenharmony_ci			       const struct usb_endpoint_descriptor *desc)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
6008c2ecf20Sopenharmony_ci	struct ast_vhub_dev *dev;
6018c2ecf20Sopenharmony_ci	struct ast_vhub *vhub;
6028c2ecf20Sopenharmony_ci	u16 maxpacket, type;
6038c2ecf20Sopenharmony_ci	unsigned long flags;
6048c2ecf20Sopenharmony_ci	u32 ep_conf, ep_ier, imask;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	/* Check arguments */
6078c2ecf20Sopenharmony_ci	if (!u_ep || !desc)
6088c2ecf20Sopenharmony_ci		return -EINVAL;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	maxpacket = usb_endpoint_maxp(desc);
6118c2ecf20Sopenharmony_ci	if (!ep->d_idx || !ep->dev ||
6128c2ecf20Sopenharmony_ci	    desc->bDescriptorType != USB_DT_ENDPOINT ||
6138c2ecf20Sopenharmony_ci	    maxpacket == 0 || maxpacket > ep->ep.maxpacket) {
6148c2ecf20Sopenharmony_ci		EPDBG(ep, "Invalid EP enable,d_idx=%d,dev=%p,type=%d,mp=%d/%d\n",
6158c2ecf20Sopenharmony_ci		      ep->d_idx, ep->dev, desc->bDescriptorType,
6168c2ecf20Sopenharmony_ci		      maxpacket, ep->ep.maxpacket);
6178c2ecf20Sopenharmony_ci		return -EINVAL;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci	if (ep->d_idx != usb_endpoint_num(desc)) {
6208c2ecf20Sopenharmony_ci		EPDBG(ep, "EP number mismatch !\n");
6218c2ecf20Sopenharmony_ci		return -EINVAL;
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (ep->epn.enabled) {
6258c2ecf20Sopenharmony_ci		EPDBG(ep, "Already enabled\n");
6268c2ecf20Sopenharmony_ci		return -EBUSY;
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci	dev = ep->dev;
6298c2ecf20Sopenharmony_ci	vhub = ep->vhub;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	/* Check device state */
6328c2ecf20Sopenharmony_ci	if (!dev->driver) {
6338c2ecf20Sopenharmony_ci		EPDBG(ep, "Bogus device state: driver=%p speed=%d\n",
6348c2ecf20Sopenharmony_ci		       dev->driver, dev->gadget.speed);
6358c2ecf20Sopenharmony_ci		return -ESHUTDOWN;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	/* Grab some info from the descriptor */
6398c2ecf20Sopenharmony_ci	ep->epn.is_in = usb_endpoint_dir_in(desc);
6408c2ecf20Sopenharmony_ci	ep->ep.maxpacket = maxpacket;
6418c2ecf20Sopenharmony_ci	type = usb_endpoint_type(desc);
6428c2ecf20Sopenharmony_ci	ep->epn.d_next = ep->epn.d_last = 0;
6438c2ecf20Sopenharmony_ci	ep->epn.is_iso = false;
6448c2ecf20Sopenharmony_ci	ep->epn.stalled = false;
6458c2ecf20Sopenharmony_ci	ep->epn.wedged = false;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	EPDBG(ep, "Enabling [%s] %s num %d maxpacket=%d\n",
6488c2ecf20Sopenharmony_ci	      ep->epn.is_in ? "in" : "out", usb_ep_type_string(type),
6498c2ecf20Sopenharmony_ci	      usb_endpoint_num(desc), maxpacket);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	/* Can we use DMA descriptor mode ? */
6528c2ecf20Sopenharmony_ci	ep->epn.desc_mode = ep->epn.descs && ep->epn.is_in;
6538c2ecf20Sopenharmony_ci	if (ep->epn.desc_mode)
6548c2ecf20Sopenharmony_ci		memset(ep->epn.descs, 0, 8 * AST_VHUB_DESCS_COUNT);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	/*
6578c2ecf20Sopenharmony_ci	 * Large send function can send up to 8 packets from
6588c2ecf20Sopenharmony_ci	 * one descriptor with a limit of 4095 bytes.
6598c2ecf20Sopenharmony_ci	 */
6608c2ecf20Sopenharmony_ci	ep->epn.chunk_max = ep->ep.maxpacket;
6618c2ecf20Sopenharmony_ci	if (ep->epn.is_in) {
6628c2ecf20Sopenharmony_ci		ep->epn.chunk_max <<= 3;
6638c2ecf20Sopenharmony_ci		while (ep->epn.chunk_max > 4095)
6648c2ecf20Sopenharmony_ci			ep->epn.chunk_max -= ep->ep.maxpacket;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	switch(type) {
6688c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_CONTROL:
6698c2ecf20Sopenharmony_ci		EPDBG(ep, "Only one control endpoint\n");
6708c2ecf20Sopenharmony_ci		return -EINVAL;
6718c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_INT:
6728c2ecf20Sopenharmony_ci		ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_INT);
6738c2ecf20Sopenharmony_ci		break;
6748c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_BULK:
6758c2ecf20Sopenharmony_ci		ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_BULK);
6768c2ecf20Sopenharmony_ci		break;
6778c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_ISOC:
6788c2ecf20Sopenharmony_ci		ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_ISO);
6798c2ecf20Sopenharmony_ci		ep->epn.is_iso = true;
6808c2ecf20Sopenharmony_ci		break;
6818c2ecf20Sopenharmony_ci	default:
6828c2ecf20Sopenharmony_ci		return -EINVAL;
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* Encode the rest of the EP config register */
6868c2ecf20Sopenharmony_ci	if (maxpacket < 1024)
6878c2ecf20Sopenharmony_ci		ep_conf |= VHUB_EP_CFG_SET_MAX_PKT(maxpacket);
6888c2ecf20Sopenharmony_ci	if (!ep->epn.is_in)
6898c2ecf20Sopenharmony_ci		ep_conf |= VHUB_EP_CFG_DIR_OUT;
6908c2ecf20Sopenharmony_ci	ep_conf |= VHUB_EP_CFG_SET_EP_NUM(usb_endpoint_num(desc));
6918c2ecf20Sopenharmony_ci	ep_conf |= VHUB_EP_CFG_ENABLE;
6928c2ecf20Sopenharmony_ci	ep_conf |= VHUB_EP_CFG_SET_DEV(dev->index + 1);
6938c2ecf20Sopenharmony_ci	EPVDBG(ep, "config=%08x\n", ep_conf);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vhub->lock, flags);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* Disable HW and reset DMA */
6988c2ecf20Sopenharmony_ci	writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG);
6998c2ecf20Sopenharmony_ci	writel(VHUB_EP_DMA_CTRL_RESET,
7008c2ecf20Sopenharmony_ci	       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	/* Configure and enable */
7038c2ecf20Sopenharmony_ci	writel(ep_conf, ep->epn.regs + AST_VHUB_EP_CONFIG);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (ep->epn.desc_mode) {
7068c2ecf20Sopenharmony_ci		/* Clear DMA status, including the DMA read ptr */
7078c2ecf20Sopenharmony_ci		writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci		/* Set descriptor base */
7108c2ecf20Sopenharmony_ci		writel(ep->epn.descs_dma,
7118c2ecf20Sopenharmony_ci		       ep->epn.regs + AST_VHUB_EP_DESC_BASE);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci		/* Set base DMA config value */
7148c2ecf20Sopenharmony_ci		ep->epn.dma_conf = VHUB_EP_DMA_DESC_MODE;
7158c2ecf20Sopenharmony_ci		if (ep->epn.is_in)
7168c2ecf20Sopenharmony_ci			ep->epn.dma_conf |= VHUB_EP_DMA_IN_LONG_MODE;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci		/* First reset and disable all operations */
7198c2ecf20Sopenharmony_ci		writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET,
7208c2ecf20Sopenharmony_ci		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci		/* Enable descriptor mode */
7238c2ecf20Sopenharmony_ci		writel(ep->epn.dma_conf,
7248c2ecf20Sopenharmony_ci		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
7258c2ecf20Sopenharmony_ci	} else {
7268c2ecf20Sopenharmony_ci		/* Set base DMA config value */
7278c2ecf20Sopenharmony_ci		ep->epn.dma_conf = VHUB_EP_DMA_SINGLE_STAGE;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci		/* Reset and switch to single stage mode */
7308c2ecf20Sopenharmony_ci		writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET,
7318c2ecf20Sopenharmony_ci		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
7328c2ecf20Sopenharmony_ci		writel(ep->epn.dma_conf,
7338c2ecf20Sopenharmony_ci		       ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
7348c2ecf20Sopenharmony_ci		writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	/* Cleanup data toggle just in case */
7388c2ecf20Sopenharmony_ci	writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx),
7398c2ecf20Sopenharmony_ci	       vhub->regs + AST_VHUB_EP_TOGGLE);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	/* Cleanup and enable ACK interrupt */
7428c2ecf20Sopenharmony_ci	imask = VHUB_EP_IRQ(ep->epn.g_idx);
7438c2ecf20Sopenharmony_ci	writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR);
7448c2ecf20Sopenharmony_ci	ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER);
7458c2ecf20Sopenharmony_ci	ep_ier |= imask;
7468c2ecf20Sopenharmony_ci	writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	/* Woot, we are online ! */
7498c2ecf20Sopenharmony_ci	ep->epn.enabled = true;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vhub->lock, flags);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	return 0;
7548c2ecf20Sopenharmony_ci}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_cistatic void ast_vhub_epn_dispose(struct usb_ep *u_ep)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	struct ast_vhub_ep *ep = to_ast_ep(u_ep);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	if (WARN_ON(!ep->dev || !ep->d_idx))
7618c2ecf20Sopenharmony_ci		return;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	EPDBG(ep, "Releasing endpoint\n");
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	/* Take it out of the EP list */
7668c2ecf20Sopenharmony_ci	list_del_init(&ep->ep.ep_list);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/* Mark the address free in the device */
7698c2ecf20Sopenharmony_ci	ep->dev->epns[ep->d_idx - 1] = NULL;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	/* Free name & DMA buffers */
7728c2ecf20Sopenharmony_ci	kfree(ep->ep.name);
7738c2ecf20Sopenharmony_ci	ep->ep.name = NULL;
7748c2ecf20Sopenharmony_ci	dma_free_coherent(&ep->vhub->pdev->dev,
7758c2ecf20Sopenharmony_ci			  AST_VHUB_EPn_MAX_PACKET +
7768c2ecf20Sopenharmony_ci			  8 * AST_VHUB_DESCS_COUNT,
7778c2ecf20Sopenharmony_ci			  ep->buf, ep->buf_dma);
7788c2ecf20Sopenharmony_ci	ep->buf = NULL;
7798c2ecf20Sopenharmony_ci	ep->epn.descs = NULL;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	/* Mark free */
7828c2ecf20Sopenharmony_ci	ep->dev = NULL;
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic const struct usb_ep_ops ast_vhub_epn_ops = {
7868c2ecf20Sopenharmony_ci	.enable		= ast_vhub_epn_enable,
7878c2ecf20Sopenharmony_ci	.disable	= ast_vhub_epn_disable,
7888c2ecf20Sopenharmony_ci	.dispose	= ast_vhub_epn_dispose,
7898c2ecf20Sopenharmony_ci	.queue		= ast_vhub_epn_queue,
7908c2ecf20Sopenharmony_ci	.dequeue	= ast_vhub_epn_dequeue,
7918c2ecf20Sopenharmony_ci	.set_halt	= ast_vhub_epn_set_halt,
7928c2ecf20Sopenharmony_ci	.set_wedge	= ast_vhub_epn_set_wedge,
7938c2ecf20Sopenharmony_ci	.alloc_request	= ast_vhub_alloc_request,
7948c2ecf20Sopenharmony_ci	.free_request	= ast_vhub_free_request,
7958c2ecf20Sopenharmony_ci};
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistruct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	struct ast_vhub *vhub = d->vhub;
8008c2ecf20Sopenharmony_ci	struct ast_vhub_ep *ep;
8018c2ecf20Sopenharmony_ci	unsigned long flags;
8028c2ecf20Sopenharmony_ci	int i;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	/* Find a free one (no device) */
8058c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vhub->lock, flags);
8068c2ecf20Sopenharmony_ci	for (i = 0; i < vhub->max_epns; i++)
8078c2ecf20Sopenharmony_ci		if (vhub->epns[i].dev == NULL)
8088c2ecf20Sopenharmony_ci			break;
8098c2ecf20Sopenharmony_ci	if (i >= vhub->max_epns) {
8108c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vhub->lock, flags);
8118c2ecf20Sopenharmony_ci		return NULL;
8128c2ecf20Sopenharmony_ci	}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	/* Set it up */
8158c2ecf20Sopenharmony_ci	ep = &vhub->epns[i];
8168c2ecf20Sopenharmony_ci	ep->dev = d;
8178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vhub->lock, flags);
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	DDBG(d, "Allocating gen EP %d for addr %d\n", i, addr);
8208c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ep->queue);
8218c2ecf20Sopenharmony_ci	ep->d_idx = addr;
8228c2ecf20Sopenharmony_ci	ep->vhub = vhub;
8238c2ecf20Sopenharmony_ci	ep->ep.ops = &ast_vhub_epn_ops;
8248c2ecf20Sopenharmony_ci	ep->ep.name = kasprintf(GFP_KERNEL, "ep%d", addr);
8258c2ecf20Sopenharmony_ci	d->epns[addr-1] = ep;
8268c2ecf20Sopenharmony_ci	ep->epn.g_idx = i;
8278c2ecf20Sopenharmony_ci	ep->epn.regs = vhub->regs + 0x200 + (i * 0x10);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	ep->buf = dma_alloc_coherent(&vhub->pdev->dev,
8308c2ecf20Sopenharmony_ci				     AST_VHUB_EPn_MAX_PACKET +
8318c2ecf20Sopenharmony_ci				     8 * AST_VHUB_DESCS_COUNT,
8328c2ecf20Sopenharmony_ci				     &ep->buf_dma, GFP_KERNEL);
8338c2ecf20Sopenharmony_ci	if (!ep->buf) {
8348c2ecf20Sopenharmony_ci		kfree(ep->ep.name);
8358c2ecf20Sopenharmony_ci		ep->ep.name = NULL;
8368c2ecf20Sopenharmony_ci		return NULL;
8378c2ecf20Sopenharmony_ci	}
8388c2ecf20Sopenharmony_ci	ep->epn.descs = ep->buf + AST_VHUB_EPn_MAX_PACKET;
8398c2ecf20Sopenharmony_ci	ep->epn.descs_dma = ep->buf_dma + AST_VHUB_EPn_MAX_PACKET;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EPn_MAX_PACKET);
8428c2ecf20Sopenharmony_ci	list_add_tail(&ep->ep.ep_list, &d->gadget.ep_list);
8438c2ecf20Sopenharmony_ci	ep->ep.caps.type_iso = true;
8448c2ecf20Sopenharmony_ci	ep->ep.caps.type_bulk = true;
8458c2ecf20Sopenharmony_ci	ep->ep.caps.type_int = true;
8468c2ecf20Sopenharmony_ci	ep->ep.caps.dir_in = true;
8478c2ecf20Sopenharmony_ci	ep->ep.caps.dir_out = true;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	return ep;
8508c2ecf20Sopenharmony_ci}
851