18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Cadence USBSS DRD Driver - gadget side.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2018-2019 Cadence Design Systems.
68c2ecf20Sopenharmony_ci * Copyright (C) 2017-2018 NXP
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Authors: Pawel Jez <pjez@cadence.com>,
98c2ecf20Sopenharmony_ci *          Pawel Laszczak <pawell@cadence.com>
108c2ecf20Sopenharmony_ci *          Peter Chen <peter.chen@nxp.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * Work around 1:
158c2ecf20Sopenharmony_ci * At some situations, the controller may get stale data address in TRB
168c2ecf20Sopenharmony_ci * at below sequences:
178c2ecf20Sopenharmony_ci * 1. Controller read TRB includes data address
188c2ecf20Sopenharmony_ci * 2. Software updates TRBs includes data address and Cycle bit
198c2ecf20Sopenharmony_ci * 3. Controller read TRB which includes Cycle bit
208c2ecf20Sopenharmony_ci * 4. DMA run with stale data address
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * To fix this problem, driver needs to make the first TRB in TD as invalid.
238c2ecf20Sopenharmony_ci * After preparing all TRBs driver needs to check the position of DMA and
248c2ecf20Sopenharmony_ci * if the DMA point to the first just added TRB and doorbell is 1,
258c2ecf20Sopenharmony_ci * then driver must defer making this TRB as valid. This TRB will be make
268c2ecf20Sopenharmony_ci * as valid during adding next TRB only if DMA is stopped or at TRBERR
278c2ecf20Sopenharmony_ci * interrupt.
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * Issue has been fixed in DEV_VER_V3 version of controller.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Work around 2:
328c2ecf20Sopenharmony_ci * Controller for OUT endpoints has shared on-chip buffers for all incoming
338c2ecf20Sopenharmony_ci * packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
348c2ecf20Sopenharmony_ci * in correct order. If the first packet in the buffer will not be handled,
358c2ecf20Sopenharmony_ci * then the following packets directed for other endpoints and  functions
368c2ecf20Sopenharmony_ci * will be blocked.
378c2ecf20Sopenharmony_ci * Additionally the packets directed to one endpoint can block entire on-chip
388c2ecf20Sopenharmony_ci * buffers. In this case transfer to other endpoints also will blocked.
398c2ecf20Sopenharmony_ci *
408c2ecf20Sopenharmony_ci * To resolve this issue after raising the descriptor missing interrupt
418c2ecf20Sopenharmony_ci * driver prepares internal usb_request object and use it to arm DMA transfer.
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * The problematic situation was observed in case when endpoint has been enabled
448c2ecf20Sopenharmony_ci * but no usb_request were queued. Driver try detects such endpoints and will
458c2ecf20Sopenharmony_ci * use this workaround only for these endpoint.
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * Driver use limited number of buffer. This number can be set by macro
488c2ecf20Sopenharmony_ci * CDNS3_WA2_NUM_BUFFERS.
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * Such blocking situation was observed on ACM gadget. For this function
518c2ecf20Sopenharmony_ci * host send OUT data packet but ACM function is not prepared for this packet.
528c2ecf20Sopenharmony_ci * It's cause that buffer placed in on chip memory block transfer to other
538c2ecf20Sopenharmony_ci * endpoints.
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * Issue has been fixed in DEV_VER_V2 version of controller.
568c2ecf20Sopenharmony_ci *
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
608c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h>
618c2ecf20Sopenharmony_ci#include <linux/module.h>
628c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#include "core.h"
658c2ecf20Sopenharmony_ci#include "gadget-export.h"
668c2ecf20Sopenharmony_ci#include "gadget.h"
678c2ecf20Sopenharmony_ci#include "trace.h"
688c2ecf20Sopenharmony_ci#include "drd.h"
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int __cdns3_gadget_ep_queue(struct usb_ep *ep,
718c2ecf20Sopenharmony_ci				   struct usb_request *request,
728c2ecf20Sopenharmony_ci				   gfp_t gfp_flags);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
758c2ecf20Sopenharmony_ci				 struct usb_request *request);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep,
788c2ecf20Sopenharmony_ci					struct usb_request *request);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/**
818c2ecf20Sopenharmony_ci * cdns3_clear_register_bit - clear bit in given register.
828c2ecf20Sopenharmony_ci * @ptr: address of device controller register to be read and changed
838c2ecf20Sopenharmony_ci * @mask: bits requested to clar
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_cistatic void cdns3_clear_register_bit(void __iomem *ptr, u32 mask)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	mask = readl(ptr) & ~mask;
888c2ecf20Sopenharmony_ci	writel(mask, ptr);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/**
928c2ecf20Sopenharmony_ci * cdns3_set_register_bit - set bit in given register.
938c2ecf20Sopenharmony_ci * @ptr: address of device controller register to be read and changed
948c2ecf20Sopenharmony_ci * @mask: bits requested to set
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_civoid cdns3_set_register_bit(void __iomem *ptr, u32 mask)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	mask = readl(ptr) | mask;
998c2ecf20Sopenharmony_ci	writel(mask, ptr);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/**
1038c2ecf20Sopenharmony_ci * cdns3_ep_addr_to_index - Macro converts endpoint address to
1048c2ecf20Sopenharmony_ci * index of endpoint object in cdns3_device.eps[] container
1058c2ecf20Sopenharmony_ci * @ep_addr: endpoint address for which endpoint object is required
1068c2ecf20Sopenharmony_ci *
1078c2ecf20Sopenharmony_ci */
1088c2ecf20Sopenharmony_ciu8 cdns3_ep_addr_to_index(u8 ep_addr)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int cdns3_get_dma_pos(struct cdns3_device *priv_dev,
1148c2ecf20Sopenharmony_ci			     struct cdns3_endpoint *priv_ep)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	int dma_index;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	dma_index = readl(&priv_dev->regs->ep_traddr) - priv_ep->trb_pool_dma;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return dma_index / TRB_SIZE;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/**
1248c2ecf20Sopenharmony_ci * cdns3_next_request - returns next request from list
1258c2ecf20Sopenharmony_ci * @list: list containing requests
1268c2ecf20Sopenharmony_ci *
1278c2ecf20Sopenharmony_ci * Returns request or NULL if no requests in list
1288c2ecf20Sopenharmony_ci */
1298c2ecf20Sopenharmony_cistruct usb_request *cdns3_next_request(struct list_head *list)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	return list_first_entry_or_null(list, struct usb_request, list);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/**
1358c2ecf20Sopenharmony_ci * cdns3_next_align_buf - returns next buffer from list
1368c2ecf20Sopenharmony_ci * @list: list containing buffers
1378c2ecf20Sopenharmony_ci *
1388c2ecf20Sopenharmony_ci * Returns buffer or NULL if no buffers in list
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_cistatic struct cdns3_aligned_buf *cdns3_next_align_buf(struct list_head *list)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	return list_first_entry_or_null(list, struct cdns3_aligned_buf, list);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/**
1468c2ecf20Sopenharmony_ci * cdns3_next_priv_request - returns next request from list
1478c2ecf20Sopenharmony_ci * @list: list containing requests
1488c2ecf20Sopenharmony_ci *
1498c2ecf20Sopenharmony_ci * Returns request or NULL if no requests in list
1508c2ecf20Sopenharmony_ci */
1518c2ecf20Sopenharmony_cistatic struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	return list_first_entry_or_null(list, struct cdns3_request, list);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/**
1578c2ecf20Sopenharmony_ci * select_ep - selects endpoint
1588c2ecf20Sopenharmony_ci * @priv_dev:  extended gadget object
1598c2ecf20Sopenharmony_ci * @ep: endpoint address
1608c2ecf20Sopenharmony_ci */
1618c2ecf20Sopenharmony_civoid cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	if (priv_dev->selected_ep == ep)
1648c2ecf20Sopenharmony_ci		return;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	priv_dev->selected_ep = ep;
1678c2ecf20Sopenharmony_ci	writel(ep, &priv_dev->regs->ep_sel);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/**
1718c2ecf20Sopenharmony_ci * cdns3_get_tdl - gets current tdl for selected endpoint.
1728c2ecf20Sopenharmony_ci * @priv_dev:  extended gadget object
1738c2ecf20Sopenharmony_ci *
1748c2ecf20Sopenharmony_ci * Before calling this function the appropriate endpoint must
1758c2ecf20Sopenharmony_ci * be selected by means of cdns3_select_ep function.
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_cistatic int cdns3_get_tdl(struct cdns3_device *priv_dev)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver < DEV_VER_V3)
1808c2ecf20Sopenharmony_ci		return EP_CMD_TDL_GET(readl(&priv_dev->regs->ep_cmd));
1818c2ecf20Sopenharmony_ci	else
1828c2ecf20Sopenharmony_ci		return readl(&priv_dev->regs->ep_tdl);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cidma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
1868c2ecf20Sopenharmony_ci				 struct cdns3_trb *trb)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	u32 offset = (char *)trb - (char *)priv_ep->trb_pool;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return priv_ep->trb_pool_dma + offset;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	switch (priv_ep->type) {
1968c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_ISOC:
1978c2ecf20Sopenharmony_ci		return TRB_ISO_RING_SIZE;
1988c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_CONTROL:
1998c2ecf20Sopenharmony_ci		return TRB_CTRL_RING_SIZE;
2008c2ecf20Sopenharmony_ci	default:
2018c2ecf20Sopenharmony_ci		if (priv_ep->use_streams)
2028c2ecf20Sopenharmony_ci			return TRB_STREAM_RING_SIZE;
2038c2ecf20Sopenharmony_ci		else
2048c2ecf20Sopenharmony_ci			return TRB_RING_SIZE;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (priv_ep->trb_pool) {
2138c2ecf20Sopenharmony_ci		dma_free_coherent(priv_dev->sysdev,
2148c2ecf20Sopenharmony_ci				  cdns3_ring_size(priv_ep),
2158c2ecf20Sopenharmony_ci				  priv_ep->trb_pool, priv_ep->trb_pool_dma);
2168c2ecf20Sopenharmony_ci		priv_ep->trb_pool = NULL;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci/**
2218c2ecf20Sopenharmony_ci * cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint
2228c2ecf20Sopenharmony_ci * @priv_ep:  endpoint object
2238c2ecf20Sopenharmony_ci *
2248c2ecf20Sopenharmony_ci * Function will return 0 on success or -ENOMEM on allocation error
2258c2ecf20Sopenharmony_ci */
2268c2ecf20Sopenharmony_ciint cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
2298c2ecf20Sopenharmony_ci	int ring_size = cdns3_ring_size(priv_ep);
2308c2ecf20Sopenharmony_ci	int num_trbs = ring_size / TRB_SIZE;
2318c2ecf20Sopenharmony_ci	struct cdns3_trb *link_trb;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (priv_ep->trb_pool && priv_ep->alloc_ring_size < ring_size)
2348c2ecf20Sopenharmony_ci		cdns3_free_trb_pool(priv_ep);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (!priv_ep->trb_pool) {
2378c2ecf20Sopenharmony_ci		priv_ep->trb_pool = dma_alloc_coherent(priv_dev->sysdev,
2388c2ecf20Sopenharmony_ci						       ring_size,
2398c2ecf20Sopenharmony_ci						       &priv_ep->trb_pool_dma,
2408c2ecf20Sopenharmony_ci						       GFP_DMA32 | GFP_ATOMIC);
2418c2ecf20Sopenharmony_ci		if (!priv_ep->trb_pool)
2428c2ecf20Sopenharmony_ci			return -ENOMEM;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		priv_ep->alloc_ring_size = ring_size;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	memset(priv_ep->trb_pool, 0, ring_size);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	priv_ep->num_trbs = num_trbs;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (!priv_ep->num)
2528c2ecf20Sopenharmony_ci		return 0;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* Initialize the last TRB as Link TRB */
2558c2ecf20Sopenharmony_ci	link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (priv_ep->use_streams) {
2588c2ecf20Sopenharmony_ci		/*
2598c2ecf20Sopenharmony_ci		 * For stream capable endpoints driver use single correct TRB.
2608c2ecf20Sopenharmony_ci		 * The last trb has zeroed cycle bit
2618c2ecf20Sopenharmony_ci		 */
2628c2ecf20Sopenharmony_ci		link_trb->control = 0;
2638c2ecf20Sopenharmony_ci	} else {
2648c2ecf20Sopenharmony_ci		link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma));
2658c2ecf20Sopenharmony_ci		link_trb->control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE);
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci	return 0;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci/**
2718c2ecf20Sopenharmony_ci * cdns3_ep_stall_flush - Stalls and flushes selected endpoint
2728c2ecf20Sopenharmony_ci * @priv_ep: endpoint object
2738c2ecf20Sopenharmony_ci *
2748c2ecf20Sopenharmony_ci * Endpoint must be selected before call to this function
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_cistatic void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
2798c2ecf20Sopenharmony_ci	int val;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	trace_cdns3_halt(priv_ep, 1, 1);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
2848c2ecf20Sopenharmony_ci	       &priv_dev->regs->ep_cmd);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/* wait for DFLUSH cleared */
2878c2ecf20Sopenharmony_ci	readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
2888c2ecf20Sopenharmony_ci				  !(val & EP_CMD_DFLUSH), 1, 1000);
2898c2ecf20Sopenharmony_ci	priv_ep->flags |= EP_STALLED;
2908c2ecf20Sopenharmony_ci	priv_ep->flags &= ~EP_STALL_PENDING;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci/**
2948c2ecf20Sopenharmony_ci * cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller.
2958c2ecf20Sopenharmony_ci * @priv_dev: extended gadget object
2968c2ecf20Sopenharmony_ci */
2978c2ecf20Sopenharmony_civoid cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	int i;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	cdns3_allow_enable_l1(priv_dev, 0);
3048c2ecf20Sopenharmony_ci	priv_dev->hw_configured_flag = 0;
3058c2ecf20Sopenharmony_ci	priv_dev->onchip_used_size = 0;
3068c2ecf20Sopenharmony_ci	priv_dev->out_mem_is_allocated = 0;
3078c2ecf20Sopenharmony_ci	priv_dev->wait_for_setup = 0;
3088c2ecf20Sopenharmony_ci	priv_dev->using_streams = 0;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
3118c2ecf20Sopenharmony_ci		if (priv_dev->eps[i])
3128c2ecf20Sopenharmony_ci			priv_dev->eps[i]->flags &= ~EP_CONFIGURED;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci/**
3168c2ecf20Sopenharmony_ci * cdns3_ep_inc_trb - increment a trb index.
3178c2ecf20Sopenharmony_ci * @index: Pointer to the TRB index to increment.
3188c2ecf20Sopenharmony_ci * @cs: Cycle state
3198c2ecf20Sopenharmony_ci * @trb_in_seg: number of TRBs in segment
3208c2ecf20Sopenharmony_ci *
3218c2ecf20Sopenharmony_ci * The index should never point to the link TRB. After incrementing,
3228c2ecf20Sopenharmony_ci * if it is point to the link TRB, wrap around to the beginning and revert
3238c2ecf20Sopenharmony_ci * cycle state bit The
3248c2ecf20Sopenharmony_ci * link TRB is always at the last TRB entry.
3258c2ecf20Sopenharmony_ci */
3268c2ecf20Sopenharmony_cistatic void cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	(*index)++;
3298c2ecf20Sopenharmony_ci	if (*index == (trb_in_seg - 1)) {
3308c2ecf20Sopenharmony_ci		*index = 0;
3318c2ecf20Sopenharmony_ci		*cs ^=  1;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci/**
3368c2ecf20Sopenharmony_ci * cdns3_ep_inc_enq - increment endpoint's enqueue pointer
3378c2ecf20Sopenharmony_ci * @priv_ep: The endpoint whose enqueue pointer we're incrementing
3388c2ecf20Sopenharmony_ci */
3398c2ecf20Sopenharmony_cistatic void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	priv_ep->free_trbs--;
3428c2ecf20Sopenharmony_ci	cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs);
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci/**
3468c2ecf20Sopenharmony_ci * cdns3_ep_inc_deq - increment endpoint's dequeue pointer
3478c2ecf20Sopenharmony_ci * @priv_ep: The endpoint whose dequeue pointer we're incrementing
3488c2ecf20Sopenharmony_ci */
3498c2ecf20Sopenharmony_cistatic void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	priv_ep->free_trbs++;
3528c2ecf20Sopenharmony_ci	cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/**
3568c2ecf20Sopenharmony_ci * cdns3_allow_enable_l1 - enable/disable permits to transition to L1.
3578c2ecf20Sopenharmony_ci * @priv_dev: Extended gadget object
3588c2ecf20Sopenharmony_ci * @enable: Enable/disable permit to transition to L1.
3598c2ecf20Sopenharmony_ci *
3608c2ecf20Sopenharmony_ci * If bit USB_CONF_L1EN is set and device receive Extended Token packet,
3618c2ecf20Sopenharmony_ci * then controller answer with ACK handshake.
3628c2ecf20Sopenharmony_ci * If bit USB_CONF_L1DS is set and device receive Extended Token packet,
3638c2ecf20Sopenharmony_ci * then controller answer with NYET handshake.
3648c2ecf20Sopenharmony_ci */
3658c2ecf20Sopenharmony_civoid cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	if (enable)
3688c2ecf20Sopenharmony_ci		writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf);
3698c2ecf20Sopenharmony_ci	else
3708c2ecf20Sopenharmony_ci		writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cienum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	u32 reg;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	reg = readl(&priv_dev->regs->usb_sts);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (DEV_SUPERSPEED(reg))
3808c2ecf20Sopenharmony_ci		return USB_SPEED_SUPER;
3818c2ecf20Sopenharmony_ci	else if (DEV_HIGHSPEED(reg))
3828c2ecf20Sopenharmony_ci		return USB_SPEED_HIGH;
3838c2ecf20Sopenharmony_ci	else if (DEV_FULLSPEED(reg))
3848c2ecf20Sopenharmony_ci		return USB_SPEED_FULL;
3858c2ecf20Sopenharmony_ci	else if (DEV_LOWSPEED(reg))
3868c2ecf20Sopenharmony_ci		return USB_SPEED_LOW;
3878c2ecf20Sopenharmony_ci	return USB_SPEED_UNKNOWN;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci/**
3918c2ecf20Sopenharmony_ci * cdns3_start_all_request - add to ring all request not started
3928c2ecf20Sopenharmony_ci * @priv_dev: Extended gadget object
3938c2ecf20Sopenharmony_ci * @priv_ep: The endpoint for whom request will be started.
3948c2ecf20Sopenharmony_ci *
3958c2ecf20Sopenharmony_ci * Returns return ENOMEM if transfer ring i not enough TRBs to start
3968c2ecf20Sopenharmony_ci *         all requests.
3978c2ecf20Sopenharmony_ci */
3988c2ecf20Sopenharmony_cistatic int cdns3_start_all_request(struct cdns3_device *priv_dev,
3998c2ecf20Sopenharmony_ci				   struct cdns3_endpoint *priv_ep)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct usb_request *request;
4028c2ecf20Sopenharmony_ci	int ret = 0;
4038c2ecf20Sopenharmony_ci	u8 pending_empty = list_empty(&priv_ep->pending_req_list);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/*
4068c2ecf20Sopenharmony_ci	 * If the last pending transfer is INTERNAL
4078c2ecf20Sopenharmony_ci	 * OR streams are enabled for this endpoint
4088c2ecf20Sopenharmony_ci	 * do NOT start new transfer till the last one is pending
4098c2ecf20Sopenharmony_ci	 */
4108c2ecf20Sopenharmony_ci	if (!pending_empty) {
4118c2ecf20Sopenharmony_ci		struct cdns3_request *priv_req;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		request = cdns3_next_request(&priv_ep->pending_req_list);
4148c2ecf20Sopenharmony_ci		priv_req = to_cdns3_request(request);
4158c2ecf20Sopenharmony_ci		if ((priv_req->flags & REQUEST_INTERNAL) ||
4168c2ecf20Sopenharmony_ci		    (priv_ep->flags & EP_TDLCHK_EN) ||
4178c2ecf20Sopenharmony_ci			priv_ep->use_streams) {
4188c2ecf20Sopenharmony_ci			dev_dbg(priv_dev->dev, "Blocking external request\n");
4198c2ecf20Sopenharmony_ci			return ret;
4208c2ecf20Sopenharmony_ci		}
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	while (!list_empty(&priv_ep->deferred_req_list)) {
4248c2ecf20Sopenharmony_ci		request = cdns3_next_request(&priv_ep->deferred_req_list);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		if (!priv_ep->use_streams) {
4278c2ecf20Sopenharmony_ci			ret = cdns3_ep_run_transfer(priv_ep, request);
4288c2ecf20Sopenharmony_ci		} else {
4298c2ecf20Sopenharmony_ci			priv_ep->stream_sg_idx = 0;
4308c2ecf20Sopenharmony_ci			ret = cdns3_ep_run_stream_transfer(priv_ep, request);
4318c2ecf20Sopenharmony_ci		}
4328c2ecf20Sopenharmony_ci		if (ret)
4338c2ecf20Sopenharmony_ci			return ret;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		list_del(&request->list);
4368c2ecf20Sopenharmony_ci		list_add_tail(&request->list,
4378c2ecf20Sopenharmony_ci			      &priv_ep->pending_req_list);
4388c2ecf20Sopenharmony_ci		if (request->stream_id != 0 || (priv_ep->flags & EP_TDLCHK_EN))
4398c2ecf20Sopenharmony_ci			break;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	priv_ep->flags &= ~EP_RING_FULL;
4438c2ecf20Sopenharmony_ci	return ret;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci/*
4478c2ecf20Sopenharmony_ci * WA2: Set flag for all not ISOC OUT endpoints. If this flag is set
4488c2ecf20Sopenharmony_ci * driver try to detect whether endpoint need additional internal
4498c2ecf20Sopenharmony_ci * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
4508c2ecf20Sopenharmony_ci * if before first DESCMISS interrupt the DMA will be armed.
4518c2ecf20Sopenharmony_ci */
4528c2ecf20Sopenharmony_ci#define cdns3_wa2_enable_detection(priv_dev, priv_ep, reg) do { \
4538c2ecf20Sopenharmony_ci	if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) { \
4548c2ecf20Sopenharmony_ci		priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET; \
4558c2ecf20Sopenharmony_ci		(reg) |= EP_STS_EN_DESCMISEN; \
4568c2ecf20Sopenharmony_ci	} } while (0)
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic void __cdns3_descmiss_copy_data(struct usb_request *request,
4598c2ecf20Sopenharmony_ci	struct usb_request *descmiss_req)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	int length = request->actual + descmiss_req->actual;
4628c2ecf20Sopenharmony_ci	struct scatterlist *s = request->sg;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	if (!s) {
4658c2ecf20Sopenharmony_ci		if (length <= request->length) {
4668c2ecf20Sopenharmony_ci			memcpy(&((u8 *)request->buf)[request->actual],
4678c2ecf20Sopenharmony_ci			       descmiss_req->buf,
4688c2ecf20Sopenharmony_ci			       descmiss_req->actual);
4698c2ecf20Sopenharmony_ci			request->actual = length;
4708c2ecf20Sopenharmony_ci		} else {
4718c2ecf20Sopenharmony_ci			/* It should never occures */
4728c2ecf20Sopenharmony_ci			request->status = -ENOMEM;
4738c2ecf20Sopenharmony_ci		}
4748c2ecf20Sopenharmony_ci	} else {
4758c2ecf20Sopenharmony_ci		if (length <= sg_dma_len(s)) {
4768c2ecf20Sopenharmony_ci			void *p = phys_to_virt(sg_dma_address(s));
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci			memcpy(&((u8 *)p)[request->actual],
4798c2ecf20Sopenharmony_ci				descmiss_req->buf,
4808c2ecf20Sopenharmony_ci				descmiss_req->actual);
4818c2ecf20Sopenharmony_ci			request->actual = length;
4828c2ecf20Sopenharmony_ci		} else {
4838c2ecf20Sopenharmony_ci			request->status = -ENOMEM;
4848c2ecf20Sopenharmony_ci		}
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci/**
4898c2ecf20Sopenharmony_ci * cdns3_wa2_descmiss_copy_data copy data from internal requests to
4908c2ecf20Sopenharmony_ci * request queued by class driver.
4918c2ecf20Sopenharmony_ci * @priv_ep: extended endpoint object
4928c2ecf20Sopenharmony_ci * @request: request object
4938c2ecf20Sopenharmony_ci */
4948c2ecf20Sopenharmony_cistatic void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
4958c2ecf20Sopenharmony_ci					 struct usb_request *request)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	struct usb_request *descmiss_req;
4988c2ecf20Sopenharmony_ci	struct cdns3_request *descmiss_priv_req;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	while (!list_empty(&priv_ep->wa2_descmiss_req_list)) {
5018c2ecf20Sopenharmony_ci		int chunk_end;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci		descmiss_priv_req =
5048c2ecf20Sopenharmony_ci			cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list);
5058c2ecf20Sopenharmony_ci		descmiss_req = &descmiss_priv_req->request;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci		/* driver can't touch pending request */
5088c2ecf20Sopenharmony_ci		if (descmiss_priv_req->flags & REQUEST_PENDING)
5098c2ecf20Sopenharmony_ci			break;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci		chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
5128c2ecf20Sopenharmony_ci		request->status = descmiss_req->status;
5138c2ecf20Sopenharmony_ci		__cdns3_descmiss_copy_data(request, descmiss_req);
5148c2ecf20Sopenharmony_ci		list_del_init(&descmiss_priv_req->list);
5158c2ecf20Sopenharmony_ci		kfree(descmiss_req->buf);
5168c2ecf20Sopenharmony_ci		cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
5178c2ecf20Sopenharmony_ci		--priv_ep->wa2_counter;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci		if (!chunk_end)
5208c2ecf20Sopenharmony_ci			break;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cistatic struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
5258c2ecf20Sopenharmony_ci						     struct cdns3_endpoint *priv_ep,
5268c2ecf20Sopenharmony_ci						     struct cdns3_request *priv_req)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
5298c2ecf20Sopenharmony_ci	    priv_req->flags & REQUEST_INTERNAL) {
5308c2ecf20Sopenharmony_ci		struct usb_request *req;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		req = cdns3_next_request(&priv_ep->deferred_req_list);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci		priv_ep->descmis_req = NULL;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		if (!req)
5378c2ecf20Sopenharmony_ci			return NULL;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		/* unmap the gadget request before copying data */
5408c2ecf20Sopenharmony_ci		usb_gadget_unmap_request_by_dev(priv_dev->sysdev, req,
5418c2ecf20Sopenharmony_ci						priv_ep->dir);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci		cdns3_wa2_descmiss_copy_data(priv_ep, req);
5448c2ecf20Sopenharmony_ci		if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
5458c2ecf20Sopenharmony_ci		    req->length != req->actual) {
5468c2ecf20Sopenharmony_ci			/* wait for next part of transfer */
5478c2ecf20Sopenharmony_ci			/* re-map the gadget request buffer*/
5488c2ecf20Sopenharmony_ci			usb_gadget_map_request_by_dev(priv_dev->sysdev, req,
5498c2ecf20Sopenharmony_ci				usb_endpoint_dir_in(priv_ep->endpoint.desc));
5508c2ecf20Sopenharmony_ci			return NULL;
5518c2ecf20Sopenharmony_ci		}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci		if (req->status == -EINPROGRESS)
5548c2ecf20Sopenharmony_ci			req->status = 0;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci		list_del_init(&req->list);
5578c2ecf20Sopenharmony_ci		cdns3_start_all_request(priv_dev, priv_ep);
5588c2ecf20Sopenharmony_ci		return req;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	return &priv_req->request;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
5658c2ecf20Sopenharmony_ci				     struct cdns3_endpoint *priv_ep,
5668c2ecf20Sopenharmony_ci				     struct cdns3_request *priv_req)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	int deferred = 0;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/*
5718c2ecf20Sopenharmony_ci	 * If transfer was queued before DESCMISS appear than we
5728c2ecf20Sopenharmony_ci	 * can disable handling of DESCMISS interrupt. Driver assumes that it
5738c2ecf20Sopenharmony_ci	 * can disable special treatment for this endpoint.
5748c2ecf20Sopenharmony_ci	 */
5758c2ecf20Sopenharmony_ci	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
5768c2ecf20Sopenharmony_ci		u32 reg;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci		cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
5798c2ecf20Sopenharmony_ci		priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
5808c2ecf20Sopenharmony_ci		reg = readl(&priv_dev->regs->ep_sts_en);
5818c2ecf20Sopenharmony_ci		reg &= ~EP_STS_EN_DESCMISEN;
5828c2ecf20Sopenharmony_ci		trace_cdns3_wa2(priv_ep, "workaround disabled\n");
5838c2ecf20Sopenharmony_ci		writel(reg, &priv_dev->regs->ep_sts_en);
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
5878c2ecf20Sopenharmony_ci		u8 pending_empty = list_empty(&priv_ep->pending_req_list);
5888c2ecf20Sopenharmony_ci		u8 descmiss_empty = list_empty(&priv_ep->wa2_descmiss_req_list);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci		/*
5918c2ecf20Sopenharmony_ci		 *  DESCMISS transfer has been finished, so data will be
5928c2ecf20Sopenharmony_ci		 *  directly copied from internal allocated usb_request
5938c2ecf20Sopenharmony_ci		 *  objects.
5948c2ecf20Sopenharmony_ci		 */
5958c2ecf20Sopenharmony_ci		if (pending_empty && !descmiss_empty &&
5968c2ecf20Sopenharmony_ci		    !(priv_req->flags & REQUEST_INTERNAL)) {
5978c2ecf20Sopenharmony_ci			cdns3_wa2_descmiss_copy_data(priv_ep,
5988c2ecf20Sopenharmony_ci						     &priv_req->request);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci			trace_cdns3_wa2(priv_ep, "get internal stored data");
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci			list_add_tail(&priv_req->request.list,
6038c2ecf20Sopenharmony_ci				      &priv_ep->pending_req_list);
6048c2ecf20Sopenharmony_ci			cdns3_gadget_giveback(priv_ep, priv_req,
6058c2ecf20Sopenharmony_ci					      priv_req->request.status);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci			/*
6088c2ecf20Sopenharmony_ci			 * Intentionally driver returns positive value as
6098c2ecf20Sopenharmony_ci			 * correct value. It informs that transfer has
6108c2ecf20Sopenharmony_ci			 * been finished.
6118c2ecf20Sopenharmony_ci			 */
6128c2ecf20Sopenharmony_ci			return EINPROGRESS;
6138c2ecf20Sopenharmony_ci		}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci		/*
6168c2ecf20Sopenharmony_ci		 * Driver will wait for completion DESCMISS transfer,
6178c2ecf20Sopenharmony_ci		 * before starts new, not DESCMISS transfer.
6188c2ecf20Sopenharmony_ci		 */
6198c2ecf20Sopenharmony_ci		if (!pending_empty && !descmiss_empty) {
6208c2ecf20Sopenharmony_ci			trace_cdns3_wa2(priv_ep, "wait for pending transfer\n");
6218c2ecf20Sopenharmony_ci			deferred = 1;
6228c2ecf20Sopenharmony_ci		}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci		if (priv_req->flags & REQUEST_INTERNAL)
6258c2ecf20Sopenharmony_ci			list_add_tail(&priv_req->list,
6268c2ecf20Sopenharmony_ci				      &priv_ep->wa2_descmiss_req_list);
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	return deferred;
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cistatic void cdns3_wa2_remove_old_request(struct cdns3_endpoint *priv_ep)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	struct cdns3_request *priv_req;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	while (!list_empty(&priv_ep->wa2_descmiss_req_list)) {
6378c2ecf20Sopenharmony_ci		u8 chain;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list);
6408c2ecf20Sopenharmony_ci		chain = !!(priv_req->flags & REQUEST_INTERNAL_CH);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		trace_cdns3_wa2(priv_ep, "removes eldest request");
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci		kfree(priv_req->request.buf);
6458c2ecf20Sopenharmony_ci		list_del_init(&priv_req->list);
6468c2ecf20Sopenharmony_ci		cdns3_gadget_ep_free_request(&priv_ep->endpoint,
6478c2ecf20Sopenharmony_ci					     &priv_req->request);
6488c2ecf20Sopenharmony_ci		--priv_ep->wa2_counter;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci		if (!chain)
6518c2ecf20Sopenharmony_ci			break;
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci/**
6568c2ecf20Sopenharmony_ci * cdns3_wa2_descmissing_packet - handles descriptor missing event.
6578c2ecf20Sopenharmony_ci * @priv_ep: extended gadget object
6588c2ecf20Sopenharmony_ci *
6598c2ecf20Sopenharmony_ci * This function is used only for WA2. For more information see Work around 2
6608c2ecf20Sopenharmony_ci * description.
6618c2ecf20Sopenharmony_ci */
6628c2ecf20Sopenharmony_cistatic void cdns3_wa2_descmissing_packet(struct cdns3_endpoint *priv_ep)
6638c2ecf20Sopenharmony_ci{
6648c2ecf20Sopenharmony_ci	struct cdns3_request *priv_req;
6658c2ecf20Sopenharmony_ci	struct usb_request *request;
6668c2ecf20Sopenharmony_ci	u8 pending_empty = list_empty(&priv_ep->pending_req_list);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* check for pending transfer */
6698c2ecf20Sopenharmony_ci	if (!pending_empty) {
6708c2ecf20Sopenharmony_ci		trace_cdns3_wa2(priv_ep, "Ignoring Descriptor missing IRQ\n");
6718c2ecf20Sopenharmony_ci		return;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
6758c2ecf20Sopenharmony_ci		priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
6768c2ecf20Sopenharmony_ci		priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	trace_cdns3_wa2(priv_ep, "Description Missing detected\n");
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (priv_ep->wa2_counter >= CDNS3_WA2_NUM_BUFFERS) {
6828c2ecf20Sopenharmony_ci		trace_cdns3_wa2(priv_ep, "WA2 overflow\n");
6838c2ecf20Sopenharmony_ci		cdns3_wa2_remove_old_request(priv_ep);
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
6878c2ecf20Sopenharmony_ci						GFP_ATOMIC);
6888c2ecf20Sopenharmony_ci	if (!request)
6898c2ecf20Sopenharmony_ci		goto err;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	priv_req = to_cdns3_request(request);
6928c2ecf20Sopenharmony_ci	priv_req->flags |= REQUEST_INTERNAL;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	/* if this field is still assigned it indicate that transfer related
6958c2ecf20Sopenharmony_ci	 * with this request has not been finished yet. Driver in this
6968c2ecf20Sopenharmony_ci	 * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
6978c2ecf20Sopenharmony_ci	 * flag to previous one. It will indicate that current request is
6988c2ecf20Sopenharmony_ci	 * part of the previous one.
6998c2ecf20Sopenharmony_ci	 */
7008c2ecf20Sopenharmony_ci	if (priv_ep->descmis_req)
7018c2ecf20Sopenharmony_ci		priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
7048c2ecf20Sopenharmony_ci					GFP_ATOMIC);
7058c2ecf20Sopenharmony_ci	priv_ep->wa2_counter++;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	if (!priv_req->request.buf) {
7088c2ecf20Sopenharmony_ci		cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
7098c2ecf20Sopenharmony_ci		goto err;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
7138c2ecf20Sopenharmony_ci	priv_ep->descmis_req = priv_req;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	__cdns3_gadget_ep_queue(&priv_ep->endpoint,
7168c2ecf20Sopenharmony_ci				&priv_ep->descmis_req->request,
7178c2ecf20Sopenharmony_ci				GFP_ATOMIC);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	return;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_cierr:
7228c2ecf20Sopenharmony_ci	dev_err(priv_ep->cdns3_dev->dev,
7238c2ecf20Sopenharmony_ci		"Failed: No sufficient memory for DESCMIS\n");
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistatic void cdns3_wa2_reset_tdl(struct cdns3_device *priv_dev)
7278c2ecf20Sopenharmony_ci{
7288c2ecf20Sopenharmony_ci	u16 tdl = EP_CMD_TDL_GET(readl(&priv_dev->regs->ep_cmd));
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	if (tdl) {
7318c2ecf20Sopenharmony_ci		u16 reset_val = EP_CMD_TDL_MAX + 1 - tdl;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci		writel(EP_CMD_TDL_SET(reset_val) | EP_CMD_STDL,
7348c2ecf20Sopenharmony_ci		       &priv_dev->regs->ep_cmd);
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cistatic void cdns3_wa2_check_outq_status(struct cdns3_device *priv_dev)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	u32 ep_sts_reg;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	/* select EP0-out */
7438c2ecf20Sopenharmony_ci	cdns3_select_ep(priv_dev, 0);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	if (EP_STS_OUTQ_VAL(ep_sts_reg)) {
7488c2ecf20Sopenharmony_ci		u32 outq_ep_num = EP_STS_OUTQ_NO(ep_sts_reg);
7498c2ecf20Sopenharmony_ci		struct cdns3_endpoint *outq_ep = priv_dev->eps[outq_ep_num];
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci		if ((outq_ep->flags & EP_ENABLED) && !(outq_ep->use_streams) &&
7528c2ecf20Sopenharmony_ci		    outq_ep->type != USB_ENDPOINT_XFER_ISOC && outq_ep_num) {
7538c2ecf20Sopenharmony_ci			u8 pending_empty = list_empty(&outq_ep->pending_req_list);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci			if ((outq_ep->flags & EP_QUIRK_EXTRA_BUF_DET) ||
7568c2ecf20Sopenharmony_ci			    (outq_ep->flags & EP_QUIRK_EXTRA_BUF_EN) ||
7578c2ecf20Sopenharmony_ci			    !pending_empty) {
7588c2ecf20Sopenharmony_ci			} else {
7598c2ecf20Sopenharmony_ci				u32 ep_sts_en_reg;
7608c2ecf20Sopenharmony_ci				u32 ep_cmd_reg;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci				cdns3_select_ep(priv_dev, outq_ep->num |
7638c2ecf20Sopenharmony_ci						outq_ep->dir);
7648c2ecf20Sopenharmony_ci				ep_sts_en_reg = readl(&priv_dev->regs->ep_sts_en);
7658c2ecf20Sopenharmony_ci				ep_cmd_reg = readl(&priv_dev->regs->ep_cmd);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci				outq_ep->flags |= EP_TDLCHK_EN;
7688c2ecf20Sopenharmony_ci				cdns3_set_register_bit(&priv_dev->regs->ep_cfg,
7698c2ecf20Sopenharmony_ci						       EP_CFG_TDL_CHK);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci				cdns3_wa2_enable_detection(priv_dev, outq_ep,
7728c2ecf20Sopenharmony_ci							   ep_sts_en_reg);
7738c2ecf20Sopenharmony_ci				writel(ep_sts_en_reg,
7748c2ecf20Sopenharmony_ci				       &priv_dev->regs->ep_sts_en);
7758c2ecf20Sopenharmony_ci				/* reset tdl value to zero */
7768c2ecf20Sopenharmony_ci				cdns3_wa2_reset_tdl(priv_dev);
7778c2ecf20Sopenharmony_ci				/*
7788c2ecf20Sopenharmony_ci				 * Memory barrier - Reset tdl before ringing the
7798c2ecf20Sopenharmony_ci				 * doorbell.
7808c2ecf20Sopenharmony_ci				 */
7818c2ecf20Sopenharmony_ci				wmb();
7828c2ecf20Sopenharmony_ci				if (EP_CMD_DRDY & ep_cmd_reg) {
7838c2ecf20Sopenharmony_ci					trace_cdns3_wa2(outq_ep, "Enabling WA2 skipping doorbell\n");
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci				} else {
7868c2ecf20Sopenharmony_ci					trace_cdns3_wa2(outq_ep, "Enabling WA2 ringing doorbell\n");
7878c2ecf20Sopenharmony_ci					/*
7888c2ecf20Sopenharmony_ci					 * ring doorbell to generate DESCMIS irq
7898c2ecf20Sopenharmony_ci					 */
7908c2ecf20Sopenharmony_ci					writel(EP_CMD_DRDY,
7918c2ecf20Sopenharmony_ci					       &priv_dev->regs->ep_cmd);
7928c2ecf20Sopenharmony_ci				}
7938c2ecf20Sopenharmony_ci			}
7948c2ecf20Sopenharmony_ci		}
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci/**
7998c2ecf20Sopenharmony_ci * cdns3_gadget_giveback - call struct usb_request's ->complete callback
8008c2ecf20Sopenharmony_ci * @priv_ep: The endpoint to whom the request belongs to
8018c2ecf20Sopenharmony_ci * @priv_req: The request we're giving back
8028c2ecf20Sopenharmony_ci * @status: completion code for the request
8038c2ecf20Sopenharmony_ci *
8048c2ecf20Sopenharmony_ci * Must be called with controller's lock held and interrupts disabled. This
8058c2ecf20Sopenharmony_ci * function will unmap @req and call its ->complete() callback to notify upper
8068c2ecf20Sopenharmony_ci * layers that it has completed.
8078c2ecf20Sopenharmony_ci */
8088c2ecf20Sopenharmony_civoid cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
8098c2ecf20Sopenharmony_ci			   struct cdns3_request *priv_req,
8108c2ecf20Sopenharmony_ci			   int status)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
8138c2ecf20Sopenharmony_ci	struct usb_request *request = &priv_req->request;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	list_del_init(&request->list);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	if (request->status == -EINPROGRESS)
8188c2ecf20Sopenharmony_ci		request->status = status;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
8218c2ecf20Sopenharmony_ci					priv_ep->dir);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	if ((priv_req->flags & REQUEST_UNALIGNED) &&
8248c2ecf20Sopenharmony_ci	    priv_ep->dir == USB_DIR_OUT && !request->status)
8258c2ecf20Sopenharmony_ci		memcpy(request->buf, priv_req->aligned_buf->buf,
8268c2ecf20Sopenharmony_ci		       request->length);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED);
8298c2ecf20Sopenharmony_ci	/* All TRBs have finished, clear the counter */
8308c2ecf20Sopenharmony_ci	priv_req->finished_trb = 0;
8318c2ecf20Sopenharmony_ci	trace_cdns3_gadget_giveback(priv_req);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver < DEV_VER_V2) {
8348c2ecf20Sopenharmony_ci		request = cdns3_wa2_gadget_giveback(priv_dev, priv_ep,
8358c2ecf20Sopenharmony_ci						    priv_req);
8368c2ecf20Sopenharmony_ci		if (!request)
8378c2ecf20Sopenharmony_ci			return;
8388c2ecf20Sopenharmony_ci	}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	if (request->complete) {
8418c2ecf20Sopenharmony_ci		spin_unlock(&priv_dev->lock);
8428c2ecf20Sopenharmony_ci		usb_gadget_giveback_request(&priv_ep->endpoint,
8438c2ecf20Sopenharmony_ci					    request);
8448c2ecf20Sopenharmony_ci		spin_lock(&priv_dev->lock);
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if (request->buf == priv_dev->zlp_buf)
8488c2ecf20Sopenharmony_ci		cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_cistatic void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
8528c2ecf20Sopenharmony_ci{
8538c2ecf20Sopenharmony_ci	/* Work around for stale data address in TRB*/
8548c2ecf20Sopenharmony_ci	if (priv_ep->wa1_set) {
8558c2ecf20Sopenharmony_ci		trace_cdns3_wa1(priv_ep, "restore cycle bit");
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci		priv_ep->wa1_set = 0;
8588c2ecf20Sopenharmony_ci		priv_ep->wa1_trb_index = 0xFFFF;
8598c2ecf20Sopenharmony_ci		if (priv_ep->wa1_cycle_bit) {
8608c2ecf20Sopenharmony_ci			priv_ep->wa1_trb->control =
8618c2ecf20Sopenharmony_ci				priv_ep->wa1_trb->control | cpu_to_le32(0x1);
8628c2ecf20Sopenharmony_ci		} else {
8638c2ecf20Sopenharmony_ci			priv_ep->wa1_trb->control =
8648c2ecf20Sopenharmony_ci				priv_ep->wa1_trb->control & cpu_to_le32(~0x1);
8658c2ecf20Sopenharmony_ci		}
8668c2ecf20Sopenharmony_ci	}
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_cistatic void cdns3_free_aligned_request_buf(struct work_struct *work)
8708c2ecf20Sopenharmony_ci{
8718c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
8728c2ecf20Sopenharmony_ci					aligned_buf_wq);
8738c2ecf20Sopenharmony_ci	struct cdns3_aligned_buf *buf, *tmp;
8748c2ecf20Sopenharmony_ci	unsigned long flags;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv_dev->lock, flags);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	list_for_each_entry_safe(buf, tmp, &priv_dev->aligned_buf_list, list) {
8798c2ecf20Sopenharmony_ci		if (!buf->in_use) {
8808c2ecf20Sopenharmony_ci			list_del(&buf->list);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci			/*
8838c2ecf20Sopenharmony_ci			 * Re-enable interrupts to free DMA capable memory.
8848c2ecf20Sopenharmony_ci			 * Driver can't free this memory with disabled
8858c2ecf20Sopenharmony_ci			 * interrupts.
8868c2ecf20Sopenharmony_ci			 */
8878c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&priv_dev->lock, flags);
8888c2ecf20Sopenharmony_ci			dma_free_coherent(priv_dev->sysdev, buf->size,
8898c2ecf20Sopenharmony_ci					  buf->buf, buf->dma);
8908c2ecf20Sopenharmony_ci			kfree(buf);
8918c2ecf20Sopenharmony_ci			spin_lock_irqsave(&priv_dev->lock, flags);
8928c2ecf20Sopenharmony_ci		}
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv_dev->lock, flags);
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistatic int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep = priv_req->priv_ep;
9018c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
9028c2ecf20Sopenharmony_ci	struct cdns3_aligned_buf *buf;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	/* check if buffer is aligned to 8. */
9058c2ecf20Sopenharmony_ci	if (!((uintptr_t)priv_req->request.buf & 0x7))
9068c2ecf20Sopenharmony_ci		return 0;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	buf = priv_req->aligned_buf;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	if (!buf || priv_req->request.length > buf->size) {
9118c2ecf20Sopenharmony_ci		buf = kzalloc(sizeof(*buf), GFP_ATOMIC);
9128c2ecf20Sopenharmony_ci		if (!buf)
9138c2ecf20Sopenharmony_ci			return -ENOMEM;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		buf->size = priv_req->request.length;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci		buf->buf = dma_alloc_coherent(priv_dev->sysdev,
9188c2ecf20Sopenharmony_ci					      buf->size,
9198c2ecf20Sopenharmony_ci					      &buf->dma,
9208c2ecf20Sopenharmony_ci					      GFP_ATOMIC);
9218c2ecf20Sopenharmony_ci		if (!buf->buf) {
9228c2ecf20Sopenharmony_ci			kfree(buf);
9238c2ecf20Sopenharmony_ci			return -ENOMEM;
9248c2ecf20Sopenharmony_ci		}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci		if (priv_req->aligned_buf) {
9278c2ecf20Sopenharmony_ci			trace_cdns3_free_aligned_request(priv_req);
9288c2ecf20Sopenharmony_ci			priv_req->aligned_buf->in_use = 0;
9298c2ecf20Sopenharmony_ci			queue_work(system_freezable_wq,
9308c2ecf20Sopenharmony_ci				   &priv_dev->aligned_buf_wq);
9318c2ecf20Sopenharmony_ci		}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci		buf->in_use = 1;
9348c2ecf20Sopenharmony_ci		priv_req->aligned_buf = buf;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci		list_add_tail(&buf->list,
9378c2ecf20Sopenharmony_ci			      &priv_dev->aligned_buf_list);
9388c2ecf20Sopenharmony_ci	}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	if (priv_ep->dir == USB_DIR_IN) {
9418c2ecf20Sopenharmony_ci		memcpy(buf->buf, priv_req->request.buf,
9428c2ecf20Sopenharmony_ci		       priv_req->request.length);
9438c2ecf20Sopenharmony_ci	}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	priv_req->flags |= REQUEST_UNALIGNED;
9468c2ecf20Sopenharmony_ci	trace_cdns3_prepare_aligned_request(priv_req);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	return 0;
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic int cdns3_wa1_update_guard(struct cdns3_endpoint *priv_ep,
9528c2ecf20Sopenharmony_ci				  struct cdns3_trb *trb)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	if (!priv_ep->wa1_set) {
9578c2ecf20Sopenharmony_ci		u32 doorbell;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci		doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci		if (doorbell) {
9628c2ecf20Sopenharmony_ci			priv_ep->wa1_cycle_bit = priv_ep->pcs ? TRB_CYCLE : 0;
9638c2ecf20Sopenharmony_ci			priv_ep->wa1_set = 1;
9648c2ecf20Sopenharmony_ci			priv_ep->wa1_trb = trb;
9658c2ecf20Sopenharmony_ci			priv_ep->wa1_trb_index = priv_ep->enqueue;
9668c2ecf20Sopenharmony_ci			trace_cdns3_wa1(priv_ep, "set guard");
9678c2ecf20Sopenharmony_ci			return 0;
9688c2ecf20Sopenharmony_ci		}
9698c2ecf20Sopenharmony_ci	}
9708c2ecf20Sopenharmony_ci	return 1;
9718c2ecf20Sopenharmony_ci}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_cistatic void cdns3_wa1_tray_restore_cycle_bit(struct cdns3_device *priv_dev,
9748c2ecf20Sopenharmony_ci					     struct cdns3_endpoint *priv_ep)
9758c2ecf20Sopenharmony_ci{
9768c2ecf20Sopenharmony_ci	int dma_index;
9778c2ecf20Sopenharmony_ci	u32 doorbell;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
9808c2ecf20Sopenharmony_ci	dma_index = cdns3_get_dma_pos(priv_dev, priv_ep);
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	if (!doorbell || dma_index != priv_ep->wa1_trb_index)
9838c2ecf20Sopenharmony_ci		cdns3_wa1_restore_cycle_bit(priv_ep);
9848c2ecf20Sopenharmony_ci}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_cistatic int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep,
9878c2ecf20Sopenharmony_ci					struct usb_request *request)
9888c2ecf20Sopenharmony_ci{
9898c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
9908c2ecf20Sopenharmony_ci	struct cdns3_request *priv_req;
9918c2ecf20Sopenharmony_ci	struct cdns3_trb *trb;
9928c2ecf20Sopenharmony_ci	dma_addr_t trb_dma;
9938c2ecf20Sopenharmony_ci	int address;
9948c2ecf20Sopenharmony_ci	u32 control;
9958c2ecf20Sopenharmony_ci	u32 length;
9968c2ecf20Sopenharmony_ci	u32 tdl;
9978c2ecf20Sopenharmony_ci	unsigned int sg_idx = priv_ep->stream_sg_idx;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	priv_req = to_cdns3_request(request);
10008c2ecf20Sopenharmony_ci	address = priv_ep->endpoint.desc->bEndpointAddress;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	priv_ep->flags |= EP_PENDING_REQUEST;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	/* must allocate buffer aligned to 8 */
10058c2ecf20Sopenharmony_ci	if (priv_req->flags & REQUEST_UNALIGNED)
10068c2ecf20Sopenharmony_ci		trb_dma = priv_req->aligned_buf->dma;
10078c2ecf20Sopenharmony_ci	else
10088c2ecf20Sopenharmony_ci		trb_dma = request->dma;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	/*  For stream capable endpoints driver use only single TD. */
10118c2ecf20Sopenharmony_ci	trb = priv_ep->trb_pool + priv_ep->enqueue;
10128c2ecf20Sopenharmony_ci	priv_req->start_trb = priv_ep->enqueue;
10138c2ecf20Sopenharmony_ci	priv_req->end_trb = priv_req->start_trb;
10148c2ecf20Sopenharmony_ci	priv_req->trb = trb;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	cdns3_select_ep(priv_ep->cdns3_dev, address);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	control = TRB_TYPE(TRB_NORMAL) | TRB_CYCLE |
10198c2ecf20Sopenharmony_ci		  TRB_STREAM_ID(priv_req->request.stream_id) | TRB_ISP;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	if (!request->num_sgs) {
10228c2ecf20Sopenharmony_ci		trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma));
10238c2ecf20Sopenharmony_ci		length = request->length;
10248c2ecf20Sopenharmony_ci	} else {
10258c2ecf20Sopenharmony_ci		trb->buffer = cpu_to_le32(TRB_BUFFER(request->sg[sg_idx].dma_address));
10268c2ecf20Sopenharmony_ci		length = request->sg[sg_idx].length;
10278c2ecf20Sopenharmony_ci	}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	tdl = DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket);
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	trb->length = cpu_to_le32(TRB_BURST_LEN(16) | TRB_LEN(length));
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	/*
10348c2ecf20Sopenharmony_ci	 * For DEV_VER_V2 controller version we have enabled
10358c2ecf20Sopenharmony_ci	 * USB_CONF2_EN_TDL_TRB in DMULT configuration.
10368c2ecf20Sopenharmony_ci	 * This enables TDL calculation based on TRB, hence setting TDL in TRB.
10378c2ecf20Sopenharmony_ci	 */
10388c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver >= DEV_VER_V2) {
10398c2ecf20Sopenharmony_ci		if (priv_dev->gadget.speed == USB_SPEED_SUPER)
10408c2ecf20Sopenharmony_ci			trb->length |= cpu_to_le32(TRB_TDL_SS_SIZE(tdl));
10418c2ecf20Sopenharmony_ci	}
10428c2ecf20Sopenharmony_ci	priv_req->flags |= REQUEST_PENDING;
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	trb->control = cpu_to_le32(control);
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	/*
10498c2ecf20Sopenharmony_ci	 * Memory barrier - Cycle Bit must be set before trb->length  and
10508c2ecf20Sopenharmony_ci	 * trb->buffer fields.
10518c2ecf20Sopenharmony_ci	 */
10528c2ecf20Sopenharmony_ci	wmb();
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	/* always first element */
10558c2ecf20Sopenharmony_ci	writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma),
10568c2ecf20Sopenharmony_ci	       &priv_dev->regs->ep_traddr);
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	if (!(priv_ep->flags & EP_STALLED)) {
10598c2ecf20Sopenharmony_ci		trace_cdns3_ring(priv_ep);
10608c2ecf20Sopenharmony_ci		/*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
10618c2ecf20Sopenharmony_ci		writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci		priv_ep->prime_flag = false;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci		/*
10668c2ecf20Sopenharmony_ci		 * Controller version DEV_VER_V2 tdl calculation
10678c2ecf20Sopenharmony_ci		 * is based on TRB
10688c2ecf20Sopenharmony_ci		 */
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci		if (priv_dev->dev_ver < DEV_VER_V2)
10718c2ecf20Sopenharmony_ci			writel(EP_CMD_TDL_SET(tdl) | EP_CMD_STDL,
10728c2ecf20Sopenharmony_ci			       &priv_dev->regs->ep_cmd);
10738c2ecf20Sopenharmony_ci		else if (priv_dev->dev_ver > DEV_VER_V2)
10748c2ecf20Sopenharmony_ci			writel(tdl, &priv_dev->regs->ep_tdl);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci		priv_ep->last_stream_id = priv_req->request.stream_id;
10778c2ecf20Sopenharmony_ci		writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
10788c2ecf20Sopenharmony_ci		writel(EP_CMD_ERDY_SID(priv_req->request.stream_id) |
10798c2ecf20Sopenharmony_ci		       EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci		trace_cdns3_doorbell_epx(priv_ep->name,
10828c2ecf20Sopenharmony_ci					 readl(&priv_dev->regs->ep_traddr));
10838c2ecf20Sopenharmony_ci	}
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	/* WORKAROUND for transition to L0 */
10868c2ecf20Sopenharmony_ci	__cdns3_gadget_wakeup(priv_dev);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	return 0;
10898c2ecf20Sopenharmony_ci}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_cistatic void cdns3_rearm_drdy_if_needed(struct cdns3_endpoint *priv_ep)
10928c2ecf20Sopenharmony_ci{
10938c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver < DEV_VER_V3)
10968c2ecf20Sopenharmony_ci		return;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	if (readl(&priv_dev->regs->ep_sts) & EP_STS_TRBERR) {
10998c2ecf20Sopenharmony_ci		writel(EP_STS_TRBERR, &priv_dev->regs->ep_sts);
11008c2ecf20Sopenharmony_ci		writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci}
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci/**
11058c2ecf20Sopenharmony_ci * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
11068c2ecf20Sopenharmony_ci * @priv_ep: endpoint object
11078c2ecf20Sopenharmony_ci * @request: request object
11088c2ecf20Sopenharmony_ci *
11098c2ecf20Sopenharmony_ci * Returns zero on success or negative value on failure
11108c2ecf20Sopenharmony_ci */
11118c2ecf20Sopenharmony_cistatic int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
11128c2ecf20Sopenharmony_ci				 struct usb_request *request)
11138c2ecf20Sopenharmony_ci{
11148c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
11158c2ecf20Sopenharmony_ci	struct cdns3_request *priv_req;
11168c2ecf20Sopenharmony_ci	struct cdns3_trb *trb;
11178c2ecf20Sopenharmony_ci	struct cdns3_trb *link_trb = NULL;
11188c2ecf20Sopenharmony_ci	dma_addr_t trb_dma;
11198c2ecf20Sopenharmony_ci	u32 togle_pcs = 1;
11208c2ecf20Sopenharmony_ci	int sg_iter = 0;
11218c2ecf20Sopenharmony_ci	int num_trb_req;
11228c2ecf20Sopenharmony_ci	int trb_burst;
11238c2ecf20Sopenharmony_ci	int num_trb;
11248c2ecf20Sopenharmony_ci	int address;
11258c2ecf20Sopenharmony_ci	u32 control;
11268c2ecf20Sopenharmony_ci	int pcs;
11278c2ecf20Sopenharmony_ci	u16 total_tdl = 0;
11288c2ecf20Sopenharmony_ci	struct scatterlist *s = NULL;
11298c2ecf20Sopenharmony_ci	bool sg_supported = !!(request->num_mapped_sgs);
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	num_trb_req = sg_supported ? request->num_mapped_sgs : 1;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	/* ISO transfer require each SOF have a TD, each TD include some TRBs */
11348c2ecf20Sopenharmony_ci	if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
11358c2ecf20Sopenharmony_ci		num_trb = priv_ep->interval * num_trb_req;
11368c2ecf20Sopenharmony_ci	else
11378c2ecf20Sopenharmony_ci		num_trb = num_trb_req;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	priv_req = to_cdns3_request(request);
11408c2ecf20Sopenharmony_ci	address = priv_ep->endpoint.desc->bEndpointAddress;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	priv_ep->flags |= EP_PENDING_REQUEST;
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	/* must allocate buffer aligned to 8 */
11458c2ecf20Sopenharmony_ci	if (priv_req->flags & REQUEST_UNALIGNED)
11468c2ecf20Sopenharmony_ci		trb_dma = priv_req->aligned_buf->dma;
11478c2ecf20Sopenharmony_ci	else
11488c2ecf20Sopenharmony_ci		trb_dma = request->dma;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	trb = priv_ep->trb_pool + priv_ep->enqueue;
11518c2ecf20Sopenharmony_ci	priv_req->start_trb = priv_ep->enqueue;
11528c2ecf20Sopenharmony_ci	priv_req->trb = trb;
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	cdns3_select_ep(priv_ep->cdns3_dev, address);
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	/* prepare ring */
11578c2ecf20Sopenharmony_ci	if ((priv_ep->enqueue + num_trb)  >= (priv_ep->num_trbs - 1)) {
11588c2ecf20Sopenharmony_ci		int doorbell, dma_index;
11598c2ecf20Sopenharmony_ci		u32 ch_bit = 0;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci		doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
11628c2ecf20Sopenharmony_ci		dma_index = cdns3_get_dma_pos(priv_dev, priv_ep);
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci		/* Driver can't update LINK TRB if it is current processed. */
11658c2ecf20Sopenharmony_ci		if (doorbell && dma_index == priv_ep->num_trbs - 1) {
11668c2ecf20Sopenharmony_ci			priv_ep->flags |= EP_DEFERRED_DRDY;
11678c2ecf20Sopenharmony_ci			return -ENOBUFS;
11688c2ecf20Sopenharmony_ci		}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci		/*updating C bt in  Link TRB before starting DMA*/
11718c2ecf20Sopenharmony_ci		link_trb = priv_ep->trb_pool + (priv_ep->num_trbs - 1);
11728c2ecf20Sopenharmony_ci		/*
11738c2ecf20Sopenharmony_ci		 * For TRs size equal 2 enabling TRB_CHAIN for epXin causes
11748c2ecf20Sopenharmony_ci		 * that DMA stuck at the LINK TRB.
11758c2ecf20Sopenharmony_ci		 * On the other hand, removing TRB_CHAIN for longer TRs for
11768c2ecf20Sopenharmony_ci		 * epXout cause that DMA stuck after handling LINK TRB.
11778c2ecf20Sopenharmony_ci		 * To eliminate this strange behavioral driver set TRB_CHAIN
11788c2ecf20Sopenharmony_ci		 * bit only for TR size > 2.
11798c2ecf20Sopenharmony_ci		 */
11808c2ecf20Sopenharmony_ci		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC ||
11818c2ecf20Sopenharmony_ci		    TRBS_PER_SEGMENT > 2)
11828c2ecf20Sopenharmony_ci			ch_bit = TRB_CHAIN;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci		link_trb->control = cpu_to_le32(((priv_ep->pcs) ? TRB_CYCLE : 0) |
11858c2ecf20Sopenharmony_ci				    TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit);
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
11888c2ecf20Sopenharmony_ci			/*
11898c2ecf20Sopenharmony_ci			 * ISO require LINK TRB must be first one of TD.
11908c2ecf20Sopenharmony_ci			 * Fill LINK TRBs for left trb space to simply software process logic.
11918c2ecf20Sopenharmony_ci			 */
11928c2ecf20Sopenharmony_ci			while (priv_ep->enqueue) {
11938c2ecf20Sopenharmony_ci				*trb = *link_trb;
11948c2ecf20Sopenharmony_ci				trace_cdns3_prepare_trb(priv_ep, trb);
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci				cdns3_ep_inc_enq(priv_ep);
11978c2ecf20Sopenharmony_ci				trb = priv_ep->trb_pool + priv_ep->enqueue;
11988c2ecf20Sopenharmony_ci				priv_req->trb = trb;
11998c2ecf20Sopenharmony_ci			}
12008c2ecf20Sopenharmony_ci		}
12018c2ecf20Sopenharmony_ci	}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	if (num_trb > priv_ep->free_trbs) {
12048c2ecf20Sopenharmony_ci		priv_ep->flags |= EP_RING_FULL;
12058c2ecf20Sopenharmony_ci		return -ENOBUFS;
12068c2ecf20Sopenharmony_ci	}
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver <= DEV_VER_V2)
12098c2ecf20Sopenharmony_ci		togle_pcs = cdns3_wa1_update_guard(priv_ep, trb);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	/* set incorrect Cycle Bit for first trb*/
12128c2ecf20Sopenharmony_ci	control = priv_ep->pcs ? 0 : TRB_CYCLE;
12138c2ecf20Sopenharmony_ci	trb->length = 0;
12148c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver >= DEV_VER_V2) {
12158c2ecf20Sopenharmony_ci		u16 td_size;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci		td_size = DIV_ROUND_UP(request->length,
12188c2ecf20Sopenharmony_ci				       priv_ep->endpoint.maxpacket);
12198c2ecf20Sopenharmony_ci		if (priv_dev->gadget.speed == USB_SPEED_SUPER)
12208c2ecf20Sopenharmony_ci			trb->length = cpu_to_le32(TRB_TDL_SS_SIZE(td_size));
12218c2ecf20Sopenharmony_ci		else
12228c2ecf20Sopenharmony_ci			control |= TRB_TDL_HS_SIZE(td_size);
12238c2ecf20Sopenharmony_ci	}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	do {
12268c2ecf20Sopenharmony_ci		u32 length;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci		if (!(sg_iter % num_trb_req) && sg_supported)
12298c2ecf20Sopenharmony_ci			s = request->sg;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci		/* fill TRB */
12328c2ecf20Sopenharmony_ci		control |= TRB_TYPE(TRB_NORMAL);
12338c2ecf20Sopenharmony_ci		if (sg_supported) {
12348c2ecf20Sopenharmony_ci			trb->buffer = cpu_to_le32(TRB_BUFFER(sg_dma_address(s)));
12358c2ecf20Sopenharmony_ci			length = sg_dma_len(s);
12368c2ecf20Sopenharmony_ci		} else {
12378c2ecf20Sopenharmony_ci			trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma));
12388c2ecf20Sopenharmony_ci			length = request->length;
12398c2ecf20Sopenharmony_ci		}
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci		if (priv_ep->flags & EP_TDLCHK_EN)
12428c2ecf20Sopenharmony_ci			total_tdl += DIV_ROUND_UP(length,
12438c2ecf20Sopenharmony_ci					       priv_ep->endpoint.maxpacket);
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci		trb_burst = priv_ep->trb_burst_size;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci		/*
12488c2ecf20Sopenharmony_ci		 * Supposed DMA cross 4k bounder problem should be fixed at DEV_VER_V2, but still
12498c2ecf20Sopenharmony_ci		 * met problem when do ISO transfer if sg enabled.
12508c2ecf20Sopenharmony_ci		 *
12518c2ecf20Sopenharmony_ci		 * Data pattern likes below when sg enabled, package size is 1k and mult is 2
12528c2ecf20Sopenharmony_ci		 *       [UVC Header(8B) ] [data(3k - 8)] ...
12538c2ecf20Sopenharmony_ci		 *
12548c2ecf20Sopenharmony_ci		 * The received data at offset 0xd000 will get 0xc000 data, len 0x70. Error happen
12558c2ecf20Sopenharmony_ci		 * as below pattern:
12568c2ecf20Sopenharmony_ci		 *	0xd000: wrong
12578c2ecf20Sopenharmony_ci		 *	0xe000: wrong
12588c2ecf20Sopenharmony_ci		 *	0xf000: correct
12598c2ecf20Sopenharmony_ci		 *	0x10000: wrong
12608c2ecf20Sopenharmony_ci		 *	0x11000: wrong
12618c2ecf20Sopenharmony_ci		 *	0x12000: correct
12628c2ecf20Sopenharmony_ci		 *	...
12638c2ecf20Sopenharmony_ci		 *
12648c2ecf20Sopenharmony_ci		 * But it is still unclear about why error have not happen below 0xd000, it should
12658c2ecf20Sopenharmony_ci		 * cross 4k bounder. But anyway, the below code can fix this problem.
12668c2ecf20Sopenharmony_ci		 *
12678c2ecf20Sopenharmony_ci		 * To avoid DMA cross 4k bounder at ISO transfer, reduce burst len according to 16.
12688c2ecf20Sopenharmony_ci		 */
12698c2ecf20Sopenharmony_ci		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && priv_dev->dev_ver <= DEV_VER_V2)
12708c2ecf20Sopenharmony_ci			if (ALIGN_DOWN(trb->buffer, SZ_4K) !=
12718c2ecf20Sopenharmony_ci			    ALIGN_DOWN(trb->buffer + length, SZ_4K))
12728c2ecf20Sopenharmony_ci				trb_burst = 16;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci		trb->length |= cpu_to_le32(TRB_BURST_LEN(trb_burst) |
12758c2ecf20Sopenharmony_ci					TRB_LEN(length));
12768c2ecf20Sopenharmony_ci		pcs = priv_ep->pcs ? TRB_CYCLE : 0;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci		/*
12798c2ecf20Sopenharmony_ci		 * first trb should be prepared as last to avoid processing
12808c2ecf20Sopenharmony_ci		 *  transfer to early
12818c2ecf20Sopenharmony_ci		 */
12828c2ecf20Sopenharmony_ci		if (sg_iter != 0)
12838c2ecf20Sopenharmony_ci			control |= pcs;
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir) {
12868c2ecf20Sopenharmony_ci			control |= TRB_IOC | TRB_ISP;
12878c2ecf20Sopenharmony_ci		} else {
12888c2ecf20Sopenharmony_ci			/* for last element in TD or in SG list */
12898c2ecf20Sopenharmony_ci			if (sg_iter == (num_trb - 1) && sg_iter != 0)
12908c2ecf20Sopenharmony_ci				control |= pcs | TRB_IOC | TRB_ISP;
12918c2ecf20Sopenharmony_ci		}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci		if (sg_iter)
12948c2ecf20Sopenharmony_ci			trb->control = cpu_to_le32(control);
12958c2ecf20Sopenharmony_ci		else
12968c2ecf20Sopenharmony_ci			priv_req->trb->control = cpu_to_le32(control);
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci		if (sg_supported) {
12998c2ecf20Sopenharmony_ci			trb->control |= cpu_to_le32(TRB_ISP);
13008c2ecf20Sopenharmony_ci			/* Don't set chain bit for last TRB */
13018c2ecf20Sopenharmony_ci			if ((sg_iter % num_trb_req) < num_trb_req - 1)
13028c2ecf20Sopenharmony_ci				trb->control |= cpu_to_le32(TRB_CHAIN);
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci			s = sg_next(s);
13058c2ecf20Sopenharmony_ci		}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci		control = 0;
13088c2ecf20Sopenharmony_ci		++sg_iter;
13098c2ecf20Sopenharmony_ci		priv_req->end_trb = priv_ep->enqueue;
13108c2ecf20Sopenharmony_ci		cdns3_ep_inc_enq(priv_ep);
13118c2ecf20Sopenharmony_ci		trb = priv_ep->trb_pool + priv_ep->enqueue;
13128c2ecf20Sopenharmony_ci		trb->length = 0;
13138c2ecf20Sopenharmony_ci	} while (sg_iter < num_trb);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	trb = priv_req->trb;
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	priv_req->flags |= REQUEST_PENDING;
13188c2ecf20Sopenharmony_ci	priv_req->num_of_trb = num_trb;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	if (sg_iter == 1)
13218c2ecf20Sopenharmony_ci		trb->control |= cpu_to_le32(TRB_IOC | TRB_ISP);
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver < DEV_VER_V2 &&
13248c2ecf20Sopenharmony_ci	    (priv_ep->flags & EP_TDLCHK_EN)) {
13258c2ecf20Sopenharmony_ci		u16 tdl = total_tdl;
13268c2ecf20Sopenharmony_ci		u16 old_tdl = EP_CMD_TDL_GET(readl(&priv_dev->regs->ep_cmd));
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci		if (tdl > EP_CMD_TDL_MAX) {
13298c2ecf20Sopenharmony_ci			tdl = EP_CMD_TDL_MAX;
13308c2ecf20Sopenharmony_ci			priv_ep->pending_tdl = total_tdl - EP_CMD_TDL_MAX;
13318c2ecf20Sopenharmony_ci		}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci		if (old_tdl < tdl) {
13348c2ecf20Sopenharmony_ci			tdl -= old_tdl;
13358c2ecf20Sopenharmony_ci			writel(EP_CMD_TDL_SET(tdl) | EP_CMD_STDL,
13368c2ecf20Sopenharmony_ci			       &priv_dev->regs->ep_cmd);
13378c2ecf20Sopenharmony_ci		}
13388c2ecf20Sopenharmony_ci	}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	/*
13418c2ecf20Sopenharmony_ci	 * Memory barrier - cycle bit must be set before other filds in trb.
13428c2ecf20Sopenharmony_ci	 */
13438c2ecf20Sopenharmony_ci	wmb();
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	/* give the TD to the consumer*/
13468c2ecf20Sopenharmony_ci	if (togle_pcs)
13478c2ecf20Sopenharmony_ci		trb->control = trb->control ^ cpu_to_le32(1);
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver <= DEV_VER_V2)
13508c2ecf20Sopenharmony_ci		cdns3_wa1_tray_restore_cycle_bit(priv_dev, priv_ep);
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	if (num_trb > 1) {
13538c2ecf20Sopenharmony_ci		int i = 0;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci		while (i < num_trb) {
13568c2ecf20Sopenharmony_ci			trace_cdns3_prepare_trb(priv_ep, trb + i);
13578c2ecf20Sopenharmony_ci			if (trb + i == link_trb) {
13588c2ecf20Sopenharmony_ci				trb = priv_ep->trb_pool;
13598c2ecf20Sopenharmony_ci				num_trb = num_trb - i;
13608c2ecf20Sopenharmony_ci				i = 0;
13618c2ecf20Sopenharmony_ci			} else {
13628c2ecf20Sopenharmony_ci				i++;
13638c2ecf20Sopenharmony_ci			}
13648c2ecf20Sopenharmony_ci		}
13658c2ecf20Sopenharmony_ci	} else {
13668c2ecf20Sopenharmony_ci		trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
13678c2ecf20Sopenharmony_ci	}
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	/*
13708c2ecf20Sopenharmony_ci	 * Memory barrier - Cycle Bit must be set before trb->length  and
13718c2ecf20Sopenharmony_ci	 * trb->buffer fields.
13728c2ecf20Sopenharmony_ci	 */
13738c2ecf20Sopenharmony_ci	wmb();
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	/*
13768c2ecf20Sopenharmony_ci	 * For DMULT mode we can set address to transfer ring only once after
13778c2ecf20Sopenharmony_ci	 * enabling endpoint.
13788c2ecf20Sopenharmony_ci	 */
13798c2ecf20Sopenharmony_ci	if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
13808c2ecf20Sopenharmony_ci		/*
13818c2ecf20Sopenharmony_ci		 * Until SW is not ready to handle the OUT transfer the ISO OUT
13828c2ecf20Sopenharmony_ci		 * Endpoint should be disabled (EP_CFG.ENABLE = 0).
13838c2ecf20Sopenharmony_ci		 * EP_CFG_ENABLE must be set before updating ep_traddr.
13848c2ecf20Sopenharmony_ci		 */
13858c2ecf20Sopenharmony_ci		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir &&
13868c2ecf20Sopenharmony_ci		    !(priv_ep->flags & EP_QUIRK_ISO_OUT_EN)) {
13878c2ecf20Sopenharmony_ci			priv_ep->flags |= EP_QUIRK_ISO_OUT_EN;
13888c2ecf20Sopenharmony_ci			cdns3_set_register_bit(&priv_dev->regs->ep_cfg,
13898c2ecf20Sopenharmony_ci					       EP_CFG_ENABLE);
13908c2ecf20Sopenharmony_ci		}
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci		writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma +
13938c2ecf20Sopenharmony_ci					priv_req->start_trb * TRB_SIZE),
13948c2ecf20Sopenharmony_ci					&priv_dev->regs->ep_traddr);
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci		priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
13978c2ecf20Sopenharmony_ci	}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	if (!priv_ep->wa1_set && !(priv_ep->flags & EP_STALLED)) {
14008c2ecf20Sopenharmony_ci		trace_cdns3_ring(priv_ep);
14018c2ecf20Sopenharmony_ci		/*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
14028c2ecf20Sopenharmony_ci		writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
14038c2ecf20Sopenharmony_ci		writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
14048c2ecf20Sopenharmony_ci		cdns3_rearm_drdy_if_needed(priv_ep);
14058c2ecf20Sopenharmony_ci		trace_cdns3_doorbell_epx(priv_ep->name,
14068c2ecf20Sopenharmony_ci					 readl(&priv_dev->regs->ep_traddr));
14078c2ecf20Sopenharmony_ci	}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	/* WORKAROUND for transition to L0 */
14108c2ecf20Sopenharmony_ci	__cdns3_gadget_wakeup(priv_dev);
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	return 0;
14138c2ecf20Sopenharmony_ci}
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_civoid cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
14168c2ecf20Sopenharmony_ci{
14178c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep;
14188c2ecf20Sopenharmony_ci	struct usb_ep *ep;
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	if (priv_dev->hw_configured_flag)
14218c2ecf20Sopenharmony_ci		return;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	cdns3_set_register_bit(&priv_dev->regs->usb_conf,
14268c2ecf20Sopenharmony_ci			       USB_CONF_U1EN | USB_CONF_U2EN);
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	priv_dev->hw_configured_flag = 1;
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
14318c2ecf20Sopenharmony_ci		if (ep->enabled) {
14328c2ecf20Sopenharmony_ci			priv_ep = ep_to_cdns3_ep(ep);
14338c2ecf20Sopenharmony_ci			cdns3_start_all_request(priv_dev, priv_ep);
14348c2ecf20Sopenharmony_ci		}
14358c2ecf20Sopenharmony_ci	}
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	cdns3_allow_enable_l1(priv_dev, 1);
14388c2ecf20Sopenharmony_ci}
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci/**
14418c2ecf20Sopenharmony_ci * cdns3_trb_handled - check whether trb has been handled by DMA
14428c2ecf20Sopenharmony_ci *
14438c2ecf20Sopenharmony_ci * @priv_ep: extended endpoint object.
14448c2ecf20Sopenharmony_ci * @priv_req: request object for checking
14458c2ecf20Sopenharmony_ci *
14468c2ecf20Sopenharmony_ci * Endpoint must be selected before invoking this function.
14478c2ecf20Sopenharmony_ci *
14488c2ecf20Sopenharmony_ci * Returns false if request has not been handled by DMA, else returns true.
14498c2ecf20Sopenharmony_ci *
14508c2ecf20Sopenharmony_ci * SR - start ring
14518c2ecf20Sopenharmony_ci * ER -  end ring
14528c2ecf20Sopenharmony_ci * DQ = priv_ep->dequeue - dequeue position
14538c2ecf20Sopenharmony_ci * EQ = priv_ep->enqueue -  enqueue position
14548c2ecf20Sopenharmony_ci * ST = priv_req->start_trb - index of first TRB in transfer ring
14558c2ecf20Sopenharmony_ci * ET = priv_req->end_trb - index of last TRB in transfer ring
14568c2ecf20Sopenharmony_ci * CI = current_index - index of processed TRB by DMA.
14578c2ecf20Sopenharmony_ci *
14588c2ecf20Sopenharmony_ci * As first step, we check if the TRB between the ST and ET.
14598c2ecf20Sopenharmony_ci * Then, we check if cycle bit for index priv_ep->dequeue
14608c2ecf20Sopenharmony_ci * is correct.
14618c2ecf20Sopenharmony_ci *
14628c2ecf20Sopenharmony_ci * some rules:
14638c2ecf20Sopenharmony_ci * 1. priv_ep->dequeue never equals to current_index.
14648c2ecf20Sopenharmony_ci * 2  priv_ep->enqueue never exceed priv_ep->dequeue
14658c2ecf20Sopenharmony_ci * 3. exception: priv_ep->enqueue == priv_ep->dequeue
14668c2ecf20Sopenharmony_ci *    and priv_ep->free_trbs is zero.
14678c2ecf20Sopenharmony_ci *    This case indicate that TR is full.
14688c2ecf20Sopenharmony_ci *
14698c2ecf20Sopenharmony_ci * At below two cases, the request have been handled.
14708c2ecf20Sopenharmony_ci * Case 1 - priv_ep->dequeue < current_index
14718c2ecf20Sopenharmony_ci *      SR ... EQ ... DQ ... CI ... ER
14728c2ecf20Sopenharmony_ci *      SR ... DQ ... CI ... EQ ... ER
14738c2ecf20Sopenharmony_ci *
14748c2ecf20Sopenharmony_ci * Case 2 - priv_ep->dequeue > current_index
14758c2ecf20Sopenharmony_ci * This situation takes place when CI go through the LINK TRB at the end of
14768c2ecf20Sopenharmony_ci * transfer ring.
14778c2ecf20Sopenharmony_ci *      SR ... CI ... EQ ... DQ ... ER
14788c2ecf20Sopenharmony_ci */
14798c2ecf20Sopenharmony_cistatic bool cdns3_trb_handled(struct cdns3_endpoint *priv_ep,
14808c2ecf20Sopenharmony_ci				  struct cdns3_request *priv_req)
14818c2ecf20Sopenharmony_ci{
14828c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
14838c2ecf20Sopenharmony_ci	struct cdns3_trb *trb;
14848c2ecf20Sopenharmony_ci	int current_index = 0;
14858c2ecf20Sopenharmony_ci	int handled = 0;
14868c2ecf20Sopenharmony_ci	int doorbell;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	current_index = cdns3_get_dma_pos(priv_dev, priv_ep);
14898c2ecf20Sopenharmony_ci	doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	/* current trb doesn't belong to this request */
14928c2ecf20Sopenharmony_ci	if (priv_req->start_trb < priv_req->end_trb) {
14938c2ecf20Sopenharmony_ci		if (priv_ep->dequeue > priv_req->end_trb)
14948c2ecf20Sopenharmony_ci			goto finish;
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci		if (priv_ep->dequeue < priv_req->start_trb)
14978c2ecf20Sopenharmony_ci			goto finish;
14988c2ecf20Sopenharmony_ci	}
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	if ((priv_req->start_trb > priv_req->end_trb) &&
15018c2ecf20Sopenharmony_ci		(priv_ep->dequeue > priv_req->end_trb) &&
15028c2ecf20Sopenharmony_ci		(priv_ep->dequeue < priv_req->start_trb))
15038c2ecf20Sopenharmony_ci		goto finish;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	if ((priv_req->start_trb == priv_req->end_trb) &&
15068c2ecf20Sopenharmony_ci		(priv_ep->dequeue != priv_req->end_trb))
15078c2ecf20Sopenharmony_ci		goto finish;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	trb = &priv_ep->trb_pool[priv_ep->dequeue];
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	if ((le32_to_cpu(trb->control) & TRB_CYCLE) != priv_ep->ccs)
15128c2ecf20Sopenharmony_ci		goto finish;
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	if (doorbell == 1 && current_index == priv_ep->dequeue)
15158c2ecf20Sopenharmony_ci		goto finish;
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	/* The corner case for TRBS_PER_SEGMENT equal 2). */
15188c2ecf20Sopenharmony_ci	if (TRBS_PER_SEGMENT == 2 && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
15198c2ecf20Sopenharmony_ci		handled = 1;
15208c2ecf20Sopenharmony_ci		goto finish;
15218c2ecf20Sopenharmony_ci	}
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	if (priv_ep->enqueue == priv_ep->dequeue &&
15248c2ecf20Sopenharmony_ci	    priv_ep->free_trbs == 0) {
15258c2ecf20Sopenharmony_ci		handled = 1;
15268c2ecf20Sopenharmony_ci	} else if (priv_ep->dequeue < current_index) {
15278c2ecf20Sopenharmony_ci		if ((current_index == (priv_ep->num_trbs - 1)) &&
15288c2ecf20Sopenharmony_ci		    !priv_ep->dequeue)
15298c2ecf20Sopenharmony_ci			goto finish;
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci		handled = 1;
15328c2ecf20Sopenharmony_ci	} else if (priv_ep->dequeue  > current_index) {
15338c2ecf20Sopenharmony_ci			handled = 1;
15348c2ecf20Sopenharmony_ci	}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_cifinish:
15378c2ecf20Sopenharmony_ci	trace_cdns3_request_handled(priv_req, current_index, handled);
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	return handled;
15408c2ecf20Sopenharmony_ci}
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_cistatic void cdns3_transfer_completed(struct cdns3_device *priv_dev,
15438c2ecf20Sopenharmony_ci				     struct cdns3_endpoint *priv_ep)
15448c2ecf20Sopenharmony_ci{
15458c2ecf20Sopenharmony_ci	struct cdns3_request *priv_req;
15468c2ecf20Sopenharmony_ci	struct usb_request *request;
15478c2ecf20Sopenharmony_ci	struct cdns3_trb *trb;
15488c2ecf20Sopenharmony_ci	bool request_handled = false;
15498c2ecf20Sopenharmony_ci	bool transfer_end = false;
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	while (!list_empty(&priv_ep->pending_req_list)) {
15528c2ecf20Sopenharmony_ci		request = cdns3_next_request(&priv_ep->pending_req_list);
15538c2ecf20Sopenharmony_ci		priv_req = to_cdns3_request(request);
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci		trb = priv_ep->trb_pool + priv_ep->dequeue;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci		/* The TRB was changed as link TRB, and the request was handled at ep_dequeue */
15588c2ecf20Sopenharmony_ci		while (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK) {
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci			/* ISO ep_traddr may stop at LINK TRB */
15618c2ecf20Sopenharmony_ci			if (priv_ep->dequeue == cdns3_get_dma_pos(priv_dev, priv_ep) &&
15628c2ecf20Sopenharmony_ci			    priv_ep->type == USB_ENDPOINT_XFER_ISOC)
15638c2ecf20Sopenharmony_ci				break;
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci			trace_cdns3_complete_trb(priv_ep, trb);
15668c2ecf20Sopenharmony_ci			cdns3_ep_inc_deq(priv_ep);
15678c2ecf20Sopenharmony_ci			trb = priv_ep->trb_pool + priv_ep->dequeue;
15688c2ecf20Sopenharmony_ci		}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci		if (!request->stream_id) {
15718c2ecf20Sopenharmony_ci			/* Re-select endpoint. It could be changed by other CPU
15728c2ecf20Sopenharmony_ci			 * during handling usb_gadget_giveback_request.
15738c2ecf20Sopenharmony_ci			 */
15748c2ecf20Sopenharmony_ci			cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci			while (cdns3_trb_handled(priv_ep, priv_req)) {
15778c2ecf20Sopenharmony_ci				priv_req->finished_trb++;
15788c2ecf20Sopenharmony_ci				if (priv_req->finished_trb >= priv_req->num_of_trb)
15798c2ecf20Sopenharmony_ci					request_handled = true;
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci				trb = priv_ep->trb_pool + priv_ep->dequeue;
15828c2ecf20Sopenharmony_ci				trace_cdns3_complete_trb(priv_ep, trb);
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci				if (!transfer_end)
15858c2ecf20Sopenharmony_ci					request->actual +=
15868c2ecf20Sopenharmony_ci						TRB_LEN(le32_to_cpu(trb->length));
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci				if (priv_req->num_of_trb > 1 &&
15898c2ecf20Sopenharmony_ci					le32_to_cpu(trb->control) & TRB_SMM &&
15908c2ecf20Sopenharmony_ci					le32_to_cpu(trb->control) & TRB_CHAIN)
15918c2ecf20Sopenharmony_ci					transfer_end = true;
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci				cdns3_ep_inc_deq(priv_ep);
15948c2ecf20Sopenharmony_ci			}
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci			if (request_handled) {
15978c2ecf20Sopenharmony_ci				/* TRBs are duplicated by priv_ep->interval time for ISO IN */
15988c2ecf20Sopenharmony_ci				if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && priv_ep->dir)
15998c2ecf20Sopenharmony_ci					request->actual /= priv_ep->interval;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci				cdns3_gadget_giveback(priv_ep, priv_req, 0);
16028c2ecf20Sopenharmony_ci				request_handled = false;
16038c2ecf20Sopenharmony_ci				transfer_end = false;
16048c2ecf20Sopenharmony_ci			} else {
16058c2ecf20Sopenharmony_ci				goto prepare_next_td;
16068c2ecf20Sopenharmony_ci			}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci			if (priv_ep->type != USB_ENDPOINT_XFER_ISOC &&
16098c2ecf20Sopenharmony_ci			    TRBS_PER_SEGMENT == 2)
16108c2ecf20Sopenharmony_ci				break;
16118c2ecf20Sopenharmony_ci		} else {
16128c2ecf20Sopenharmony_ci			/* Re-select endpoint. It could be changed by other CPU
16138c2ecf20Sopenharmony_ci			 * during handling usb_gadget_giveback_request.
16148c2ecf20Sopenharmony_ci			 */
16158c2ecf20Sopenharmony_ci			cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci			trb = priv_ep->trb_pool;
16188c2ecf20Sopenharmony_ci			trace_cdns3_complete_trb(priv_ep, trb);
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci			if (trb != priv_req->trb)
16218c2ecf20Sopenharmony_ci				dev_warn(priv_dev->dev,
16228c2ecf20Sopenharmony_ci					 "request_trb=0x%p, queue_trb=0x%p\n",
16238c2ecf20Sopenharmony_ci					 priv_req->trb, trb);
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci			request->actual += TRB_LEN(le32_to_cpu(trb->length));
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci			if (!request->num_sgs ||
16288c2ecf20Sopenharmony_ci			    (request->num_sgs == (priv_ep->stream_sg_idx + 1))) {
16298c2ecf20Sopenharmony_ci				priv_ep->stream_sg_idx = 0;
16308c2ecf20Sopenharmony_ci				cdns3_gadget_giveback(priv_ep, priv_req, 0);
16318c2ecf20Sopenharmony_ci			} else {
16328c2ecf20Sopenharmony_ci				priv_ep->stream_sg_idx++;
16338c2ecf20Sopenharmony_ci				cdns3_ep_run_stream_transfer(priv_ep, request);
16348c2ecf20Sopenharmony_ci			}
16358c2ecf20Sopenharmony_ci			break;
16368c2ecf20Sopenharmony_ci		}
16378c2ecf20Sopenharmony_ci	}
16388c2ecf20Sopenharmony_ci	priv_ep->flags &= ~EP_PENDING_REQUEST;
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ciprepare_next_td:
16418c2ecf20Sopenharmony_ci	if (!(priv_ep->flags & EP_STALLED) &&
16428c2ecf20Sopenharmony_ci	    !(priv_ep->flags & EP_STALL_PENDING))
16438c2ecf20Sopenharmony_ci		cdns3_start_all_request(priv_dev, priv_ep);
16448c2ecf20Sopenharmony_ci}
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_civoid cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm)
16478c2ecf20Sopenharmony_ci{
16488c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	cdns3_wa1_restore_cycle_bit(priv_ep);
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	if (rearm) {
16538c2ecf20Sopenharmony_ci		trace_cdns3_ring(priv_ep);
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci		/* Cycle Bit must be updated before arming DMA. */
16568c2ecf20Sopenharmony_ci		wmb();
16578c2ecf20Sopenharmony_ci		writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci		__cdns3_gadget_wakeup(priv_dev);
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci		trace_cdns3_doorbell_epx(priv_ep->name,
16628c2ecf20Sopenharmony_ci					 readl(&priv_dev->regs->ep_traddr));
16638c2ecf20Sopenharmony_ci	}
16648c2ecf20Sopenharmony_ci}
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_cistatic void cdns3_reprogram_tdl(struct cdns3_endpoint *priv_ep)
16678c2ecf20Sopenharmony_ci{
16688c2ecf20Sopenharmony_ci	u16 tdl = priv_ep->pending_tdl;
16698c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	if (tdl > EP_CMD_TDL_MAX) {
16728c2ecf20Sopenharmony_ci		tdl = EP_CMD_TDL_MAX;
16738c2ecf20Sopenharmony_ci		priv_ep->pending_tdl -= EP_CMD_TDL_MAX;
16748c2ecf20Sopenharmony_ci	} else {
16758c2ecf20Sopenharmony_ci		priv_ep->pending_tdl = 0;
16768c2ecf20Sopenharmony_ci	}
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	writel(EP_CMD_TDL_SET(tdl) | EP_CMD_STDL, &priv_dev->regs->ep_cmd);
16798c2ecf20Sopenharmony_ci}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci/**
16828c2ecf20Sopenharmony_ci * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
16838c2ecf20Sopenharmony_ci * @priv_ep: endpoint object
16848c2ecf20Sopenharmony_ci *
16858c2ecf20Sopenharmony_ci * Returns 0
16868c2ecf20Sopenharmony_ci */
16878c2ecf20Sopenharmony_cistatic int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
16888c2ecf20Sopenharmony_ci{
16898c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
16908c2ecf20Sopenharmony_ci	u32 ep_sts_reg;
16918c2ecf20Sopenharmony_ci	struct usb_request *deferred_request;
16928c2ecf20Sopenharmony_ci	struct usb_request *pending_request;
16938c2ecf20Sopenharmony_ci	u32 tdl = 0;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	trace_cdns3_epx_irq(priv_dev, priv_ep);
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
17008c2ecf20Sopenharmony_ci	writel(ep_sts_reg, &priv_dev->regs->ep_sts);
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	if ((ep_sts_reg & EP_STS_PRIME) && priv_ep->use_streams) {
17038c2ecf20Sopenharmony_ci		bool dbusy = !!(ep_sts_reg & EP_STS_DBUSY);
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci		tdl = cdns3_get_tdl(priv_dev);
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci		/*
17088c2ecf20Sopenharmony_ci		 * Continue the previous transfer:
17098c2ecf20Sopenharmony_ci		 * There is some racing between ERDY and PRIME. The device send
17108c2ecf20Sopenharmony_ci		 * ERDY and almost in the same time Host send PRIME. It cause
17118c2ecf20Sopenharmony_ci		 * that host ignore the ERDY packet and driver has to send it
17128c2ecf20Sopenharmony_ci		 * again.
17138c2ecf20Sopenharmony_ci		 */
17148c2ecf20Sopenharmony_ci		if (tdl && (dbusy || !EP_STS_BUFFEMPTY(ep_sts_reg) ||
17158c2ecf20Sopenharmony_ci		    EP_STS_HOSTPP(ep_sts_reg))) {
17168c2ecf20Sopenharmony_ci			writel(EP_CMD_ERDY |
17178c2ecf20Sopenharmony_ci			       EP_CMD_ERDY_SID(priv_ep->last_stream_id),
17188c2ecf20Sopenharmony_ci			       &priv_dev->regs->ep_cmd);
17198c2ecf20Sopenharmony_ci			ep_sts_reg &= ~(EP_STS_MD_EXIT | EP_STS_IOC);
17208c2ecf20Sopenharmony_ci		} else {
17218c2ecf20Sopenharmony_ci			priv_ep->prime_flag = true;
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci			pending_request = cdns3_next_request(&priv_ep->pending_req_list);
17248c2ecf20Sopenharmony_ci			deferred_request = cdns3_next_request(&priv_ep->deferred_req_list);
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci			if (deferred_request && !pending_request) {
17278c2ecf20Sopenharmony_ci				cdns3_start_all_request(priv_dev, priv_ep);
17288c2ecf20Sopenharmony_ci			}
17298c2ecf20Sopenharmony_ci		}
17308c2ecf20Sopenharmony_ci	}
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	if (ep_sts_reg & EP_STS_TRBERR) {
17338c2ecf20Sopenharmony_ci		if (priv_ep->flags & EP_STALL_PENDING &&
17348c2ecf20Sopenharmony_ci		    !(ep_sts_reg & EP_STS_DESCMIS &&
17358c2ecf20Sopenharmony_ci		    priv_dev->dev_ver < DEV_VER_V2)) {
17368c2ecf20Sopenharmony_ci			cdns3_ep_stall_flush(priv_ep);
17378c2ecf20Sopenharmony_ci		}
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci		/*
17408c2ecf20Sopenharmony_ci		 * For isochronous transfer driver completes request on
17418c2ecf20Sopenharmony_ci		 * IOC or on TRBERR. IOC appears only when device receive
17428c2ecf20Sopenharmony_ci		 * OUT data packet. If host disable stream or lost some packet
17438c2ecf20Sopenharmony_ci		 * then the only way to finish all queued transfer is to do it
17448c2ecf20Sopenharmony_ci		 * on TRBERR event.
17458c2ecf20Sopenharmony_ci		 */
17468c2ecf20Sopenharmony_ci		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC &&
17478c2ecf20Sopenharmony_ci		    !priv_ep->wa1_set) {
17488c2ecf20Sopenharmony_ci			if (!priv_ep->dir) {
17498c2ecf20Sopenharmony_ci				u32 ep_cfg = readl(&priv_dev->regs->ep_cfg);
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci				ep_cfg &= ~EP_CFG_ENABLE;
17528c2ecf20Sopenharmony_ci				writel(ep_cfg, &priv_dev->regs->ep_cfg);
17538c2ecf20Sopenharmony_ci				priv_ep->flags &= ~EP_QUIRK_ISO_OUT_EN;
17548c2ecf20Sopenharmony_ci				priv_ep->flags |= EP_UPDATE_EP_TRBADDR;
17558c2ecf20Sopenharmony_ci			}
17568c2ecf20Sopenharmony_ci			cdns3_transfer_completed(priv_dev, priv_ep);
17578c2ecf20Sopenharmony_ci		} else if (!(priv_ep->flags & EP_STALLED) &&
17588c2ecf20Sopenharmony_ci			  !(priv_ep->flags & EP_STALL_PENDING)) {
17598c2ecf20Sopenharmony_ci			if (priv_ep->flags & EP_DEFERRED_DRDY) {
17608c2ecf20Sopenharmony_ci				priv_ep->flags &= ~EP_DEFERRED_DRDY;
17618c2ecf20Sopenharmony_ci				cdns3_start_all_request(priv_dev, priv_ep);
17628c2ecf20Sopenharmony_ci			} else {
17638c2ecf20Sopenharmony_ci				cdns3_rearm_transfer(priv_ep,
17648c2ecf20Sopenharmony_ci						     priv_ep->wa1_set);
17658c2ecf20Sopenharmony_ci			}
17668c2ecf20Sopenharmony_ci		}
17678c2ecf20Sopenharmony_ci	}
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci	if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP) ||
17708c2ecf20Sopenharmony_ci	    (ep_sts_reg & EP_STS_IOT)) {
17718c2ecf20Sopenharmony_ci		if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
17728c2ecf20Sopenharmony_ci			if (ep_sts_reg & EP_STS_ISP)
17738c2ecf20Sopenharmony_ci				priv_ep->flags |= EP_QUIRK_END_TRANSFER;
17748c2ecf20Sopenharmony_ci			else
17758c2ecf20Sopenharmony_ci				priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
17768c2ecf20Sopenharmony_ci		}
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci		if (!priv_ep->use_streams) {
17798c2ecf20Sopenharmony_ci			if ((ep_sts_reg & EP_STS_IOC) ||
17808c2ecf20Sopenharmony_ci			    (ep_sts_reg & EP_STS_ISP)) {
17818c2ecf20Sopenharmony_ci				cdns3_transfer_completed(priv_dev, priv_ep);
17828c2ecf20Sopenharmony_ci			} else if ((priv_ep->flags & EP_TDLCHK_EN) &
17838c2ecf20Sopenharmony_ci				   priv_ep->pending_tdl) {
17848c2ecf20Sopenharmony_ci				/* handle IOT with pending tdl */
17858c2ecf20Sopenharmony_ci				cdns3_reprogram_tdl(priv_ep);
17868c2ecf20Sopenharmony_ci			}
17878c2ecf20Sopenharmony_ci		} else if (priv_ep->dir == USB_DIR_OUT) {
17888c2ecf20Sopenharmony_ci			priv_ep->ep_sts_pending |= ep_sts_reg;
17898c2ecf20Sopenharmony_ci		} else if (ep_sts_reg & EP_STS_IOT) {
17908c2ecf20Sopenharmony_ci			cdns3_transfer_completed(priv_dev, priv_ep);
17918c2ecf20Sopenharmony_ci		}
17928c2ecf20Sopenharmony_ci	}
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	/*
17958c2ecf20Sopenharmony_ci	 * MD_EXIT interrupt sets when stream capable endpoint exits
17968c2ecf20Sopenharmony_ci	 * from MOVE DATA state of Bulk IN/OUT stream protocol state machine
17978c2ecf20Sopenharmony_ci	 */
17988c2ecf20Sopenharmony_ci	if (priv_ep->dir == USB_DIR_OUT && (ep_sts_reg & EP_STS_MD_EXIT) &&
17998c2ecf20Sopenharmony_ci	    (priv_ep->ep_sts_pending & EP_STS_IOT) && priv_ep->use_streams) {
18008c2ecf20Sopenharmony_ci		priv_ep->ep_sts_pending = 0;
18018c2ecf20Sopenharmony_ci		cdns3_transfer_completed(priv_dev, priv_ep);
18028c2ecf20Sopenharmony_ci	}
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	/*
18058c2ecf20Sopenharmony_ci	 * WA2: this condition should only be meet when
18068c2ecf20Sopenharmony_ci	 * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
18078c2ecf20Sopenharmony_ci	 * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
18088c2ecf20Sopenharmony_ci	 * In other cases this interrupt will be disabled.
18098c2ecf20Sopenharmony_ci	 */
18108c2ecf20Sopenharmony_ci	if (ep_sts_reg & EP_STS_DESCMIS && priv_dev->dev_ver < DEV_VER_V2 &&
18118c2ecf20Sopenharmony_ci	    !(priv_ep->flags & EP_STALLED))
18128c2ecf20Sopenharmony_ci		cdns3_wa2_descmissing_packet(priv_ep);
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci	return 0;
18158c2ecf20Sopenharmony_ci}
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_cistatic void cdns3_disconnect_gadget(struct cdns3_device *priv_dev)
18188c2ecf20Sopenharmony_ci{
18198c2ecf20Sopenharmony_ci	if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect)
18208c2ecf20Sopenharmony_ci		priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
18218c2ecf20Sopenharmony_ci}
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci/**
18248c2ecf20Sopenharmony_ci * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
18258c2ecf20Sopenharmony_ci * @priv_dev: extended gadget object
18268c2ecf20Sopenharmony_ci * @usb_ists: bitmap representation of device's reported interrupts
18278c2ecf20Sopenharmony_ci * (usb_ists register value)
18288c2ecf20Sopenharmony_ci */
18298c2ecf20Sopenharmony_cistatic void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
18308c2ecf20Sopenharmony_ci					      u32 usb_ists)
18318c2ecf20Sopenharmony_ci__must_hold(&priv_dev->lock)
18328c2ecf20Sopenharmony_ci{
18338c2ecf20Sopenharmony_ci	int speed = 0;
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	trace_cdns3_usb_irq(priv_dev, usb_ists);
18368c2ecf20Sopenharmony_ci	if (usb_ists & USB_ISTS_L1ENTI) {
18378c2ecf20Sopenharmony_ci		/*
18388c2ecf20Sopenharmony_ci		 * WORKAROUND: CDNS3 controller has issue with hardware resuming
18398c2ecf20Sopenharmony_ci		 * from L1. To fix it, if any DMA transfer is pending driver
18408c2ecf20Sopenharmony_ci		 * must starts driving resume signal immediately.
18418c2ecf20Sopenharmony_ci		 */
18428c2ecf20Sopenharmony_ci		if (readl(&priv_dev->regs->drbl))
18438c2ecf20Sopenharmony_ci			__cdns3_gadget_wakeup(priv_dev);
18448c2ecf20Sopenharmony_ci	}
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	/* Connection detected */
18478c2ecf20Sopenharmony_ci	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
18488c2ecf20Sopenharmony_ci		speed = cdns3_get_speed(priv_dev);
18498c2ecf20Sopenharmony_ci		priv_dev->gadget.speed = speed;
18508c2ecf20Sopenharmony_ci		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED);
18518c2ecf20Sopenharmony_ci		cdns3_ep0_config(priv_dev);
18528c2ecf20Sopenharmony_ci	}
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci	/* Disconnection detected */
18558c2ecf20Sopenharmony_ci	if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
18568c2ecf20Sopenharmony_ci		spin_unlock(&priv_dev->lock);
18578c2ecf20Sopenharmony_ci		cdns3_disconnect_gadget(priv_dev);
18588c2ecf20Sopenharmony_ci		spin_lock(&priv_dev->lock);
18598c2ecf20Sopenharmony_ci		priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
18608c2ecf20Sopenharmony_ci		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
18618c2ecf20Sopenharmony_ci		cdns3_hw_reset_eps_config(priv_dev);
18628c2ecf20Sopenharmony_ci	}
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	if (usb_ists & (USB_ISTS_L2ENTI | USB_ISTS_U3ENTI)) {
18658c2ecf20Sopenharmony_ci		if (priv_dev->gadget_driver &&
18668c2ecf20Sopenharmony_ci		    priv_dev->gadget_driver->suspend) {
18678c2ecf20Sopenharmony_ci			spin_unlock(&priv_dev->lock);
18688c2ecf20Sopenharmony_ci			priv_dev->gadget_driver->suspend(&priv_dev->gadget);
18698c2ecf20Sopenharmony_ci			spin_lock(&priv_dev->lock);
18708c2ecf20Sopenharmony_ci		}
18718c2ecf20Sopenharmony_ci	}
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	if (usb_ists & (USB_ISTS_L2EXTI | USB_ISTS_U3EXTI)) {
18748c2ecf20Sopenharmony_ci		if (priv_dev->gadget_driver &&
18758c2ecf20Sopenharmony_ci		    priv_dev->gadget_driver->resume) {
18768c2ecf20Sopenharmony_ci			spin_unlock(&priv_dev->lock);
18778c2ecf20Sopenharmony_ci			priv_dev->gadget_driver->resume(&priv_dev->gadget);
18788c2ecf20Sopenharmony_ci			spin_lock(&priv_dev->lock);
18798c2ecf20Sopenharmony_ci		}
18808c2ecf20Sopenharmony_ci	}
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	/* reset*/
18838c2ecf20Sopenharmony_ci	if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
18848c2ecf20Sopenharmony_ci		if (priv_dev->gadget_driver) {
18858c2ecf20Sopenharmony_ci			spin_unlock(&priv_dev->lock);
18868c2ecf20Sopenharmony_ci			usb_gadget_udc_reset(&priv_dev->gadget,
18878c2ecf20Sopenharmony_ci					     priv_dev->gadget_driver);
18888c2ecf20Sopenharmony_ci			spin_lock(&priv_dev->lock);
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci			/*read again to check the actual speed*/
18918c2ecf20Sopenharmony_ci			speed = cdns3_get_speed(priv_dev);
18928c2ecf20Sopenharmony_ci			priv_dev->gadget.speed = speed;
18938c2ecf20Sopenharmony_ci			cdns3_hw_reset_eps_config(priv_dev);
18948c2ecf20Sopenharmony_ci			cdns3_ep0_config(priv_dev);
18958c2ecf20Sopenharmony_ci		}
18968c2ecf20Sopenharmony_ci	}
18978c2ecf20Sopenharmony_ci}
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci/**
19008c2ecf20Sopenharmony_ci * cdns3_device_irq_handler- interrupt handler for device part of controller
19018c2ecf20Sopenharmony_ci *
19028c2ecf20Sopenharmony_ci * @irq: irq number for cdns3 core device
19038c2ecf20Sopenharmony_ci * @data: structure of cdns3
19048c2ecf20Sopenharmony_ci *
19058c2ecf20Sopenharmony_ci * Returns IRQ_HANDLED or IRQ_NONE
19068c2ecf20Sopenharmony_ci */
19078c2ecf20Sopenharmony_cistatic irqreturn_t cdns3_device_irq_handler(int irq, void *data)
19088c2ecf20Sopenharmony_ci{
19098c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = data;
19108c2ecf20Sopenharmony_ci	struct cdns3 *cdns = dev_get_drvdata(priv_dev->dev);
19118c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
19128c2ecf20Sopenharmony_ci	u32 reg;
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	if (cdns->in_lpm)
19158c2ecf20Sopenharmony_ci		return ret;
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	/* check USB device interrupt */
19188c2ecf20Sopenharmony_ci	reg = readl(&priv_dev->regs->usb_ists);
19198c2ecf20Sopenharmony_ci	if (reg) {
19208c2ecf20Sopenharmony_ci		/* After masking interrupts the new interrupts won't be
19218c2ecf20Sopenharmony_ci		 * reported in usb_ists/ep_ists. In order to not lose some
19228c2ecf20Sopenharmony_ci		 * of them driver disables only detected interrupts.
19238c2ecf20Sopenharmony_ci		 * They will be enabled ASAP after clearing source of
19248c2ecf20Sopenharmony_ci		 * interrupt. This an unusual behavior only applies to
19258c2ecf20Sopenharmony_ci		 * usb_ists register.
19268c2ecf20Sopenharmony_ci		 */
19278c2ecf20Sopenharmony_ci		reg = ~reg & readl(&priv_dev->regs->usb_ien);
19288c2ecf20Sopenharmony_ci		/* mask deferred interrupt. */
19298c2ecf20Sopenharmony_ci		writel(reg, &priv_dev->regs->usb_ien);
19308c2ecf20Sopenharmony_ci		ret = IRQ_WAKE_THREAD;
19318c2ecf20Sopenharmony_ci	}
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	/* check endpoint interrupt */
19348c2ecf20Sopenharmony_ci	reg = readl(&priv_dev->regs->ep_ists);
19358c2ecf20Sopenharmony_ci	if (reg) {
19368c2ecf20Sopenharmony_ci		writel(0, &priv_dev->regs->ep_ien);
19378c2ecf20Sopenharmony_ci		ret = IRQ_WAKE_THREAD;
19388c2ecf20Sopenharmony_ci	}
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci	return ret;
19418c2ecf20Sopenharmony_ci}
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci/**
19448c2ecf20Sopenharmony_ci * cdns3_device_thread_irq_handler- interrupt handler for device part
19458c2ecf20Sopenharmony_ci * of controller
19468c2ecf20Sopenharmony_ci *
19478c2ecf20Sopenharmony_ci * @irq: irq number for cdns3 core device
19488c2ecf20Sopenharmony_ci * @data: structure of cdns3
19498c2ecf20Sopenharmony_ci *
19508c2ecf20Sopenharmony_ci * Returns IRQ_HANDLED or IRQ_NONE
19518c2ecf20Sopenharmony_ci */
19528c2ecf20Sopenharmony_cistatic irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
19538c2ecf20Sopenharmony_ci{
19548c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = data;
19558c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
19568c2ecf20Sopenharmony_ci	unsigned long flags;
19578c2ecf20Sopenharmony_ci	unsigned int bit;
19588c2ecf20Sopenharmony_ci	unsigned long reg;
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv_dev->lock, flags);
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci	reg = readl(&priv_dev->regs->usb_ists);
19638c2ecf20Sopenharmony_ci	if (reg) {
19648c2ecf20Sopenharmony_ci		writel(reg, &priv_dev->regs->usb_ists);
19658c2ecf20Sopenharmony_ci		writel(USB_IEN_INIT, &priv_dev->regs->usb_ien);
19668c2ecf20Sopenharmony_ci		cdns3_check_usb_interrupt_proceed(priv_dev, reg);
19678c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
19688c2ecf20Sopenharmony_ci	}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	reg = readl(&priv_dev->regs->ep_ists);
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_ci	/* handle default endpoint OUT */
19738c2ecf20Sopenharmony_ci	if (reg & EP_ISTS_EP_OUT0) {
19748c2ecf20Sopenharmony_ci		cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
19758c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
19768c2ecf20Sopenharmony_ci	}
19778c2ecf20Sopenharmony_ci
19788c2ecf20Sopenharmony_ci	/* handle default endpoint IN */
19798c2ecf20Sopenharmony_ci	if (reg & EP_ISTS_EP_IN0) {
19808c2ecf20Sopenharmony_ci		cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
19818c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
19828c2ecf20Sopenharmony_ci	}
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	/* check if interrupt from non default endpoint, if no exit */
19858c2ecf20Sopenharmony_ci	reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
19868c2ecf20Sopenharmony_ci	if (!reg)
19878c2ecf20Sopenharmony_ci		goto irqend;
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	for_each_set_bit(bit, &reg,
19908c2ecf20Sopenharmony_ci			 sizeof(u32) * BITS_PER_BYTE) {
19918c2ecf20Sopenharmony_ci		cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
19928c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
19938c2ecf20Sopenharmony_ci	}
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver < DEV_VER_V2 && priv_dev->using_streams)
19968c2ecf20Sopenharmony_ci		cdns3_wa2_check_outq_status(priv_dev);
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ciirqend:
19998c2ecf20Sopenharmony_ci	writel(~0, &priv_dev->regs->ep_ien);
20008c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv_dev->lock, flags);
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci	return ret;
20038c2ecf20Sopenharmony_ci}
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci/**
20068c2ecf20Sopenharmony_ci * cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP
20078c2ecf20Sopenharmony_ci *
20088c2ecf20Sopenharmony_ci * The real reservation will occur during write to EP_CFG register,
20098c2ecf20Sopenharmony_ci * this function is used to check if the 'size' reservation is allowed.
20108c2ecf20Sopenharmony_ci *
20118c2ecf20Sopenharmony_ci * @priv_dev: extended gadget object
20128c2ecf20Sopenharmony_ci * @size: the size (KB) for EP would like to allocate
20138c2ecf20Sopenharmony_ci * @is_in: endpoint direction
20148c2ecf20Sopenharmony_ci *
20158c2ecf20Sopenharmony_ci * Return 0 if the required size can met or negative value on failure
20168c2ecf20Sopenharmony_ci */
20178c2ecf20Sopenharmony_cistatic int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
20188c2ecf20Sopenharmony_ci					  int size, int is_in)
20198c2ecf20Sopenharmony_ci{
20208c2ecf20Sopenharmony_ci	int remained;
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	/* 2KB are reserved for EP0*/
20238c2ecf20Sopenharmony_ci	remained = priv_dev->onchip_buffers - priv_dev->onchip_used_size - 2;
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_ci	if (is_in) {
20268c2ecf20Sopenharmony_ci		if (remained < size)
20278c2ecf20Sopenharmony_ci			return -EPERM;
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci		priv_dev->onchip_used_size += size;
20308c2ecf20Sopenharmony_ci	} else {
20318c2ecf20Sopenharmony_ci		int required;
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci		/**
20348c2ecf20Sopenharmony_ci		 *  ALL OUT EPs are shared the same chunk onchip memory, so
20358c2ecf20Sopenharmony_ci		 * driver checks if it already has assigned enough buffers
20368c2ecf20Sopenharmony_ci		 */
20378c2ecf20Sopenharmony_ci		if (priv_dev->out_mem_is_allocated >= size)
20388c2ecf20Sopenharmony_ci			return 0;
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci		required = size - priv_dev->out_mem_is_allocated;
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci		if (required > remained)
20438c2ecf20Sopenharmony_ci			return -EPERM;
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci		priv_dev->out_mem_is_allocated += required;
20468c2ecf20Sopenharmony_ci		priv_dev->onchip_used_size += required;
20478c2ecf20Sopenharmony_ci	}
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci	return 0;
20508c2ecf20Sopenharmony_ci}
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_cistatic void cdns3_configure_dmult(struct cdns3_device *priv_dev,
20538c2ecf20Sopenharmony_ci				  struct cdns3_endpoint *priv_ep)
20548c2ecf20Sopenharmony_ci{
20558c2ecf20Sopenharmony_ci	struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci	/* For dev_ver > DEV_VER_V2 DMULT is configured per endpoint */
20588c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver <= DEV_VER_V2)
20598c2ecf20Sopenharmony_ci		writel(USB_CONF_DMULT, &regs->usb_conf);
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver == DEV_VER_V2)
20628c2ecf20Sopenharmony_ci		writel(USB_CONF2_EN_TDL_TRB, &regs->usb_conf2);
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver >= DEV_VER_V3 && priv_ep) {
20658c2ecf20Sopenharmony_ci		u32 mask;
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci		if (priv_ep->dir)
20688c2ecf20Sopenharmony_ci			mask = BIT(priv_ep->num + 16);
20698c2ecf20Sopenharmony_ci		else
20708c2ecf20Sopenharmony_ci			mask = BIT(priv_ep->num);
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci		if (priv_ep->type != USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir) {
20738c2ecf20Sopenharmony_ci			cdns3_set_register_bit(&regs->tdl_from_trb, mask);
20748c2ecf20Sopenharmony_ci			cdns3_set_register_bit(&regs->tdl_beh, mask);
20758c2ecf20Sopenharmony_ci			cdns3_set_register_bit(&regs->tdl_beh2, mask);
20768c2ecf20Sopenharmony_ci			cdns3_set_register_bit(&regs->dma_adv_td, mask);
20778c2ecf20Sopenharmony_ci		}
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir)
20808c2ecf20Sopenharmony_ci			cdns3_set_register_bit(&regs->tdl_from_trb, mask);
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci		cdns3_set_register_bit(&regs->dtrans, mask);
20838c2ecf20Sopenharmony_ci	}
20848c2ecf20Sopenharmony_ci}
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci/**
20878c2ecf20Sopenharmony_ci * cdns3_ep_config Configure hardware endpoint
20888c2ecf20Sopenharmony_ci * @priv_ep: extended endpoint object
20898c2ecf20Sopenharmony_ci * @enable: set EP_CFG_ENABLE bit in ep_cfg register.
20908c2ecf20Sopenharmony_ci */
20918c2ecf20Sopenharmony_ciint cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
20928c2ecf20Sopenharmony_ci{
20938c2ecf20Sopenharmony_ci	bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
20948c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
20958c2ecf20Sopenharmony_ci	u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
20968c2ecf20Sopenharmony_ci	u32 max_packet_size = priv_ep->wMaxPacketSize;
20978c2ecf20Sopenharmony_ci	u8 maxburst = priv_ep->bMaxBurst;
20988c2ecf20Sopenharmony_ci	u32 ep_cfg = 0;
20998c2ecf20Sopenharmony_ci	u8 buffering;
21008c2ecf20Sopenharmony_ci	int ret;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	buffering = priv_dev->ep_buf_size - 1;
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_ci	cdns3_configure_dmult(priv_dev, priv_ep);
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	switch (priv_ep->type) {
21078c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_INT:
21088c2ecf20Sopenharmony_ci		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci		if (priv_dev->dev_ver >= DEV_VER_V2 && !priv_ep->dir)
21118c2ecf20Sopenharmony_ci			ep_cfg |= EP_CFG_TDL_CHK;
21128c2ecf20Sopenharmony_ci		break;
21138c2ecf20Sopenharmony_ci	case USB_ENDPOINT_XFER_BULK:
21148c2ecf20Sopenharmony_ci		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_ci		if (priv_dev->dev_ver >= DEV_VER_V2 && !priv_ep->dir)
21178c2ecf20Sopenharmony_ci			ep_cfg |= EP_CFG_TDL_CHK;
21188c2ecf20Sopenharmony_ci		break;
21198c2ecf20Sopenharmony_ci	default:
21208c2ecf20Sopenharmony_ci		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
21218c2ecf20Sopenharmony_ci		buffering = (priv_ep->bMaxBurst + 1) * (priv_ep->mult + 1) - 1;
21228c2ecf20Sopenharmony_ci	}
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci	switch (priv_dev->gadget.speed) {
21258c2ecf20Sopenharmony_ci	case USB_SPEED_FULL:
21268c2ecf20Sopenharmony_ci		max_packet_size = is_iso_ep ? 1023 : 64;
21278c2ecf20Sopenharmony_ci		break;
21288c2ecf20Sopenharmony_ci	case USB_SPEED_HIGH:
21298c2ecf20Sopenharmony_ci		max_packet_size = is_iso_ep ? 1024 : 512;
21308c2ecf20Sopenharmony_ci		break;
21318c2ecf20Sopenharmony_ci	case USB_SPEED_SUPER:
21328c2ecf20Sopenharmony_ci		if (priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
21338c2ecf20Sopenharmony_ci			max_packet_size = 1024;
21348c2ecf20Sopenharmony_ci			maxburst = priv_dev->ep_buf_size - 1;
21358c2ecf20Sopenharmony_ci		}
21368c2ecf20Sopenharmony_ci		break;
21378c2ecf20Sopenharmony_ci	default:
21388c2ecf20Sopenharmony_ci		/* all other speed are not supported */
21398c2ecf20Sopenharmony_ci		return -EINVAL;
21408c2ecf20Sopenharmony_ci	}
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ci	if (max_packet_size == 1024)
21438c2ecf20Sopenharmony_ci		priv_ep->trb_burst_size = 128;
21448c2ecf20Sopenharmony_ci	else if (max_packet_size >= 512)
21458c2ecf20Sopenharmony_ci		priv_ep->trb_burst_size = 64;
21468c2ecf20Sopenharmony_ci	else
21478c2ecf20Sopenharmony_ci		priv_ep->trb_burst_size = 16;
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_ci	/*
21508c2ecf20Sopenharmony_ci	 * In versions preceding DEV_VER_V2, for example, iMX8QM, there exit the bugs
21518c2ecf20Sopenharmony_ci	 * in the DMA. These bugs occur when the trb_burst_size exceeds 16 and the
21528c2ecf20Sopenharmony_ci	 * address is not aligned to 128 Bytes (which is a product of the 64-bit AXI
21538c2ecf20Sopenharmony_ci	 * and AXI maximum burst length of 16 or 0xF+1, dma_axi_ctrl0[3:0]). This
21548c2ecf20Sopenharmony_ci	 * results in data corruption when it crosses the 4K border. The corruption
21558c2ecf20Sopenharmony_ci	 * specifically occurs from the position (4K - (address & 0x7F)) to 4K.
21568c2ecf20Sopenharmony_ci	 *
21578c2ecf20Sopenharmony_ci	 * So force trb_burst_size to 16 at such platform.
21588c2ecf20Sopenharmony_ci	 */
21598c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver < DEV_VER_V2)
21608c2ecf20Sopenharmony_ci		priv_ep->trb_burst_size = 16;
21618c2ecf20Sopenharmony_ci
21628c2ecf20Sopenharmony_ci	buffering = min_t(u8, buffering, EP_CFG_BUFFERING_MAX);
21638c2ecf20Sopenharmony_ci	maxburst = min_t(u8, maxburst, EP_CFG_MAXBURST_MAX);
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_ci	/* onchip buffer is only allocated before configuration */
21668c2ecf20Sopenharmony_ci	if (!priv_dev->hw_configured_flag) {
21678c2ecf20Sopenharmony_ci		ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1,
21688c2ecf20Sopenharmony_ci						     !!priv_ep->dir);
21698c2ecf20Sopenharmony_ci		if (ret) {
21708c2ecf20Sopenharmony_ci			dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
21718c2ecf20Sopenharmony_ci			return ret;
21728c2ecf20Sopenharmony_ci		}
21738c2ecf20Sopenharmony_ci	}
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci	if (enable)
21768c2ecf20Sopenharmony_ci		ep_cfg |= EP_CFG_ENABLE;
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_ci	if (priv_ep->use_streams && priv_dev->gadget.speed >= USB_SPEED_SUPER) {
21798c2ecf20Sopenharmony_ci		if (priv_dev->dev_ver >= DEV_VER_V3) {
21808c2ecf20Sopenharmony_ci			u32 mask = BIT(priv_ep->num + (priv_ep->dir ? 16 : 0));
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci			/*
21838c2ecf20Sopenharmony_ci			 * Stream capable endpoints are handled by using ep_tdl
21848c2ecf20Sopenharmony_ci			 * register. Other endpoints use TDL from TRB feature.
21858c2ecf20Sopenharmony_ci			 */
21868c2ecf20Sopenharmony_ci			cdns3_clear_register_bit(&priv_dev->regs->tdl_from_trb,
21878c2ecf20Sopenharmony_ci						 mask);
21888c2ecf20Sopenharmony_ci		}
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_ci		/*  Enable Stream Bit TDL chk and SID chk */
21918c2ecf20Sopenharmony_ci		ep_cfg |=  EP_CFG_STREAM_EN | EP_CFG_TDL_CHK | EP_CFG_SID_CHK;
21928c2ecf20Sopenharmony_ci	}
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
21958c2ecf20Sopenharmony_ci		  EP_CFG_MULT(priv_ep->mult) |			/* must match EP setting */
21968c2ecf20Sopenharmony_ci		  EP_CFG_BUFFERING(buffering) |
21978c2ecf20Sopenharmony_ci		  EP_CFG_MAXBURST(maxburst);
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci	cdns3_select_ep(priv_dev, bEndpointAddress);
22008c2ecf20Sopenharmony_ci	writel(ep_cfg, &priv_dev->regs->ep_cfg);
22018c2ecf20Sopenharmony_ci	priv_ep->flags |= EP_CONFIGURED;
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci	dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
22048c2ecf20Sopenharmony_ci		priv_ep->name, ep_cfg);
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci	return 0;
22078c2ecf20Sopenharmony_ci}
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci/* Find correct direction for HW endpoint according to description */
22108c2ecf20Sopenharmony_cistatic int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc,
22118c2ecf20Sopenharmony_ci				   struct cdns3_endpoint *priv_ep)
22128c2ecf20Sopenharmony_ci{
22138c2ecf20Sopenharmony_ci	return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) ||
22148c2ecf20Sopenharmony_ci	       (priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc));
22158c2ecf20Sopenharmony_ci}
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_cistatic struct
22188c2ecf20Sopenharmony_cicdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev,
22198c2ecf20Sopenharmony_ci					struct usb_endpoint_descriptor *desc)
22208c2ecf20Sopenharmony_ci{
22218c2ecf20Sopenharmony_ci	struct usb_ep *ep;
22228c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep;
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
22258c2ecf20Sopenharmony_ci		unsigned long num;
22268c2ecf20Sopenharmony_ci		int ret;
22278c2ecf20Sopenharmony_ci		/* ep name pattern likes epXin or epXout */
22288c2ecf20Sopenharmony_ci		char c[2] = {ep->name[2], '\0'};
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci		ret = kstrtoul(c, 10, &num);
22318c2ecf20Sopenharmony_ci		if (ret)
22328c2ecf20Sopenharmony_ci			return ERR_PTR(ret);
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci		priv_ep = ep_to_cdns3_ep(ep);
22358c2ecf20Sopenharmony_ci		if (cdns3_ep_dir_is_correct(desc, priv_ep)) {
22368c2ecf20Sopenharmony_ci			if (!(priv_ep->flags & EP_CLAIMED)) {
22378c2ecf20Sopenharmony_ci				priv_ep->num  = num;
22388c2ecf20Sopenharmony_ci				return priv_ep;
22398c2ecf20Sopenharmony_ci			}
22408c2ecf20Sopenharmony_ci		}
22418c2ecf20Sopenharmony_ci	}
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOENT);
22448c2ecf20Sopenharmony_ci}
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_ci/*
22478c2ecf20Sopenharmony_ci *  Cadence IP has one limitation that all endpoints must be configured
22488c2ecf20Sopenharmony_ci * (Type & MaxPacketSize) before setting configuration through hardware
22498c2ecf20Sopenharmony_ci * register, it means we can't change endpoints configuration after
22508c2ecf20Sopenharmony_ci * set_configuration.
22518c2ecf20Sopenharmony_ci *
22528c2ecf20Sopenharmony_ci * This function set EP_CLAIMED flag which is added when the gadget driver
22538c2ecf20Sopenharmony_ci * uses usb_ep_autoconfig to configure specific endpoint;
22548c2ecf20Sopenharmony_ci * When the udc driver receives set_configurion request,
22558c2ecf20Sopenharmony_ci * it goes through all claimed endpoints, and configure all endpoints
22568c2ecf20Sopenharmony_ci * accordingly.
22578c2ecf20Sopenharmony_ci *
22588c2ecf20Sopenharmony_ci * At usb_ep_ops.enable/disable, we only enable and disable endpoint through
22598c2ecf20Sopenharmony_ci * ep_cfg register which can be changed after set_configuration, and do
22608c2ecf20Sopenharmony_ci * some software operation accordingly.
22618c2ecf20Sopenharmony_ci */
22628c2ecf20Sopenharmony_cistatic struct
22638c2ecf20Sopenharmony_ciusb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
22648c2ecf20Sopenharmony_ci			      struct usb_endpoint_descriptor *desc,
22658c2ecf20Sopenharmony_ci			      struct usb_ss_ep_comp_descriptor *comp_desc)
22668c2ecf20Sopenharmony_ci{
22678c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
22688c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep;
22698c2ecf20Sopenharmony_ci	unsigned long flags;
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci	priv_ep = cdns3_find_available_ep(priv_dev, desc);
22728c2ecf20Sopenharmony_ci	if (IS_ERR(priv_ep)) {
22738c2ecf20Sopenharmony_ci		dev_err(priv_dev->dev, "no available ep\n");
22748c2ecf20Sopenharmony_ci		return NULL;
22758c2ecf20Sopenharmony_ci	}
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_ci	dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name);
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv_dev->lock, flags);
22808c2ecf20Sopenharmony_ci	priv_ep->endpoint.desc = desc;
22818c2ecf20Sopenharmony_ci	priv_ep->dir  = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT;
22828c2ecf20Sopenharmony_ci	priv_ep->type = usb_endpoint_type(desc);
22838c2ecf20Sopenharmony_ci	priv_ep->flags |= EP_CLAIMED;
22848c2ecf20Sopenharmony_ci	priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
22858c2ecf20Sopenharmony_ci	priv_ep->wMaxPacketSize =  usb_endpoint_maxp(desc);
22868c2ecf20Sopenharmony_ci	priv_ep->mult = USB_EP_MAXP_MULT(priv_ep->wMaxPacketSize);
22878c2ecf20Sopenharmony_ci	priv_ep->wMaxPacketSize &= USB_ENDPOINT_MAXP_MASK;
22888c2ecf20Sopenharmony_ci	if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && comp_desc) {
22898c2ecf20Sopenharmony_ci		priv_ep->mult =  USB_SS_MULT(comp_desc->bmAttributes) - 1;
22908c2ecf20Sopenharmony_ci		priv_ep->bMaxBurst = comp_desc->bMaxBurst;
22918c2ecf20Sopenharmony_ci	}
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv_dev->lock, flags);
22948c2ecf20Sopenharmony_ci	return &priv_ep->endpoint;
22958c2ecf20Sopenharmony_ci}
22968c2ecf20Sopenharmony_ci
22978c2ecf20Sopenharmony_ci/**
22988c2ecf20Sopenharmony_ci * cdns3_gadget_ep_alloc_request Allocates request
22998c2ecf20Sopenharmony_ci * @ep: endpoint object associated with request
23008c2ecf20Sopenharmony_ci * @gfp_flags: gfp flags
23018c2ecf20Sopenharmony_ci *
23028c2ecf20Sopenharmony_ci * Returns allocated request address, NULL on allocation error
23038c2ecf20Sopenharmony_ci */
23048c2ecf20Sopenharmony_cistruct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
23058c2ecf20Sopenharmony_ci						  gfp_t gfp_flags)
23068c2ecf20Sopenharmony_ci{
23078c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
23088c2ecf20Sopenharmony_ci	struct cdns3_request *priv_req;
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_ci	priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
23118c2ecf20Sopenharmony_ci	if (!priv_req)
23128c2ecf20Sopenharmony_ci		return NULL;
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	priv_req->priv_ep = priv_ep;
23158c2ecf20Sopenharmony_ci
23168c2ecf20Sopenharmony_ci	trace_cdns3_alloc_request(priv_req);
23178c2ecf20Sopenharmony_ci	return &priv_req->request;
23188c2ecf20Sopenharmony_ci}
23198c2ecf20Sopenharmony_ci
23208c2ecf20Sopenharmony_ci/**
23218c2ecf20Sopenharmony_ci * cdns3_gadget_ep_free_request Free memory occupied by request
23228c2ecf20Sopenharmony_ci * @ep: endpoint object associated with request
23238c2ecf20Sopenharmony_ci * @request: request to free memory
23248c2ecf20Sopenharmony_ci */
23258c2ecf20Sopenharmony_civoid cdns3_gadget_ep_free_request(struct usb_ep *ep,
23268c2ecf20Sopenharmony_ci				  struct usb_request *request)
23278c2ecf20Sopenharmony_ci{
23288c2ecf20Sopenharmony_ci	struct cdns3_request *priv_req = to_cdns3_request(request);
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci	if (priv_req->aligned_buf)
23318c2ecf20Sopenharmony_ci		priv_req->aligned_buf->in_use = 0;
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci	trace_cdns3_free_request(priv_req);
23348c2ecf20Sopenharmony_ci	kfree(priv_req);
23358c2ecf20Sopenharmony_ci}
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_ci/**
23388c2ecf20Sopenharmony_ci * cdns3_gadget_ep_enable Enable endpoint
23398c2ecf20Sopenharmony_ci * @ep: endpoint object
23408c2ecf20Sopenharmony_ci * @desc: endpoint descriptor
23418c2ecf20Sopenharmony_ci *
23428c2ecf20Sopenharmony_ci * Returns 0 on success, error code elsewhere
23438c2ecf20Sopenharmony_ci */
23448c2ecf20Sopenharmony_cistatic int cdns3_gadget_ep_enable(struct usb_ep *ep,
23458c2ecf20Sopenharmony_ci				  const struct usb_endpoint_descriptor *desc)
23468c2ecf20Sopenharmony_ci{
23478c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep;
23488c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev;
23498c2ecf20Sopenharmony_ci	const struct usb_ss_ep_comp_descriptor *comp_desc;
23508c2ecf20Sopenharmony_ci	u32 reg = EP_STS_EN_TRBERREN;
23518c2ecf20Sopenharmony_ci	u32 bEndpointAddress;
23528c2ecf20Sopenharmony_ci	unsigned long flags;
23538c2ecf20Sopenharmony_ci	int enable = 1;
23548c2ecf20Sopenharmony_ci	int ret = 0;
23558c2ecf20Sopenharmony_ci	int val;
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci	if (!ep) {
23588c2ecf20Sopenharmony_ci		pr_debug("usbss: ep not configured?\n");
23598c2ecf20Sopenharmony_ci		return -EINVAL;
23608c2ecf20Sopenharmony_ci	}
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	priv_ep = ep_to_cdns3_ep(ep);
23638c2ecf20Sopenharmony_ci	priv_dev = priv_ep->cdns3_dev;
23648c2ecf20Sopenharmony_ci	comp_desc = priv_ep->endpoint.comp_desc;
23658c2ecf20Sopenharmony_ci
23668c2ecf20Sopenharmony_ci	if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
23678c2ecf20Sopenharmony_ci		dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
23688c2ecf20Sopenharmony_ci		return -EINVAL;
23698c2ecf20Sopenharmony_ci	}
23708c2ecf20Sopenharmony_ci
23718c2ecf20Sopenharmony_ci	if (!desc->wMaxPacketSize) {
23728c2ecf20Sopenharmony_ci		dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n");
23738c2ecf20Sopenharmony_ci		return -EINVAL;
23748c2ecf20Sopenharmony_ci	}
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_ci	if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED,
23778c2ecf20Sopenharmony_ci			  "%s is already enabled\n", priv_ep->name))
23788c2ecf20Sopenharmony_ci		return 0;
23798c2ecf20Sopenharmony_ci
23808c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv_dev->lock, flags);
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci	priv_ep->endpoint.desc = desc;
23838c2ecf20Sopenharmony_ci	priv_ep->type = usb_endpoint_type(desc);
23848c2ecf20Sopenharmony_ci	priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci	if (priv_ep->interval > ISO_MAX_INTERVAL &&
23878c2ecf20Sopenharmony_ci	    priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
23888c2ecf20Sopenharmony_ci		dev_err(priv_dev->dev, "Driver is limited to %d period\n",
23898c2ecf20Sopenharmony_ci			ISO_MAX_INTERVAL);
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_ci		ret =  -EINVAL;
23928c2ecf20Sopenharmony_ci		goto exit;
23938c2ecf20Sopenharmony_ci	}
23948c2ecf20Sopenharmony_ci
23958c2ecf20Sopenharmony_ci	bEndpointAddress = priv_ep->num | priv_ep->dir;
23968c2ecf20Sopenharmony_ci	cdns3_select_ep(priv_dev, bEndpointAddress);
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_ci	/*
23998c2ecf20Sopenharmony_ci	 * For some versions of controller at some point during ISO OUT traffic
24008c2ecf20Sopenharmony_ci	 * DMA reads Transfer Ring for the EP which has never got doorbell.
24018c2ecf20Sopenharmony_ci	 * This issue was detected only on simulation, but to avoid this issue
24028c2ecf20Sopenharmony_ci	 * driver add protection against it. To fix it driver enable ISO OUT
24038c2ecf20Sopenharmony_ci	 * endpoint before setting DRBL. This special treatment of ISO OUT
24048c2ecf20Sopenharmony_ci	 * endpoints are recommended by controller specification.
24058c2ecf20Sopenharmony_ci	 */
24068c2ecf20Sopenharmony_ci	if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir)
24078c2ecf20Sopenharmony_ci		enable = 0;
24088c2ecf20Sopenharmony_ci
24098c2ecf20Sopenharmony_ci	if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
24108c2ecf20Sopenharmony_ci		/*
24118c2ecf20Sopenharmony_ci		 * Enable stream support (SS mode) related interrupts
24128c2ecf20Sopenharmony_ci		 * in EP_STS_EN Register
24138c2ecf20Sopenharmony_ci		 */
24148c2ecf20Sopenharmony_ci		if (priv_dev->gadget.speed >= USB_SPEED_SUPER) {
24158c2ecf20Sopenharmony_ci			reg |= EP_STS_EN_IOTEN | EP_STS_EN_PRIMEEEN |
24168c2ecf20Sopenharmony_ci				EP_STS_EN_SIDERREN | EP_STS_EN_MD_EXITEN |
24178c2ecf20Sopenharmony_ci				EP_STS_EN_STREAMREN;
24188c2ecf20Sopenharmony_ci			priv_ep->use_streams = true;
24198c2ecf20Sopenharmony_ci			ret = cdns3_ep_config(priv_ep, enable);
24208c2ecf20Sopenharmony_ci			priv_dev->using_streams |= true;
24218c2ecf20Sopenharmony_ci		}
24228c2ecf20Sopenharmony_ci	} else {
24238c2ecf20Sopenharmony_ci		ret = cdns3_ep_config(priv_ep, enable);
24248c2ecf20Sopenharmony_ci	}
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_ci	if (ret)
24278c2ecf20Sopenharmony_ci		goto exit;
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	ret = cdns3_allocate_trb_pool(priv_ep);
24308c2ecf20Sopenharmony_ci	if (ret)
24318c2ecf20Sopenharmony_ci		goto exit;
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci	bEndpointAddress = priv_ep->num | priv_ep->dir;
24348c2ecf20Sopenharmony_ci	cdns3_select_ep(priv_dev, bEndpointAddress);
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_ci	trace_cdns3_gadget_ep_enable(priv_ep);
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci	writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci	ret = readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
24418c2ecf20Sopenharmony_ci					!(val & (EP_CMD_CSTALL | EP_CMD_EPRST)),
24428c2ecf20Sopenharmony_ci					1, 1000);
24438c2ecf20Sopenharmony_ci
24448c2ecf20Sopenharmony_ci	if (unlikely(ret)) {
24458c2ecf20Sopenharmony_ci		cdns3_free_trb_pool(priv_ep);
24468c2ecf20Sopenharmony_ci		ret =  -EINVAL;
24478c2ecf20Sopenharmony_ci		goto exit;
24488c2ecf20Sopenharmony_ci	}
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_ci	/* enable interrupt for selected endpoint */
24518c2ecf20Sopenharmony_ci	cdns3_set_register_bit(&priv_dev->regs->ep_ien,
24528c2ecf20Sopenharmony_ci			       BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
24538c2ecf20Sopenharmony_ci
24548c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver < DEV_VER_V2)
24558c2ecf20Sopenharmony_ci		cdns3_wa2_enable_detection(priv_dev, priv_ep, reg);
24568c2ecf20Sopenharmony_ci
24578c2ecf20Sopenharmony_ci	writel(reg, &priv_dev->regs->ep_sts_en);
24588c2ecf20Sopenharmony_ci
24598c2ecf20Sopenharmony_ci	ep->desc = desc;
24608c2ecf20Sopenharmony_ci	priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALLED | EP_STALL_PENDING |
24618c2ecf20Sopenharmony_ci			    EP_QUIRK_ISO_OUT_EN | EP_QUIRK_EXTRA_BUF_EN);
24628c2ecf20Sopenharmony_ci	priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
24638c2ecf20Sopenharmony_ci	priv_ep->wa1_set = 0;
24648c2ecf20Sopenharmony_ci	priv_ep->enqueue = 0;
24658c2ecf20Sopenharmony_ci	priv_ep->dequeue = 0;
24668c2ecf20Sopenharmony_ci	reg = readl(&priv_dev->regs->ep_sts);
24678c2ecf20Sopenharmony_ci	priv_ep->pcs = !!EP_STS_CCS(reg);
24688c2ecf20Sopenharmony_ci	priv_ep->ccs = !!EP_STS_CCS(reg);
24698c2ecf20Sopenharmony_ci	/* one TRB is reserved for link TRB used in DMULT mode*/
24708c2ecf20Sopenharmony_ci	priv_ep->free_trbs = priv_ep->num_trbs - 1;
24718c2ecf20Sopenharmony_ciexit:
24728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv_dev->lock, flags);
24738c2ecf20Sopenharmony_ci
24748c2ecf20Sopenharmony_ci	return ret;
24758c2ecf20Sopenharmony_ci}
24768c2ecf20Sopenharmony_ci
24778c2ecf20Sopenharmony_ci/**
24788c2ecf20Sopenharmony_ci * cdns3_gadget_ep_disable Disable endpoint
24798c2ecf20Sopenharmony_ci * @ep: endpoint object
24808c2ecf20Sopenharmony_ci *
24818c2ecf20Sopenharmony_ci * Returns 0 on success, error code elsewhere
24828c2ecf20Sopenharmony_ci */
24838c2ecf20Sopenharmony_cistatic int cdns3_gadget_ep_disable(struct usb_ep *ep)
24848c2ecf20Sopenharmony_ci{
24858c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep;
24868c2ecf20Sopenharmony_ci	struct cdns3_request *priv_req;
24878c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev;
24888c2ecf20Sopenharmony_ci	struct usb_request *request;
24898c2ecf20Sopenharmony_ci	unsigned long flags;
24908c2ecf20Sopenharmony_ci	int ret = 0;
24918c2ecf20Sopenharmony_ci	u32 ep_cfg;
24928c2ecf20Sopenharmony_ci	int val;
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	if (!ep) {
24958c2ecf20Sopenharmony_ci		pr_err("usbss: invalid parameters\n");
24968c2ecf20Sopenharmony_ci		return -EINVAL;
24978c2ecf20Sopenharmony_ci	}
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	priv_ep = ep_to_cdns3_ep(ep);
25008c2ecf20Sopenharmony_ci	priv_dev = priv_ep->cdns3_dev;
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED),
25038c2ecf20Sopenharmony_ci			  "%s is already disabled\n", priv_ep->name))
25048c2ecf20Sopenharmony_ci		return 0;
25058c2ecf20Sopenharmony_ci
25068c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv_dev->lock, flags);
25078c2ecf20Sopenharmony_ci
25088c2ecf20Sopenharmony_ci	trace_cdns3_gadget_ep_disable(priv_ep);
25098c2ecf20Sopenharmony_ci
25108c2ecf20Sopenharmony_ci	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
25118c2ecf20Sopenharmony_ci
25128c2ecf20Sopenharmony_ci	ep_cfg = readl(&priv_dev->regs->ep_cfg);
25138c2ecf20Sopenharmony_ci	ep_cfg &= ~EP_CFG_ENABLE;
25148c2ecf20Sopenharmony_ci	writel(ep_cfg, &priv_dev->regs->ep_cfg);
25158c2ecf20Sopenharmony_ci
25168c2ecf20Sopenharmony_ci	/**
25178c2ecf20Sopenharmony_ci	 * Driver needs some time before resetting endpoint.
25188c2ecf20Sopenharmony_ci	 * It need waits for clearing DBUSY bit or for timeout expired.
25198c2ecf20Sopenharmony_ci	 * 10us is enough time for controller to stop transfer.
25208c2ecf20Sopenharmony_ci	 */
25218c2ecf20Sopenharmony_ci	readl_poll_timeout_atomic(&priv_dev->regs->ep_sts, val,
25228c2ecf20Sopenharmony_ci				  !(val & EP_STS_DBUSY), 1, 10);
25238c2ecf20Sopenharmony_ci	writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
25248c2ecf20Sopenharmony_ci
25258c2ecf20Sopenharmony_ci	readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
25268c2ecf20Sopenharmony_ci				  !(val & (EP_CMD_CSTALL | EP_CMD_EPRST)),
25278c2ecf20Sopenharmony_ci				  1, 1000);
25288c2ecf20Sopenharmony_ci	if (unlikely(ret))
25298c2ecf20Sopenharmony_ci		dev_err(priv_dev->dev, "Timeout: %s resetting failed.\n",
25308c2ecf20Sopenharmony_ci			priv_ep->name);
25318c2ecf20Sopenharmony_ci
25328c2ecf20Sopenharmony_ci	while (!list_empty(&priv_ep->pending_req_list)) {
25338c2ecf20Sopenharmony_ci		request = cdns3_next_request(&priv_ep->pending_req_list);
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_ci		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
25368c2ecf20Sopenharmony_ci				      -ESHUTDOWN);
25378c2ecf20Sopenharmony_ci	}
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_ci	while (!list_empty(&priv_ep->wa2_descmiss_req_list)) {
25408c2ecf20Sopenharmony_ci		priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list);
25418c2ecf20Sopenharmony_ci
25428c2ecf20Sopenharmony_ci		kfree(priv_req->request.buf);
25438c2ecf20Sopenharmony_ci		cdns3_gadget_ep_free_request(&priv_ep->endpoint,
25448c2ecf20Sopenharmony_ci					     &priv_req->request);
25458c2ecf20Sopenharmony_ci		list_del_init(&priv_req->list);
25468c2ecf20Sopenharmony_ci		--priv_ep->wa2_counter;
25478c2ecf20Sopenharmony_ci	}
25488c2ecf20Sopenharmony_ci
25498c2ecf20Sopenharmony_ci	while (!list_empty(&priv_ep->deferred_req_list)) {
25508c2ecf20Sopenharmony_ci		request = cdns3_next_request(&priv_ep->deferred_req_list);
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
25538c2ecf20Sopenharmony_ci				      -ESHUTDOWN);
25548c2ecf20Sopenharmony_ci	}
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci	priv_ep->descmis_req = NULL;
25578c2ecf20Sopenharmony_ci
25588c2ecf20Sopenharmony_ci	ep->desc = NULL;
25598c2ecf20Sopenharmony_ci	priv_ep->flags &= ~EP_ENABLED;
25608c2ecf20Sopenharmony_ci	priv_ep->use_streams = false;
25618c2ecf20Sopenharmony_ci
25628c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv_dev->lock, flags);
25638c2ecf20Sopenharmony_ci
25648c2ecf20Sopenharmony_ci	return ret;
25658c2ecf20Sopenharmony_ci}
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_ci/**
25688c2ecf20Sopenharmony_ci * cdns3_gadget_ep_queue Transfer data on endpoint
25698c2ecf20Sopenharmony_ci * @ep: endpoint object
25708c2ecf20Sopenharmony_ci * @request: request object
25718c2ecf20Sopenharmony_ci * @gfp_flags: gfp flags
25728c2ecf20Sopenharmony_ci *
25738c2ecf20Sopenharmony_ci * Returns 0 on success, error code elsewhere
25748c2ecf20Sopenharmony_ci */
25758c2ecf20Sopenharmony_cistatic int __cdns3_gadget_ep_queue(struct usb_ep *ep,
25768c2ecf20Sopenharmony_ci				   struct usb_request *request,
25778c2ecf20Sopenharmony_ci				   gfp_t gfp_flags)
25788c2ecf20Sopenharmony_ci{
25798c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
25808c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
25818c2ecf20Sopenharmony_ci	struct cdns3_request *priv_req;
25828c2ecf20Sopenharmony_ci	int ret = 0;
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_ci	request->actual = 0;
25858c2ecf20Sopenharmony_ci	request->status = -EINPROGRESS;
25868c2ecf20Sopenharmony_ci	priv_req = to_cdns3_request(request);
25878c2ecf20Sopenharmony_ci	trace_cdns3_ep_queue(priv_req);
25888c2ecf20Sopenharmony_ci
25898c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver < DEV_VER_V2) {
25908c2ecf20Sopenharmony_ci		ret = cdns3_wa2_gadget_ep_queue(priv_dev, priv_ep,
25918c2ecf20Sopenharmony_ci						priv_req);
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_ci		if (ret == EINPROGRESS)
25948c2ecf20Sopenharmony_ci			return 0;
25958c2ecf20Sopenharmony_ci	}
25968c2ecf20Sopenharmony_ci
25978c2ecf20Sopenharmony_ci	ret = cdns3_prepare_aligned_request_buf(priv_req);
25988c2ecf20Sopenharmony_ci	if (ret < 0)
25998c2ecf20Sopenharmony_ci		return ret;
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_ci	ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
26028c2ecf20Sopenharmony_ci					    usb_endpoint_dir_in(ep->desc));
26038c2ecf20Sopenharmony_ci	if (ret)
26048c2ecf20Sopenharmony_ci		return ret;
26058c2ecf20Sopenharmony_ci
26068c2ecf20Sopenharmony_ci	list_add_tail(&request->list, &priv_ep->deferred_req_list);
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci	/*
26098c2ecf20Sopenharmony_ci	 * For stream capable endpoint if prime irq flag is set then only start
26108c2ecf20Sopenharmony_ci	 * request.
26118c2ecf20Sopenharmony_ci	 * If hardware endpoint configuration has not been set yet then
26128c2ecf20Sopenharmony_ci	 * just queue request in deferred list. Transfer will be started in
26138c2ecf20Sopenharmony_ci	 * cdns3_set_hw_configuration.
26148c2ecf20Sopenharmony_ci	 */
26158c2ecf20Sopenharmony_ci	if (!request->stream_id) {
26168c2ecf20Sopenharmony_ci		if (priv_dev->hw_configured_flag &&
26178c2ecf20Sopenharmony_ci		    !(priv_ep->flags & EP_STALLED) &&
26188c2ecf20Sopenharmony_ci		    !(priv_ep->flags & EP_STALL_PENDING))
26198c2ecf20Sopenharmony_ci			cdns3_start_all_request(priv_dev, priv_ep);
26208c2ecf20Sopenharmony_ci	} else {
26218c2ecf20Sopenharmony_ci		if (priv_dev->hw_configured_flag && priv_ep->prime_flag)
26228c2ecf20Sopenharmony_ci			cdns3_start_all_request(priv_dev, priv_ep);
26238c2ecf20Sopenharmony_ci	}
26248c2ecf20Sopenharmony_ci
26258c2ecf20Sopenharmony_ci	return 0;
26268c2ecf20Sopenharmony_ci}
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_cistatic int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
26298c2ecf20Sopenharmony_ci				 gfp_t gfp_flags)
26308c2ecf20Sopenharmony_ci{
26318c2ecf20Sopenharmony_ci	struct usb_request *zlp_request;
26328c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep;
26338c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev;
26348c2ecf20Sopenharmony_ci	unsigned long flags;
26358c2ecf20Sopenharmony_ci	int ret;
26368c2ecf20Sopenharmony_ci
26378c2ecf20Sopenharmony_ci	if (!request || !ep)
26388c2ecf20Sopenharmony_ci		return -EINVAL;
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci	priv_ep = ep_to_cdns3_ep(ep);
26418c2ecf20Sopenharmony_ci	priv_dev = priv_ep->cdns3_dev;
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv_dev->lock, flags);
26448c2ecf20Sopenharmony_ci
26458c2ecf20Sopenharmony_ci	ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
26468c2ecf20Sopenharmony_ci
26478c2ecf20Sopenharmony_ci	if (ret == 0 && request->zero && request->length &&
26488c2ecf20Sopenharmony_ci	    (request->length % ep->maxpacket == 0)) {
26498c2ecf20Sopenharmony_ci		struct cdns3_request *priv_req;
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ci		zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
26528c2ecf20Sopenharmony_ci		zlp_request->buf = priv_dev->zlp_buf;
26538c2ecf20Sopenharmony_ci		zlp_request->length = 0;
26548c2ecf20Sopenharmony_ci
26558c2ecf20Sopenharmony_ci		priv_req = to_cdns3_request(zlp_request);
26568c2ecf20Sopenharmony_ci		priv_req->flags |= REQUEST_ZLP;
26578c2ecf20Sopenharmony_ci
26588c2ecf20Sopenharmony_ci		dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n",
26598c2ecf20Sopenharmony_ci			priv_ep->name);
26608c2ecf20Sopenharmony_ci		ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags);
26618c2ecf20Sopenharmony_ci	}
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv_dev->lock, flags);
26648c2ecf20Sopenharmony_ci	return ret;
26658c2ecf20Sopenharmony_ci}
26668c2ecf20Sopenharmony_ci
26678c2ecf20Sopenharmony_ci/**
26688c2ecf20Sopenharmony_ci * cdns3_gadget_ep_dequeue Remove request from transfer queue
26698c2ecf20Sopenharmony_ci * @ep: endpoint object associated with request
26708c2ecf20Sopenharmony_ci * @request: request object
26718c2ecf20Sopenharmony_ci *
26728c2ecf20Sopenharmony_ci * Returns 0 on success, error code elsewhere
26738c2ecf20Sopenharmony_ci */
26748c2ecf20Sopenharmony_ciint cdns3_gadget_ep_dequeue(struct usb_ep *ep,
26758c2ecf20Sopenharmony_ci			    struct usb_request *request)
26768c2ecf20Sopenharmony_ci{
26778c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
26788c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev;
26798c2ecf20Sopenharmony_ci	struct usb_request *req, *req_temp;
26808c2ecf20Sopenharmony_ci	struct cdns3_request *priv_req;
26818c2ecf20Sopenharmony_ci	struct cdns3_trb *link_trb;
26828c2ecf20Sopenharmony_ci	u8 req_on_hw_ring = 0;
26838c2ecf20Sopenharmony_ci	unsigned long flags;
26848c2ecf20Sopenharmony_ci	int ret = 0;
26858c2ecf20Sopenharmony_ci
26868c2ecf20Sopenharmony_ci	if (!ep || !request || !ep->desc)
26878c2ecf20Sopenharmony_ci		return -EINVAL;
26888c2ecf20Sopenharmony_ci
26898c2ecf20Sopenharmony_ci	priv_dev = priv_ep->cdns3_dev;
26908c2ecf20Sopenharmony_ci
26918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv_dev->lock, flags);
26928c2ecf20Sopenharmony_ci
26938c2ecf20Sopenharmony_ci	priv_req = to_cdns3_request(request);
26948c2ecf20Sopenharmony_ci
26958c2ecf20Sopenharmony_ci	trace_cdns3_ep_dequeue(priv_req);
26968c2ecf20Sopenharmony_ci
26978c2ecf20Sopenharmony_ci	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
26988c2ecf20Sopenharmony_ci
26998c2ecf20Sopenharmony_ci	list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
27008c2ecf20Sopenharmony_ci				 list) {
27018c2ecf20Sopenharmony_ci		if (request == req) {
27028c2ecf20Sopenharmony_ci			req_on_hw_ring = 1;
27038c2ecf20Sopenharmony_ci			goto found;
27048c2ecf20Sopenharmony_ci		}
27058c2ecf20Sopenharmony_ci	}
27068c2ecf20Sopenharmony_ci
27078c2ecf20Sopenharmony_ci	list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
27088c2ecf20Sopenharmony_ci				 list) {
27098c2ecf20Sopenharmony_ci		if (request == req)
27108c2ecf20Sopenharmony_ci			goto found;
27118c2ecf20Sopenharmony_ci	}
27128c2ecf20Sopenharmony_ci
27138c2ecf20Sopenharmony_ci	goto not_found;
27148c2ecf20Sopenharmony_ci
27158c2ecf20Sopenharmony_cifound:
27168c2ecf20Sopenharmony_ci	link_trb = priv_req->trb;
27178c2ecf20Sopenharmony_ci
27188c2ecf20Sopenharmony_ci	/* Update ring only if removed request is on pending_req_list list */
27198c2ecf20Sopenharmony_ci	if (req_on_hw_ring && link_trb) {
27208c2ecf20Sopenharmony_ci		link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma +
27218c2ecf20Sopenharmony_ci			((priv_req->end_trb + 1) * TRB_SIZE)));
27228c2ecf20Sopenharmony_ci		link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control) & TRB_CYCLE) |
27238c2ecf20Sopenharmony_ci				    TRB_TYPE(TRB_LINK) | TRB_CHAIN);
27248c2ecf20Sopenharmony_ci
27258c2ecf20Sopenharmony_ci		if (priv_ep->wa1_trb == priv_req->trb)
27268c2ecf20Sopenharmony_ci			cdns3_wa1_restore_cycle_bit(priv_ep);
27278c2ecf20Sopenharmony_ci	}
27288c2ecf20Sopenharmony_ci
27298c2ecf20Sopenharmony_ci	cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
27308c2ecf20Sopenharmony_ci
27318c2ecf20Sopenharmony_cinot_found:
27328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv_dev->lock, flags);
27338c2ecf20Sopenharmony_ci	return ret;
27348c2ecf20Sopenharmony_ci}
27358c2ecf20Sopenharmony_ci
27368c2ecf20Sopenharmony_ci/**
27378c2ecf20Sopenharmony_ci * __cdns3_gadget_ep_set_halt Sets stall on selected endpoint
27388c2ecf20Sopenharmony_ci * Should be called after acquiring spin_lock and selecting ep
27398c2ecf20Sopenharmony_ci * @priv_ep: endpoint object to set stall on.
27408c2ecf20Sopenharmony_ci */
27418c2ecf20Sopenharmony_civoid __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep)
27428c2ecf20Sopenharmony_ci{
27438c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
27448c2ecf20Sopenharmony_ci
27458c2ecf20Sopenharmony_ci	trace_cdns3_halt(priv_ep, 1, 0);
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_ci	if (!(priv_ep->flags & EP_STALLED)) {
27488c2ecf20Sopenharmony_ci		u32 ep_sts_reg = readl(&priv_dev->regs->ep_sts);
27498c2ecf20Sopenharmony_ci
27508c2ecf20Sopenharmony_ci		if (!(ep_sts_reg & EP_STS_DBUSY))
27518c2ecf20Sopenharmony_ci			cdns3_ep_stall_flush(priv_ep);
27528c2ecf20Sopenharmony_ci		else
27538c2ecf20Sopenharmony_ci			priv_ep->flags |= EP_STALL_PENDING;
27548c2ecf20Sopenharmony_ci	}
27558c2ecf20Sopenharmony_ci}
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci/**
27588c2ecf20Sopenharmony_ci * __cdns3_gadget_ep_clear_halt Clears stall on selected endpoint
27598c2ecf20Sopenharmony_ci * Should be called after acquiring spin_lock and selecting ep
27608c2ecf20Sopenharmony_ci * @priv_ep: endpoint object to clear stall on
27618c2ecf20Sopenharmony_ci */
27628c2ecf20Sopenharmony_ciint __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep)
27638c2ecf20Sopenharmony_ci{
27648c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
27658c2ecf20Sopenharmony_ci	struct usb_request *request;
27668c2ecf20Sopenharmony_ci	struct cdns3_request *priv_req;
27678c2ecf20Sopenharmony_ci	struct cdns3_trb *trb = NULL;
27688c2ecf20Sopenharmony_ci	struct cdns3_trb trb_tmp;
27698c2ecf20Sopenharmony_ci	int ret;
27708c2ecf20Sopenharmony_ci	int val;
27718c2ecf20Sopenharmony_ci
27728c2ecf20Sopenharmony_ci	trace_cdns3_halt(priv_ep, 0, 0);
27738c2ecf20Sopenharmony_ci
27748c2ecf20Sopenharmony_ci	request = cdns3_next_request(&priv_ep->pending_req_list);
27758c2ecf20Sopenharmony_ci	if (request) {
27768c2ecf20Sopenharmony_ci		priv_req = to_cdns3_request(request);
27778c2ecf20Sopenharmony_ci		trb = priv_req->trb;
27788c2ecf20Sopenharmony_ci		if (trb) {
27798c2ecf20Sopenharmony_ci			trb_tmp = *trb;
27808c2ecf20Sopenharmony_ci			trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE);
27818c2ecf20Sopenharmony_ci		}
27828c2ecf20Sopenharmony_ci	}
27838c2ecf20Sopenharmony_ci
27848c2ecf20Sopenharmony_ci	writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
27858c2ecf20Sopenharmony_ci
27868c2ecf20Sopenharmony_ci	/* wait for EPRST cleared */
27878c2ecf20Sopenharmony_ci	ret = readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
27888c2ecf20Sopenharmony_ci					!(val & EP_CMD_EPRST), 1, 100);
27898c2ecf20Sopenharmony_ci	if (ret)
27908c2ecf20Sopenharmony_ci		return -EINVAL;
27918c2ecf20Sopenharmony_ci
27928c2ecf20Sopenharmony_ci	priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING);
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_ci	if (request) {
27958c2ecf20Sopenharmony_ci		if (trb)
27968c2ecf20Sopenharmony_ci			*trb = trb_tmp;
27978c2ecf20Sopenharmony_ci
27988c2ecf20Sopenharmony_ci		cdns3_rearm_transfer(priv_ep, 1);
27998c2ecf20Sopenharmony_ci	}
28008c2ecf20Sopenharmony_ci
28018c2ecf20Sopenharmony_ci	cdns3_start_all_request(priv_dev, priv_ep);
28028c2ecf20Sopenharmony_ci	return ret;
28038c2ecf20Sopenharmony_ci}
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_ci/**
28068c2ecf20Sopenharmony_ci * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
28078c2ecf20Sopenharmony_ci * @ep: endpoint object to set/clear stall on
28088c2ecf20Sopenharmony_ci * @value: 1 for set stall, 0 for clear stall
28098c2ecf20Sopenharmony_ci *
28108c2ecf20Sopenharmony_ci * Returns 0 on success, error code elsewhere
28118c2ecf20Sopenharmony_ci */
28128c2ecf20Sopenharmony_ciint cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
28138c2ecf20Sopenharmony_ci{
28148c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
28158c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
28168c2ecf20Sopenharmony_ci	unsigned long flags;
28178c2ecf20Sopenharmony_ci	int ret = 0;
28188c2ecf20Sopenharmony_ci
28198c2ecf20Sopenharmony_ci	if (!(priv_ep->flags & EP_ENABLED))
28208c2ecf20Sopenharmony_ci		return -EPERM;
28218c2ecf20Sopenharmony_ci
28228c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv_dev->lock, flags);
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_ci	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
28258c2ecf20Sopenharmony_ci
28268c2ecf20Sopenharmony_ci	if (!value) {
28278c2ecf20Sopenharmony_ci		priv_ep->flags &= ~EP_WEDGE;
28288c2ecf20Sopenharmony_ci		ret = __cdns3_gadget_ep_clear_halt(priv_ep);
28298c2ecf20Sopenharmony_ci	} else {
28308c2ecf20Sopenharmony_ci		__cdns3_gadget_ep_set_halt(priv_ep);
28318c2ecf20Sopenharmony_ci	}
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv_dev->lock, flags);
28348c2ecf20Sopenharmony_ci
28358c2ecf20Sopenharmony_ci	return ret;
28368c2ecf20Sopenharmony_ci}
28378c2ecf20Sopenharmony_ci
28388c2ecf20Sopenharmony_ciextern const struct usb_ep_ops cdns3_gadget_ep0_ops;
28398c2ecf20Sopenharmony_ci
28408c2ecf20Sopenharmony_cistatic const struct usb_ep_ops cdns3_gadget_ep_ops = {
28418c2ecf20Sopenharmony_ci	.enable = cdns3_gadget_ep_enable,
28428c2ecf20Sopenharmony_ci	.disable = cdns3_gadget_ep_disable,
28438c2ecf20Sopenharmony_ci	.alloc_request = cdns3_gadget_ep_alloc_request,
28448c2ecf20Sopenharmony_ci	.free_request = cdns3_gadget_ep_free_request,
28458c2ecf20Sopenharmony_ci	.queue = cdns3_gadget_ep_queue,
28468c2ecf20Sopenharmony_ci	.dequeue = cdns3_gadget_ep_dequeue,
28478c2ecf20Sopenharmony_ci	.set_halt = cdns3_gadget_ep_set_halt,
28488c2ecf20Sopenharmony_ci	.set_wedge = cdns3_gadget_ep_set_wedge,
28498c2ecf20Sopenharmony_ci};
28508c2ecf20Sopenharmony_ci
28518c2ecf20Sopenharmony_ci/**
28528c2ecf20Sopenharmony_ci * cdns3_gadget_get_frame Returns number of actual ITP frame
28538c2ecf20Sopenharmony_ci * @gadget: gadget object
28548c2ecf20Sopenharmony_ci *
28558c2ecf20Sopenharmony_ci * Returns number of actual ITP frame
28568c2ecf20Sopenharmony_ci */
28578c2ecf20Sopenharmony_cistatic int cdns3_gadget_get_frame(struct usb_gadget *gadget)
28588c2ecf20Sopenharmony_ci{
28598c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
28608c2ecf20Sopenharmony_ci
28618c2ecf20Sopenharmony_ci	return readl(&priv_dev->regs->usb_itpn);
28628c2ecf20Sopenharmony_ci}
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_ciint __cdns3_gadget_wakeup(struct cdns3_device *priv_dev)
28658c2ecf20Sopenharmony_ci{
28668c2ecf20Sopenharmony_ci	enum usb_device_speed speed;
28678c2ecf20Sopenharmony_ci
28688c2ecf20Sopenharmony_ci	speed = cdns3_get_speed(priv_dev);
28698c2ecf20Sopenharmony_ci
28708c2ecf20Sopenharmony_ci	if (speed >= USB_SPEED_SUPER)
28718c2ecf20Sopenharmony_ci		return 0;
28728c2ecf20Sopenharmony_ci
28738c2ecf20Sopenharmony_ci	/* Start driving resume signaling to indicate remote wakeup. */
28748c2ecf20Sopenharmony_ci	writel(USB_CONF_LGO_L0, &priv_dev->regs->usb_conf);
28758c2ecf20Sopenharmony_ci
28768c2ecf20Sopenharmony_ci	return 0;
28778c2ecf20Sopenharmony_ci}
28788c2ecf20Sopenharmony_ci
28798c2ecf20Sopenharmony_cistatic int cdns3_gadget_wakeup(struct usb_gadget *gadget)
28808c2ecf20Sopenharmony_ci{
28818c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
28828c2ecf20Sopenharmony_ci	unsigned long flags;
28838c2ecf20Sopenharmony_ci	int ret = 0;
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv_dev->lock, flags);
28868c2ecf20Sopenharmony_ci	ret = __cdns3_gadget_wakeup(priv_dev);
28878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv_dev->lock, flags);
28888c2ecf20Sopenharmony_ci	return ret;
28898c2ecf20Sopenharmony_ci}
28908c2ecf20Sopenharmony_ci
28918c2ecf20Sopenharmony_cistatic int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
28928c2ecf20Sopenharmony_ci					int is_selfpowered)
28938c2ecf20Sopenharmony_ci{
28948c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
28958c2ecf20Sopenharmony_ci	unsigned long flags;
28968c2ecf20Sopenharmony_ci
28978c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv_dev->lock, flags);
28988c2ecf20Sopenharmony_ci	priv_dev->is_selfpowered = !!is_selfpowered;
28998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv_dev->lock, flags);
29008c2ecf20Sopenharmony_ci	return 0;
29018c2ecf20Sopenharmony_ci}
29028c2ecf20Sopenharmony_ci
29038c2ecf20Sopenharmony_cistatic int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
29048c2ecf20Sopenharmony_ci{
29058c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
29068c2ecf20Sopenharmony_ci
29078c2ecf20Sopenharmony_ci	if (is_on) {
29088c2ecf20Sopenharmony_ci		writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
29098c2ecf20Sopenharmony_ci	} else {
29108c2ecf20Sopenharmony_ci		writel(~0, &priv_dev->regs->ep_ists);
29118c2ecf20Sopenharmony_ci		writel(~0, &priv_dev->regs->usb_ists);
29128c2ecf20Sopenharmony_ci		writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
29138c2ecf20Sopenharmony_ci	}
29148c2ecf20Sopenharmony_ci
29158c2ecf20Sopenharmony_ci	return 0;
29168c2ecf20Sopenharmony_ci}
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_cistatic void cdns3_gadget_config(struct cdns3_device *priv_dev)
29198c2ecf20Sopenharmony_ci{
29208c2ecf20Sopenharmony_ci	struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
29218c2ecf20Sopenharmony_ci	u32 reg;
29228c2ecf20Sopenharmony_ci
29238c2ecf20Sopenharmony_ci	cdns3_ep0_config(priv_dev);
29248c2ecf20Sopenharmony_ci
29258c2ecf20Sopenharmony_ci	/* enable interrupts for endpoint 0 (in and out) */
29268c2ecf20Sopenharmony_ci	writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
29278c2ecf20Sopenharmony_ci
29288c2ecf20Sopenharmony_ci	/*
29298c2ecf20Sopenharmony_ci	 * Driver needs to modify LFPS minimal U1 Exit time for DEV_VER_TI_V1
29308c2ecf20Sopenharmony_ci	 * revision of controller.
29318c2ecf20Sopenharmony_ci	 */
29328c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver == DEV_VER_TI_V1) {
29338c2ecf20Sopenharmony_ci		reg = readl(&regs->dbg_link1);
29348c2ecf20Sopenharmony_ci
29358c2ecf20Sopenharmony_ci		reg &= ~DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_MASK;
29368c2ecf20Sopenharmony_ci		reg |= DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(0x55) |
29378c2ecf20Sopenharmony_ci		       DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET;
29388c2ecf20Sopenharmony_ci		writel(reg, &regs->dbg_link1);
29398c2ecf20Sopenharmony_ci	}
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_ci	/*
29428c2ecf20Sopenharmony_ci	 * By default some platforms has set protected access to memory.
29438c2ecf20Sopenharmony_ci	 * This cause problem with cache, so driver restore non-secure
29448c2ecf20Sopenharmony_ci	 * access to memory.
29458c2ecf20Sopenharmony_ci	 */
29468c2ecf20Sopenharmony_ci	reg = readl(&regs->dma_axi_ctrl);
29478c2ecf20Sopenharmony_ci	reg |= DMA_AXI_CTRL_MARPROT(DMA_AXI_CTRL_NON_SECURE) |
29488c2ecf20Sopenharmony_ci	       DMA_AXI_CTRL_MAWPROT(DMA_AXI_CTRL_NON_SECURE);
29498c2ecf20Sopenharmony_ci	writel(reg, &regs->dma_axi_ctrl);
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_ci	/* enable generic interrupt*/
29528c2ecf20Sopenharmony_ci	writel(USB_IEN_INIT, &regs->usb_ien);
29538c2ecf20Sopenharmony_ci	writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
29548c2ecf20Sopenharmony_ci	/*  keep Fast Access bit */
29558c2ecf20Sopenharmony_ci	writel(PUSB_PWR_FST_REG_ACCESS, &priv_dev->regs->usb_pwr);
29568c2ecf20Sopenharmony_ci
29578c2ecf20Sopenharmony_ci	cdns3_configure_dmult(priv_dev, NULL);
29588c2ecf20Sopenharmony_ci}
29598c2ecf20Sopenharmony_ci
29608c2ecf20Sopenharmony_ci/**
29618c2ecf20Sopenharmony_ci * cdns3_gadget_udc_start Gadget start
29628c2ecf20Sopenharmony_ci * @gadget: gadget object
29638c2ecf20Sopenharmony_ci * @driver: driver which operates on this gadget
29648c2ecf20Sopenharmony_ci *
29658c2ecf20Sopenharmony_ci * Returns 0 on success, error code elsewhere
29668c2ecf20Sopenharmony_ci */
29678c2ecf20Sopenharmony_cistatic int cdns3_gadget_udc_start(struct usb_gadget *gadget,
29688c2ecf20Sopenharmony_ci				  struct usb_gadget_driver *driver)
29698c2ecf20Sopenharmony_ci{
29708c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
29718c2ecf20Sopenharmony_ci	unsigned long flags;
29728c2ecf20Sopenharmony_ci	enum usb_device_speed max_speed = driver->max_speed;
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv_dev->lock, flags);
29758c2ecf20Sopenharmony_ci	priv_dev->gadget_driver = driver;
29768c2ecf20Sopenharmony_ci
29778c2ecf20Sopenharmony_ci	/* limit speed if necessary */
29788c2ecf20Sopenharmony_ci	max_speed = min(driver->max_speed, gadget->max_speed);
29798c2ecf20Sopenharmony_ci
29808c2ecf20Sopenharmony_ci	switch (max_speed) {
29818c2ecf20Sopenharmony_ci	case USB_SPEED_FULL:
29828c2ecf20Sopenharmony_ci		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
29838c2ecf20Sopenharmony_ci		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
29848c2ecf20Sopenharmony_ci		break;
29858c2ecf20Sopenharmony_ci	case USB_SPEED_HIGH:
29868c2ecf20Sopenharmony_ci		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
29878c2ecf20Sopenharmony_ci		break;
29888c2ecf20Sopenharmony_ci	case USB_SPEED_SUPER:
29898c2ecf20Sopenharmony_ci		break;
29908c2ecf20Sopenharmony_ci	default:
29918c2ecf20Sopenharmony_ci		dev_err(priv_dev->dev,
29928c2ecf20Sopenharmony_ci			"invalid maximum_speed parameter %d\n",
29938c2ecf20Sopenharmony_ci			max_speed);
29948c2ecf20Sopenharmony_ci		fallthrough;
29958c2ecf20Sopenharmony_ci	case USB_SPEED_UNKNOWN:
29968c2ecf20Sopenharmony_ci		/* default to superspeed */
29978c2ecf20Sopenharmony_ci		max_speed = USB_SPEED_SUPER;
29988c2ecf20Sopenharmony_ci		break;
29998c2ecf20Sopenharmony_ci	}
30008c2ecf20Sopenharmony_ci
30018c2ecf20Sopenharmony_ci	cdns3_gadget_config(priv_dev);
30028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv_dev->lock, flags);
30038c2ecf20Sopenharmony_ci	return 0;
30048c2ecf20Sopenharmony_ci}
30058c2ecf20Sopenharmony_ci
30068c2ecf20Sopenharmony_ci/**
30078c2ecf20Sopenharmony_ci * cdns3_gadget_udc_stop Stops gadget
30088c2ecf20Sopenharmony_ci * @gadget: gadget object
30098c2ecf20Sopenharmony_ci *
30108c2ecf20Sopenharmony_ci * Returns 0
30118c2ecf20Sopenharmony_ci */
30128c2ecf20Sopenharmony_cistatic int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
30138c2ecf20Sopenharmony_ci{
30148c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
30158c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep;
30168c2ecf20Sopenharmony_ci	u32 bEndpointAddress;
30178c2ecf20Sopenharmony_ci	struct usb_ep *ep;
30188c2ecf20Sopenharmony_ci	int val;
30198c2ecf20Sopenharmony_ci
30208c2ecf20Sopenharmony_ci	priv_dev->gadget_driver = NULL;
30218c2ecf20Sopenharmony_ci
30228c2ecf20Sopenharmony_ci	priv_dev->onchip_used_size = 0;
30238c2ecf20Sopenharmony_ci	priv_dev->out_mem_is_allocated = 0;
30248c2ecf20Sopenharmony_ci	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
30258c2ecf20Sopenharmony_ci
30268c2ecf20Sopenharmony_ci	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
30278c2ecf20Sopenharmony_ci		priv_ep = ep_to_cdns3_ep(ep);
30288c2ecf20Sopenharmony_ci		bEndpointAddress = priv_ep->num | priv_ep->dir;
30298c2ecf20Sopenharmony_ci		cdns3_select_ep(priv_dev, bEndpointAddress);
30308c2ecf20Sopenharmony_ci		writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
30318c2ecf20Sopenharmony_ci		readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
30328c2ecf20Sopenharmony_ci					  !(val & EP_CMD_EPRST), 1, 100);
30338c2ecf20Sopenharmony_ci
30348c2ecf20Sopenharmony_ci		priv_ep->flags &= ~EP_CLAIMED;
30358c2ecf20Sopenharmony_ci	}
30368c2ecf20Sopenharmony_ci
30378c2ecf20Sopenharmony_ci	/* disable interrupt for device */
30388c2ecf20Sopenharmony_ci	writel(0, &priv_dev->regs->usb_ien);
30398c2ecf20Sopenharmony_ci	writel(0, &priv_dev->regs->usb_pwr);
30408c2ecf20Sopenharmony_ci	writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
30418c2ecf20Sopenharmony_ci
30428c2ecf20Sopenharmony_ci	return 0;
30438c2ecf20Sopenharmony_ci}
30448c2ecf20Sopenharmony_ci
30458c2ecf20Sopenharmony_ci/**
30468c2ecf20Sopenharmony_ci * cdns3_gadget_check_config - ensure cdns3 can support the USB configuration
30478c2ecf20Sopenharmony_ci * @gadget: pointer to the USB gadget
30488c2ecf20Sopenharmony_ci *
30498c2ecf20Sopenharmony_ci * Used to record the maximum number of endpoints being used in a USB composite
30508c2ecf20Sopenharmony_ci * device. (across all configurations)  This is to be used in the calculation
30518c2ecf20Sopenharmony_ci * of the TXFIFO sizes when resizing internal memory for individual endpoints.
30528c2ecf20Sopenharmony_ci * It will help ensured that the resizing logic reserves enough space for at
30538c2ecf20Sopenharmony_ci * least one max packet.
30548c2ecf20Sopenharmony_ci */
30558c2ecf20Sopenharmony_cistatic int cdns3_gadget_check_config(struct usb_gadget *gadget)
30568c2ecf20Sopenharmony_ci{
30578c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
30588c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep;
30598c2ecf20Sopenharmony_ci	struct usb_ep *ep;
30608c2ecf20Sopenharmony_ci	int n_in = 0;
30618c2ecf20Sopenharmony_ci	int iso = 0;
30628c2ecf20Sopenharmony_ci	int out = 1;
30638c2ecf20Sopenharmony_ci	int total;
30648c2ecf20Sopenharmony_ci	int n;
30658c2ecf20Sopenharmony_ci
30668c2ecf20Sopenharmony_ci	list_for_each_entry(ep, &gadget->ep_list, ep_list) {
30678c2ecf20Sopenharmony_ci		priv_ep = ep_to_cdns3_ep(ep);
30688c2ecf20Sopenharmony_ci		if (!(priv_ep->flags & EP_CLAIMED))
30698c2ecf20Sopenharmony_ci			continue;
30708c2ecf20Sopenharmony_ci
30718c2ecf20Sopenharmony_ci		n = (priv_ep->mult + 1) * (priv_ep->bMaxBurst + 1);
30728c2ecf20Sopenharmony_ci		if (ep->address & USB_DIR_IN) {
30738c2ecf20Sopenharmony_ci			/*
30748c2ecf20Sopenharmony_ci			 * ISO transfer: DMA start move data when get ISO, only transfer
30758c2ecf20Sopenharmony_ci			 * data as min(TD size, iso). No benefit for allocate bigger
30768c2ecf20Sopenharmony_ci			 * internal memory than 'iso'.
30778c2ecf20Sopenharmony_ci			 */
30788c2ecf20Sopenharmony_ci			if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
30798c2ecf20Sopenharmony_ci				iso += n;
30808c2ecf20Sopenharmony_ci			else
30818c2ecf20Sopenharmony_ci				n_in++;
30828c2ecf20Sopenharmony_ci		} else {
30838c2ecf20Sopenharmony_ci			if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
30848c2ecf20Sopenharmony_ci				out = max_t(int, out, n);
30858c2ecf20Sopenharmony_ci		}
30868c2ecf20Sopenharmony_ci	}
30878c2ecf20Sopenharmony_ci
30888c2ecf20Sopenharmony_ci	/* 2KB are reserved for EP0, 1KB for out*/
30898c2ecf20Sopenharmony_ci	total = 2 + n_in + out + iso;
30908c2ecf20Sopenharmony_ci
30918c2ecf20Sopenharmony_ci	if (total > priv_dev->onchip_buffers)
30928c2ecf20Sopenharmony_ci		return -ENOMEM;
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ci	priv_dev->ep_buf_size = (priv_dev->onchip_buffers - 2 - iso) / (n_in + out);
30958c2ecf20Sopenharmony_ci
30968c2ecf20Sopenharmony_ci	return 0;
30978c2ecf20Sopenharmony_ci}
30988c2ecf20Sopenharmony_ci
30998c2ecf20Sopenharmony_cistatic const struct usb_gadget_ops cdns3_gadget_ops = {
31008c2ecf20Sopenharmony_ci	.get_frame = cdns3_gadget_get_frame,
31018c2ecf20Sopenharmony_ci	.wakeup = cdns3_gadget_wakeup,
31028c2ecf20Sopenharmony_ci	.set_selfpowered = cdns3_gadget_set_selfpowered,
31038c2ecf20Sopenharmony_ci	.pullup = cdns3_gadget_pullup,
31048c2ecf20Sopenharmony_ci	.udc_start = cdns3_gadget_udc_start,
31058c2ecf20Sopenharmony_ci	.udc_stop = cdns3_gadget_udc_stop,
31068c2ecf20Sopenharmony_ci	.match_ep = cdns3_gadget_match_ep,
31078c2ecf20Sopenharmony_ci	.check_config = cdns3_gadget_check_config,
31088c2ecf20Sopenharmony_ci};
31098c2ecf20Sopenharmony_ci
31108c2ecf20Sopenharmony_cistatic void cdns3_free_all_eps(struct cdns3_device *priv_dev)
31118c2ecf20Sopenharmony_ci{
31128c2ecf20Sopenharmony_ci	int i;
31138c2ecf20Sopenharmony_ci
31148c2ecf20Sopenharmony_ci	/* ep0 OUT point to ep0 IN. */
31158c2ecf20Sopenharmony_ci	priv_dev->eps[16] = NULL;
31168c2ecf20Sopenharmony_ci
31178c2ecf20Sopenharmony_ci	for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
31188c2ecf20Sopenharmony_ci		if (priv_dev->eps[i]) {
31198c2ecf20Sopenharmony_ci			cdns3_free_trb_pool(priv_dev->eps[i]);
31208c2ecf20Sopenharmony_ci			devm_kfree(priv_dev->dev, priv_dev->eps[i]);
31218c2ecf20Sopenharmony_ci		}
31228c2ecf20Sopenharmony_ci}
31238c2ecf20Sopenharmony_ci
31248c2ecf20Sopenharmony_ci/**
31258c2ecf20Sopenharmony_ci * cdns3_init_eps Initializes software endpoints of gadget
31268c2ecf20Sopenharmony_ci * @priv_dev: extended gadget object
31278c2ecf20Sopenharmony_ci *
31288c2ecf20Sopenharmony_ci * Returns 0 on success, error code elsewhere
31298c2ecf20Sopenharmony_ci */
31308c2ecf20Sopenharmony_cistatic int cdns3_init_eps(struct cdns3_device *priv_dev)
31318c2ecf20Sopenharmony_ci{
31328c2ecf20Sopenharmony_ci	u32 ep_enabled_reg, iso_ep_reg;
31338c2ecf20Sopenharmony_ci	struct cdns3_endpoint *priv_ep;
31348c2ecf20Sopenharmony_ci	int ep_dir, ep_number;
31358c2ecf20Sopenharmony_ci	u32 ep_mask;
31368c2ecf20Sopenharmony_ci	int ret = 0;
31378c2ecf20Sopenharmony_ci	int i;
31388c2ecf20Sopenharmony_ci
31398c2ecf20Sopenharmony_ci	/* Read it from USB_CAP3 to USB_CAP5 */
31408c2ecf20Sopenharmony_ci	ep_enabled_reg = readl(&priv_dev->regs->usb_cap3);
31418c2ecf20Sopenharmony_ci	iso_ep_reg = readl(&priv_dev->regs->usb_cap4);
31428c2ecf20Sopenharmony_ci
31438c2ecf20Sopenharmony_ci	dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
31448c2ecf20Sopenharmony_ci
31458c2ecf20Sopenharmony_ci	for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
31468c2ecf20Sopenharmony_ci		ep_dir = i >> 4;	/* i div 16 */
31478c2ecf20Sopenharmony_ci		ep_number = i & 0xF;	/* i % 16 */
31488c2ecf20Sopenharmony_ci		ep_mask = BIT(i);
31498c2ecf20Sopenharmony_ci
31508c2ecf20Sopenharmony_ci		if (!(ep_enabled_reg & ep_mask))
31518c2ecf20Sopenharmony_ci			continue;
31528c2ecf20Sopenharmony_ci
31538c2ecf20Sopenharmony_ci		if (ep_dir && !ep_number) {
31548c2ecf20Sopenharmony_ci			priv_dev->eps[i] = priv_dev->eps[0];
31558c2ecf20Sopenharmony_ci			continue;
31568c2ecf20Sopenharmony_ci		}
31578c2ecf20Sopenharmony_ci
31588c2ecf20Sopenharmony_ci		priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
31598c2ecf20Sopenharmony_ci				       GFP_KERNEL);
31608c2ecf20Sopenharmony_ci		if (!priv_ep)
31618c2ecf20Sopenharmony_ci			goto err;
31628c2ecf20Sopenharmony_ci
31638c2ecf20Sopenharmony_ci		/* set parent of endpoint object */
31648c2ecf20Sopenharmony_ci		priv_ep->cdns3_dev = priv_dev;
31658c2ecf20Sopenharmony_ci		priv_dev->eps[i] = priv_ep;
31668c2ecf20Sopenharmony_ci		priv_ep->num = ep_number;
31678c2ecf20Sopenharmony_ci		priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT;
31688c2ecf20Sopenharmony_ci
31698c2ecf20Sopenharmony_ci		if (!ep_number) {
31708c2ecf20Sopenharmony_ci			ret = cdns3_init_ep0(priv_dev, priv_ep);
31718c2ecf20Sopenharmony_ci			if (ret) {
31728c2ecf20Sopenharmony_ci				dev_err(priv_dev->dev, "Failed to init ep0\n");
31738c2ecf20Sopenharmony_ci				goto err;
31748c2ecf20Sopenharmony_ci			}
31758c2ecf20Sopenharmony_ci		} else {
31768c2ecf20Sopenharmony_ci			snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s",
31778c2ecf20Sopenharmony_ci				 ep_number, !!ep_dir ? "in" : "out");
31788c2ecf20Sopenharmony_ci			priv_ep->endpoint.name = priv_ep->name;
31798c2ecf20Sopenharmony_ci
31808c2ecf20Sopenharmony_ci			usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
31818c2ecf20Sopenharmony_ci						   CDNS3_EP_MAX_PACKET_LIMIT);
31828c2ecf20Sopenharmony_ci			priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS;
31838c2ecf20Sopenharmony_ci			priv_ep->endpoint.ops = &cdns3_gadget_ep_ops;
31848c2ecf20Sopenharmony_ci			if (ep_dir)
31858c2ecf20Sopenharmony_ci				priv_ep->endpoint.caps.dir_in = 1;
31868c2ecf20Sopenharmony_ci			else
31878c2ecf20Sopenharmony_ci				priv_ep->endpoint.caps.dir_out = 1;
31888c2ecf20Sopenharmony_ci
31898c2ecf20Sopenharmony_ci			if (iso_ep_reg & ep_mask)
31908c2ecf20Sopenharmony_ci				priv_ep->endpoint.caps.type_iso = 1;
31918c2ecf20Sopenharmony_ci
31928c2ecf20Sopenharmony_ci			priv_ep->endpoint.caps.type_bulk = 1;
31938c2ecf20Sopenharmony_ci			priv_ep->endpoint.caps.type_int = 1;
31948c2ecf20Sopenharmony_ci
31958c2ecf20Sopenharmony_ci			list_add_tail(&priv_ep->endpoint.ep_list,
31968c2ecf20Sopenharmony_ci				      &priv_dev->gadget.ep_list);
31978c2ecf20Sopenharmony_ci		}
31988c2ecf20Sopenharmony_ci
31998c2ecf20Sopenharmony_ci		priv_ep->flags = 0;
32008c2ecf20Sopenharmony_ci
32018c2ecf20Sopenharmony_ci		dev_dbg(priv_dev->dev, "Initialized  %s support: %s %s\n",
32028c2ecf20Sopenharmony_ci			 priv_ep->name,
32038c2ecf20Sopenharmony_ci			 priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
32048c2ecf20Sopenharmony_ci			 priv_ep->endpoint.caps.type_iso ? "ISO" : "");
32058c2ecf20Sopenharmony_ci
32068c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&priv_ep->pending_req_list);
32078c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&priv_ep->deferred_req_list);
32088c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&priv_ep->wa2_descmiss_req_list);
32098c2ecf20Sopenharmony_ci	}
32108c2ecf20Sopenharmony_ci
32118c2ecf20Sopenharmony_ci	return 0;
32128c2ecf20Sopenharmony_cierr:
32138c2ecf20Sopenharmony_ci	cdns3_free_all_eps(priv_dev);
32148c2ecf20Sopenharmony_ci	return -ENOMEM;
32158c2ecf20Sopenharmony_ci}
32168c2ecf20Sopenharmony_ci
32178c2ecf20Sopenharmony_cistatic void cdns3_gadget_release(struct device *dev)
32188c2ecf20Sopenharmony_ci{
32198c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = container_of(dev,
32208c2ecf20Sopenharmony_ci			struct cdns3_device, gadget.dev);
32218c2ecf20Sopenharmony_ci
32228c2ecf20Sopenharmony_ci	kfree(priv_dev);
32238c2ecf20Sopenharmony_ci}
32248c2ecf20Sopenharmony_ci
32258c2ecf20Sopenharmony_civoid cdns3_gadget_exit(struct cdns3 *cdns)
32268c2ecf20Sopenharmony_ci{
32278c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev;
32288c2ecf20Sopenharmony_ci
32298c2ecf20Sopenharmony_ci	priv_dev = cdns->gadget_dev;
32308c2ecf20Sopenharmony_ci
32318c2ecf20Sopenharmony_ci
32328c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(cdns->dev);
32338c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(cdns->dev);
32348c2ecf20Sopenharmony_ci
32358c2ecf20Sopenharmony_ci	usb_del_gadget(&priv_dev->gadget);
32368c2ecf20Sopenharmony_ci	devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev);
32378c2ecf20Sopenharmony_ci
32388c2ecf20Sopenharmony_ci	cdns3_free_all_eps(priv_dev);
32398c2ecf20Sopenharmony_ci
32408c2ecf20Sopenharmony_ci	while (!list_empty(&priv_dev->aligned_buf_list)) {
32418c2ecf20Sopenharmony_ci		struct cdns3_aligned_buf *buf;
32428c2ecf20Sopenharmony_ci
32438c2ecf20Sopenharmony_ci		buf = cdns3_next_align_buf(&priv_dev->aligned_buf_list);
32448c2ecf20Sopenharmony_ci		dma_free_coherent(priv_dev->sysdev, buf->size,
32458c2ecf20Sopenharmony_ci				  buf->buf,
32468c2ecf20Sopenharmony_ci				  buf->dma);
32478c2ecf20Sopenharmony_ci
32488c2ecf20Sopenharmony_ci		list_del(&buf->list);
32498c2ecf20Sopenharmony_ci		kfree(buf);
32508c2ecf20Sopenharmony_ci	}
32518c2ecf20Sopenharmony_ci
32528c2ecf20Sopenharmony_ci	dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
32538c2ecf20Sopenharmony_ci			  priv_dev->setup_dma);
32548c2ecf20Sopenharmony_ci
32558c2ecf20Sopenharmony_ci	kfree(priv_dev->zlp_buf);
32568c2ecf20Sopenharmony_ci	usb_put_gadget(&priv_dev->gadget);
32578c2ecf20Sopenharmony_ci	cdns->gadget_dev = NULL;
32588c2ecf20Sopenharmony_ci	cdns3_drd_gadget_off(cdns);
32598c2ecf20Sopenharmony_ci}
32608c2ecf20Sopenharmony_ci
32618c2ecf20Sopenharmony_cistatic int cdns3_gadget_start(struct cdns3 *cdns)
32628c2ecf20Sopenharmony_ci{
32638c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev;
32648c2ecf20Sopenharmony_ci	u32 max_speed;
32658c2ecf20Sopenharmony_ci	int ret;
32668c2ecf20Sopenharmony_ci
32678c2ecf20Sopenharmony_ci	priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
32688c2ecf20Sopenharmony_ci	if (!priv_dev)
32698c2ecf20Sopenharmony_ci		return -ENOMEM;
32708c2ecf20Sopenharmony_ci
32718c2ecf20Sopenharmony_ci	usb_initialize_gadget(cdns->dev, &priv_dev->gadget,
32728c2ecf20Sopenharmony_ci			cdns3_gadget_release);
32738c2ecf20Sopenharmony_ci	cdns->gadget_dev = priv_dev;
32748c2ecf20Sopenharmony_ci	priv_dev->sysdev = cdns->dev;
32758c2ecf20Sopenharmony_ci	priv_dev->dev = cdns->dev;
32768c2ecf20Sopenharmony_ci	priv_dev->regs = cdns->dev_regs;
32778c2ecf20Sopenharmony_ci
32788c2ecf20Sopenharmony_ci	device_property_read_u16(priv_dev->dev, "cdns,on-chip-buff-size",
32798c2ecf20Sopenharmony_ci				 &priv_dev->onchip_buffers);
32808c2ecf20Sopenharmony_ci
32818c2ecf20Sopenharmony_ci	if (priv_dev->onchip_buffers <=  0) {
32828c2ecf20Sopenharmony_ci		u32 reg = readl(&priv_dev->regs->usb_cap2);
32838c2ecf20Sopenharmony_ci
32848c2ecf20Sopenharmony_ci		priv_dev->onchip_buffers = USB_CAP2_ACTUAL_MEM_SIZE(reg);
32858c2ecf20Sopenharmony_ci	}
32868c2ecf20Sopenharmony_ci
32878c2ecf20Sopenharmony_ci	if (!priv_dev->onchip_buffers)
32888c2ecf20Sopenharmony_ci		priv_dev->onchip_buffers = 256;
32898c2ecf20Sopenharmony_ci
32908c2ecf20Sopenharmony_ci	max_speed = usb_get_maximum_speed(cdns->dev);
32918c2ecf20Sopenharmony_ci
32928c2ecf20Sopenharmony_ci	/* Check the maximum_speed parameter */
32938c2ecf20Sopenharmony_ci	switch (max_speed) {
32948c2ecf20Sopenharmony_ci	case USB_SPEED_FULL:
32958c2ecf20Sopenharmony_ci	case USB_SPEED_HIGH:
32968c2ecf20Sopenharmony_ci	case USB_SPEED_SUPER:
32978c2ecf20Sopenharmony_ci		break;
32988c2ecf20Sopenharmony_ci	default:
32998c2ecf20Sopenharmony_ci		dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
33008c2ecf20Sopenharmony_ci			max_speed);
33018c2ecf20Sopenharmony_ci		fallthrough;
33028c2ecf20Sopenharmony_ci	case USB_SPEED_UNKNOWN:
33038c2ecf20Sopenharmony_ci		/* default to superspeed */
33048c2ecf20Sopenharmony_ci		max_speed = USB_SPEED_SUPER;
33058c2ecf20Sopenharmony_ci		break;
33068c2ecf20Sopenharmony_ci	}
33078c2ecf20Sopenharmony_ci
33088c2ecf20Sopenharmony_ci	/* fill gadget fields */
33098c2ecf20Sopenharmony_ci	priv_dev->gadget.max_speed = max_speed;
33108c2ecf20Sopenharmony_ci	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
33118c2ecf20Sopenharmony_ci	priv_dev->gadget.ops = &cdns3_gadget_ops;
33128c2ecf20Sopenharmony_ci	priv_dev->gadget.name = "usb-ss-gadget";
33138c2ecf20Sopenharmony_ci	priv_dev->gadget.quirk_avoids_skb_reserve = 1;
33148c2ecf20Sopenharmony_ci	priv_dev->gadget.irq = cdns->dev_irq;
33158c2ecf20Sopenharmony_ci
33168c2ecf20Sopenharmony_ci	spin_lock_init(&priv_dev->lock);
33178c2ecf20Sopenharmony_ci	INIT_WORK(&priv_dev->pending_status_wq,
33188c2ecf20Sopenharmony_ci		  cdns3_pending_setup_status_handler);
33198c2ecf20Sopenharmony_ci
33208c2ecf20Sopenharmony_ci	INIT_WORK(&priv_dev->aligned_buf_wq,
33218c2ecf20Sopenharmony_ci		  cdns3_free_aligned_request_buf);
33228c2ecf20Sopenharmony_ci
33238c2ecf20Sopenharmony_ci	/* initialize endpoint container */
33248c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
33258c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&priv_dev->aligned_buf_list);
33268c2ecf20Sopenharmony_ci
33278c2ecf20Sopenharmony_ci	ret = cdns3_init_eps(priv_dev);
33288c2ecf20Sopenharmony_ci	if (ret) {
33298c2ecf20Sopenharmony_ci		dev_err(priv_dev->dev, "Failed to create endpoints\n");
33308c2ecf20Sopenharmony_ci		goto err1;
33318c2ecf20Sopenharmony_ci	}
33328c2ecf20Sopenharmony_ci
33338c2ecf20Sopenharmony_ci	/* allocate memory for setup packet buffer */
33348c2ecf20Sopenharmony_ci	priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
33358c2ecf20Sopenharmony_ci						 &priv_dev->setup_dma, GFP_DMA);
33368c2ecf20Sopenharmony_ci	if (!priv_dev->setup_buf) {
33378c2ecf20Sopenharmony_ci		ret = -ENOMEM;
33388c2ecf20Sopenharmony_ci		goto err2;
33398c2ecf20Sopenharmony_ci	}
33408c2ecf20Sopenharmony_ci
33418c2ecf20Sopenharmony_ci	priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
33428c2ecf20Sopenharmony_ci
33438c2ecf20Sopenharmony_ci	dev_dbg(priv_dev->dev, "Device Controller version: %08x\n",
33448c2ecf20Sopenharmony_ci		readl(&priv_dev->regs->usb_cap6));
33458c2ecf20Sopenharmony_ci	dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n",
33468c2ecf20Sopenharmony_ci		readl(&priv_dev->regs->usb_cap1));
33478c2ecf20Sopenharmony_ci	dev_dbg(priv_dev->dev, "On-Chip memory configuration: %08x\n",
33488c2ecf20Sopenharmony_ci		readl(&priv_dev->regs->usb_cap2));
33498c2ecf20Sopenharmony_ci
33508c2ecf20Sopenharmony_ci	priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver);
33518c2ecf20Sopenharmony_ci	if (priv_dev->dev_ver >= DEV_VER_V2)
33528c2ecf20Sopenharmony_ci		priv_dev->gadget.sg_supported = 1;
33538c2ecf20Sopenharmony_ci
33548c2ecf20Sopenharmony_ci	priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
33558c2ecf20Sopenharmony_ci	if (!priv_dev->zlp_buf) {
33568c2ecf20Sopenharmony_ci		ret = -ENOMEM;
33578c2ecf20Sopenharmony_ci		goto err3;
33588c2ecf20Sopenharmony_ci	}
33598c2ecf20Sopenharmony_ci
33608c2ecf20Sopenharmony_ci	/* add USB gadget device */
33618c2ecf20Sopenharmony_ci	ret = usb_add_gadget(&priv_dev->gadget);
33628c2ecf20Sopenharmony_ci	if (ret < 0) {
33638c2ecf20Sopenharmony_ci		dev_err(priv_dev->dev, "Failed to add gadget\n");
33648c2ecf20Sopenharmony_ci		goto err4;
33658c2ecf20Sopenharmony_ci	}
33668c2ecf20Sopenharmony_ci
33678c2ecf20Sopenharmony_ci	return 0;
33688c2ecf20Sopenharmony_cierr4:
33698c2ecf20Sopenharmony_ci	kfree(priv_dev->zlp_buf);
33708c2ecf20Sopenharmony_cierr3:
33718c2ecf20Sopenharmony_ci	dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
33728c2ecf20Sopenharmony_ci			  priv_dev->setup_dma);
33738c2ecf20Sopenharmony_cierr2:
33748c2ecf20Sopenharmony_ci	cdns3_free_all_eps(priv_dev);
33758c2ecf20Sopenharmony_cierr1:
33768c2ecf20Sopenharmony_ci	usb_put_gadget(&priv_dev->gadget);
33778c2ecf20Sopenharmony_ci	cdns->gadget_dev = NULL;
33788c2ecf20Sopenharmony_ci	return ret;
33798c2ecf20Sopenharmony_ci}
33808c2ecf20Sopenharmony_ci
33818c2ecf20Sopenharmony_cistatic int __cdns3_gadget_init(struct cdns3 *cdns)
33828c2ecf20Sopenharmony_ci{
33838c2ecf20Sopenharmony_ci	int ret = 0;
33848c2ecf20Sopenharmony_ci
33858c2ecf20Sopenharmony_ci	/* Ensure 32-bit DMA Mask in case we switched back from Host mode */
33868c2ecf20Sopenharmony_ci	ret = dma_set_mask_and_coherent(cdns->dev, DMA_BIT_MASK(32));
33878c2ecf20Sopenharmony_ci	if (ret) {
33888c2ecf20Sopenharmony_ci		dev_err(cdns->dev, "Failed to set dma mask: %d\n", ret);
33898c2ecf20Sopenharmony_ci		return ret;
33908c2ecf20Sopenharmony_ci	}
33918c2ecf20Sopenharmony_ci
33928c2ecf20Sopenharmony_ci	cdns3_drd_gadget_on(cdns);
33938c2ecf20Sopenharmony_ci	pm_runtime_get_sync(cdns->dev);
33948c2ecf20Sopenharmony_ci
33958c2ecf20Sopenharmony_ci	ret = cdns3_gadget_start(cdns);
33968c2ecf20Sopenharmony_ci	if (ret) {
33978c2ecf20Sopenharmony_ci		pm_runtime_put_sync(cdns->dev);
33988c2ecf20Sopenharmony_ci		return ret;
33998c2ecf20Sopenharmony_ci	}
34008c2ecf20Sopenharmony_ci
34018c2ecf20Sopenharmony_ci	/*
34028c2ecf20Sopenharmony_ci	 * Because interrupt line can be shared with other components in
34038c2ecf20Sopenharmony_ci	 * driver it can't use IRQF_ONESHOT flag here.
34048c2ecf20Sopenharmony_ci	 */
34058c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(cdns->dev, cdns->dev_irq,
34068c2ecf20Sopenharmony_ci					cdns3_device_irq_handler,
34078c2ecf20Sopenharmony_ci					cdns3_device_thread_irq_handler,
34088c2ecf20Sopenharmony_ci					IRQF_SHARED, dev_name(cdns->dev),
34098c2ecf20Sopenharmony_ci					cdns->gadget_dev);
34108c2ecf20Sopenharmony_ci
34118c2ecf20Sopenharmony_ci	if (ret)
34128c2ecf20Sopenharmony_ci		goto err0;
34138c2ecf20Sopenharmony_ci
34148c2ecf20Sopenharmony_ci	return 0;
34158c2ecf20Sopenharmony_cierr0:
34168c2ecf20Sopenharmony_ci	cdns3_gadget_exit(cdns);
34178c2ecf20Sopenharmony_ci	return ret;
34188c2ecf20Sopenharmony_ci}
34198c2ecf20Sopenharmony_ci
34208c2ecf20Sopenharmony_cistatic int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
34218c2ecf20Sopenharmony_ci__must_hold(&cdns->lock)
34228c2ecf20Sopenharmony_ci{
34238c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = cdns->gadget_dev;
34248c2ecf20Sopenharmony_ci
34258c2ecf20Sopenharmony_ci	spin_unlock(&cdns->lock);
34268c2ecf20Sopenharmony_ci	cdns3_disconnect_gadget(priv_dev);
34278c2ecf20Sopenharmony_ci	spin_lock(&cdns->lock);
34288c2ecf20Sopenharmony_ci
34298c2ecf20Sopenharmony_ci	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
34308c2ecf20Sopenharmony_ci	usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
34318c2ecf20Sopenharmony_ci	cdns3_hw_reset_eps_config(priv_dev);
34328c2ecf20Sopenharmony_ci
34338c2ecf20Sopenharmony_ci	/* disable interrupt for device */
34348c2ecf20Sopenharmony_ci	writel(0, &priv_dev->regs->usb_ien);
34358c2ecf20Sopenharmony_ci
34368c2ecf20Sopenharmony_ci	return 0;
34378c2ecf20Sopenharmony_ci}
34388c2ecf20Sopenharmony_ci
34398c2ecf20Sopenharmony_cistatic int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
34408c2ecf20Sopenharmony_ci{
34418c2ecf20Sopenharmony_ci	struct cdns3_device *priv_dev = cdns->gadget_dev;
34428c2ecf20Sopenharmony_ci
34438c2ecf20Sopenharmony_ci	if (!priv_dev->gadget_driver)
34448c2ecf20Sopenharmony_ci		return 0;
34458c2ecf20Sopenharmony_ci
34468c2ecf20Sopenharmony_ci	cdns3_gadget_config(priv_dev);
34478c2ecf20Sopenharmony_ci
34488c2ecf20Sopenharmony_ci	return 0;
34498c2ecf20Sopenharmony_ci}
34508c2ecf20Sopenharmony_ci
34518c2ecf20Sopenharmony_ci/**
34528c2ecf20Sopenharmony_ci * cdns3_gadget_init - initialize device structure
34538c2ecf20Sopenharmony_ci *
34548c2ecf20Sopenharmony_ci * @cdns: cdns3 instance
34558c2ecf20Sopenharmony_ci *
34568c2ecf20Sopenharmony_ci * This function initializes the gadget.
34578c2ecf20Sopenharmony_ci */
34588c2ecf20Sopenharmony_ciint cdns3_gadget_init(struct cdns3 *cdns)
34598c2ecf20Sopenharmony_ci{
34608c2ecf20Sopenharmony_ci	struct cdns3_role_driver *rdrv;
34618c2ecf20Sopenharmony_ci
34628c2ecf20Sopenharmony_ci	rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
34638c2ecf20Sopenharmony_ci	if (!rdrv)
34648c2ecf20Sopenharmony_ci		return -ENOMEM;
34658c2ecf20Sopenharmony_ci
34668c2ecf20Sopenharmony_ci	rdrv->start	= __cdns3_gadget_init;
34678c2ecf20Sopenharmony_ci	rdrv->stop	= cdns3_gadget_exit;
34688c2ecf20Sopenharmony_ci	rdrv->suspend	= cdns3_gadget_suspend;
34698c2ecf20Sopenharmony_ci	rdrv->resume	= cdns3_gadget_resume;
34708c2ecf20Sopenharmony_ci	rdrv->state	= CDNS3_ROLE_STATE_INACTIVE;
34718c2ecf20Sopenharmony_ci	rdrv->name	= "gadget";
34728c2ecf20Sopenharmony_ci	cdns->roles[USB_ROLE_DEVICE] = rdrv;
34738c2ecf20Sopenharmony_ci
34748c2ecf20Sopenharmony_ci	return 0;
34758c2ecf20Sopenharmony_ci}
3476