162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Cadence USBHS-DEV Driver - gadget side.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2023 Cadence Design Systems.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Authors: Pawel Laszczak <pawell@cadence.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/*
1162306a36Sopenharmony_ci * Work around 1:
1262306a36Sopenharmony_ci * At some situations, the controller may get stale data address in TRB
1362306a36Sopenharmony_ci * at below sequences:
1462306a36Sopenharmony_ci * 1. Controller read TRB includes data address
1562306a36Sopenharmony_ci * 2. Software updates TRBs includes data address and Cycle bit
1662306a36Sopenharmony_ci * 3. Controller read TRB which includes Cycle bit
1762306a36Sopenharmony_ci * 4. DMA run with stale data address
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * To fix this problem, driver needs to make the first TRB in TD as invalid.
2062306a36Sopenharmony_ci * After preparing all TRBs driver needs to check the position of DMA and
2162306a36Sopenharmony_ci * if the DMA point to the first just added TRB and doorbell is 1,
2262306a36Sopenharmony_ci * then driver must defer making this TRB as valid. This TRB will be make
2362306a36Sopenharmony_ci * as valid during adding next TRB only if DMA is stopped or at TRBERR
2462306a36Sopenharmony_ci * interrupt.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2962306a36Sopenharmony_ci#include <linux/pm_runtime.h>
3062306a36Sopenharmony_ci#include <linux/interrupt.h>
3162306a36Sopenharmony_ci#include <linux/property.h>
3262306a36Sopenharmony_ci#include <linux/dmapool.h>
3362306a36Sopenharmony_ci#include <linux/iopoll.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "cdns2-gadget.h"
3662306a36Sopenharmony_ci#include "cdns2-trace.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/**
3962306a36Sopenharmony_ci * set_reg_bit_32 - set bit in given 32 bits register.
4062306a36Sopenharmony_ci * @ptr: register address.
4162306a36Sopenharmony_ci * @mask: bits to set.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_cistatic void set_reg_bit_32(void __iomem *ptr, u32 mask)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	mask = readl(ptr) | mask;
4662306a36Sopenharmony_ci	writel(mask, ptr);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/*
5062306a36Sopenharmony_ci * clear_reg_bit_32 - clear bit in given 32 bits register.
5162306a36Sopenharmony_ci * @ptr: register address.
5262306a36Sopenharmony_ci * @mask: bits to clear.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic void clear_reg_bit_32(void __iomem *ptr, u32 mask)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	mask = readl(ptr) & ~mask;
5762306a36Sopenharmony_ci	writel(mask, ptr);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* Clear bit in given 8 bits register. */
6162306a36Sopenharmony_cistatic void clear_reg_bit_8(void __iomem *ptr, u8 mask)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	mask = readb(ptr) & ~mask;
6462306a36Sopenharmony_ci	writeb(mask, ptr);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* Set bit in given 16 bits register. */
6862306a36Sopenharmony_civoid set_reg_bit_8(void __iomem *ptr, u8 mask)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	mask = readb(ptr) | mask;
7162306a36Sopenharmony_ci	writeb(mask, ptr);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int cdns2_get_dma_pos(struct cdns2_device *pdev,
7562306a36Sopenharmony_ci			     struct cdns2_endpoint *pep)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	int dma_index;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	dma_index = readl(&pdev->adma_regs->ep_traddr) - pep->ring.dma;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return dma_index / TRB_SIZE;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* Get next private request from list. */
8562306a36Sopenharmony_cistruct cdns2_request *cdns2_next_preq(struct list_head *list)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	return list_first_entry_or_null(list, struct cdns2_request, list);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_civoid cdns2_select_ep(struct cdns2_device *pdev, u32 ep)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	if (pdev->selected_ep == ep)
9362306a36Sopenharmony_ci		return;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	pdev->selected_ep = ep;
9662306a36Sopenharmony_ci	writel(ep, &pdev->adma_regs->ep_sel);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cidma_addr_t cdns2_trb_virt_to_dma(struct cdns2_endpoint *pep,
10062306a36Sopenharmony_ci				 struct cdns2_trb *trb)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	u32 offset = (char *)trb - (char *)pep->ring.trbs;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return pep->ring.dma + offset;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic void cdns2_free_tr_segment(struct cdns2_endpoint *pep)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
11062306a36Sopenharmony_ci	struct cdns2_ring *ring = &pep->ring;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (pep->ring.trbs) {
11362306a36Sopenharmony_ci		dma_pool_free(pdev->eps_dma_pool, ring->trbs, ring->dma);
11462306a36Sopenharmony_ci		memset(ring, 0, sizeof(*ring));
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/* Allocates Transfer Ring segment. */
11962306a36Sopenharmony_cistatic int cdns2_alloc_tr_segment(struct cdns2_endpoint *pep)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
12262306a36Sopenharmony_ci	struct cdns2_trb *link_trb;
12362306a36Sopenharmony_ci	struct cdns2_ring *ring;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	ring = &pep->ring;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (!ring->trbs) {
12862306a36Sopenharmony_ci		ring->trbs = dma_pool_alloc(pdev->eps_dma_pool,
12962306a36Sopenharmony_ci					    GFP_DMA32 | GFP_ATOMIC,
13062306a36Sopenharmony_ci					    &ring->dma);
13162306a36Sopenharmony_ci		if (!ring->trbs)
13262306a36Sopenharmony_ci			return -ENOMEM;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	memset(ring->trbs, 0, TR_SEG_SIZE);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (!pep->num)
13862306a36Sopenharmony_ci		return 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* Initialize the last TRB as Link TRB */
14162306a36Sopenharmony_ci	link_trb = (ring->trbs + (TRBS_PER_SEGMENT - 1));
14262306a36Sopenharmony_ci	link_trb->buffer = cpu_to_le32(TRB_BUFFER(ring->dma));
14362306a36Sopenharmony_ci	link_trb->control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_LINK) |
14462306a36Sopenharmony_ci					TRB_TOGGLE);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/*
15062306a36Sopenharmony_ci * Stalls and flushes selected endpoint.
15162306a36Sopenharmony_ci * Endpoint must be selected before invoking this function.
15262306a36Sopenharmony_ci */
15362306a36Sopenharmony_cistatic void cdns2_ep_stall_flush(struct cdns2_endpoint *pep)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
15662306a36Sopenharmony_ci	int val;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	trace_cdns2_ep_halt(pep, 1, 1);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	writel(DMA_EP_CMD_DFLUSH, &pdev->adma_regs->ep_cmd);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* Wait for DFLUSH cleared. */
16362306a36Sopenharmony_ci	readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
16462306a36Sopenharmony_ci				  !(val & DMA_EP_CMD_DFLUSH), 1, 1000);
16562306a36Sopenharmony_ci	pep->ep_state |= EP_STALLED;
16662306a36Sopenharmony_ci	pep->ep_state &= ~EP_STALL_PENDING;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/*
17062306a36Sopenharmony_ci * Increment a trb index.
17162306a36Sopenharmony_ci *
17262306a36Sopenharmony_ci * The index should never point to the last link TRB in TR. After incrementing,
17362306a36Sopenharmony_ci * if it point to the link TRB, wrap around to the beginning and revert
17462306a36Sopenharmony_ci * cycle state bit. The link TRB is always at the last TRB entry.
17562306a36Sopenharmony_ci */
17662306a36Sopenharmony_cistatic void cdns2_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	(*index)++;
17962306a36Sopenharmony_ci	if (*index == (trb_in_seg - 1)) {
18062306a36Sopenharmony_ci		*index = 0;
18162306a36Sopenharmony_ci		*cs ^=  1;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic void cdns2_ep_inc_enq(struct cdns2_ring *ring)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	ring->free_trbs--;
18862306a36Sopenharmony_ci	cdns2_ep_inc_trb(&ring->enqueue, &ring->pcs, TRBS_PER_SEGMENT);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void cdns2_ep_inc_deq(struct cdns2_ring *ring)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	ring->free_trbs++;
19462306a36Sopenharmony_ci	cdns2_ep_inc_trb(&ring->dequeue, &ring->ccs, TRBS_PER_SEGMENT);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/*
19862306a36Sopenharmony_ci * Enable/disable LPM.
19962306a36Sopenharmony_ci *
20062306a36Sopenharmony_ci * If bit USBCS_LPMNYET is not set and device receive Extended Token packet,
20162306a36Sopenharmony_ci * then controller answer with ACK handshake.
20262306a36Sopenharmony_ci * If bit USBCS_LPMNYET is set and device receive Extended Token packet,
20362306a36Sopenharmony_ci * then controller answer with NYET handshake.
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_cistatic void cdns2_enable_l1(struct cdns2_device *pdev, int enable)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	if (enable) {
20862306a36Sopenharmony_ci		clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_LPMNYET);
20962306a36Sopenharmony_ci		writeb(LPMCLOCK_SLEEP_ENTRY, &pdev->usb_regs->lpmclock);
21062306a36Sopenharmony_ci	} else {
21162306a36Sopenharmony_ci		set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_LPMNYET);
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic enum usb_device_speed cdns2_get_speed(struct cdns2_device *pdev)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	u8 speed = readb(&pdev->usb_regs->speedctrl);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (speed & SPEEDCTRL_HS)
22062306a36Sopenharmony_ci		return USB_SPEED_HIGH;
22162306a36Sopenharmony_ci	else if (speed & SPEEDCTRL_FS)
22262306a36Sopenharmony_ci		return USB_SPEED_FULL;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return USB_SPEED_UNKNOWN;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic struct cdns2_trb *cdns2_next_trb(struct cdns2_endpoint *pep,
22862306a36Sopenharmony_ci					struct cdns2_trb *trb)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	if (trb == (pep->ring.trbs + (TRBS_PER_SEGMENT - 1)))
23162306a36Sopenharmony_ci		return pep->ring.trbs;
23262306a36Sopenharmony_ci	else
23362306a36Sopenharmony_ci		return ++trb;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_civoid cdns2_gadget_giveback(struct cdns2_endpoint *pep,
23762306a36Sopenharmony_ci			   struct cdns2_request *preq,
23862306a36Sopenharmony_ci			   int status)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct usb_request *request = &preq->request;
24162306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	list_del_init(&preq->list);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (request->status == -EINPROGRESS)
24662306a36Sopenharmony_ci		request->status = status;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	usb_gadget_unmap_request_by_dev(pdev->dev, request, pep->dir);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* All TRBs have finished, clear the counter. */
25162306a36Sopenharmony_ci	preq->finished_trb = 0;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	trace_cdns2_request_giveback(preq);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (request->complete) {
25662306a36Sopenharmony_ci		spin_unlock(&pdev->lock);
25762306a36Sopenharmony_ci		usb_gadget_giveback_request(&pep->endpoint, request);
25862306a36Sopenharmony_ci		spin_lock(&pdev->lock);
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (request->buf == pdev->zlp_buf)
26262306a36Sopenharmony_ci		cdns2_gadget_ep_free_request(&pep->endpoint, request);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void cdns2_wa1_restore_cycle_bit(struct cdns2_endpoint *pep)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	/* Work around for stale data address in TRB. */
26862306a36Sopenharmony_ci	if (pep->wa1_set) {
26962306a36Sopenharmony_ci		trace_cdns2_wa1(pep, "restore cycle bit");
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		pep->wa1_set = 0;
27262306a36Sopenharmony_ci		pep->wa1_trb_index = 0xFFFF;
27362306a36Sopenharmony_ci		if (pep->wa1_cycle_bit)
27462306a36Sopenharmony_ci			pep->wa1_trb->control |= cpu_to_le32(0x1);
27562306a36Sopenharmony_ci		else
27662306a36Sopenharmony_ci			pep->wa1_trb->control &= cpu_to_le32(~0x1);
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic int cdns2_wa1_update_guard(struct cdns2_endpoint *pep,
28162306a36Sopenharmony_ci				  struct cdns2_trb *trb)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (!pep->wa1_set) {
28662306a36Sopenharmony_ci		u32 doorbell;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		if (doorbell) {
29162306a36Sopenharmony_ci			pep->wa1_cycle_bit = pep->ring.pcs ? TRB_CYCLE : 0;
29262306a36Sopenharmony_ci			pep->wa1_set = 1;
29362306a36Sopenharmony_ci			pep->wa1_trb = trb;
29462306a36Sopenharmony_ci			pep->wa1_trb_index = pep->ring.enqueue;
29562306a36Sopenharmony_ci			trace_cdns2_wa1(pep, "set guard");
29662306a36Sopenharmony_ci			return 0;
29762306a36Sopenharmony_ci		}
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci	return 1;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic void cdns2_wa1_tray_restore_cycle_bit(struct cdns2_device *pdev,
30362306a36Sopenharmony_ci					     struct cdns2_endpoint *pep)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	int dma_index;
30662306a36Sopenharmony_ci	u32 doorbell;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
30962306a36Sopenharmony_ci	dma_index = cdns2_get_dma_pos(pdev, pep);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (!doorbell || dma_index != pep->wa1_trb_index)
31262306a36Sopenharmony_ci		cdns2_wa1_restore_cycle_bit(pep);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int cdns2_prepare_ring(struct cdns2_device *pdev,
31662306a36Sopenharmony_ci			      struct cdns2_endpoint *pep,
31762306a36Sopenharmony_ci			      int num_trbs)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct cdns2_trb *link_trb = NULL;
32062306a36Sopenharmony_ci	int doorbell, dma_index;
32162306a36Sopenharmony_ci	struct cdns2_ring *ring;
32262306a36Sopenharmony_ci	u32 ch_bit = 0;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ring = &pep->ring;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (num_trbs > ring->free_trbs) {
32762306a36Sopenharmony_ci		pep->ep_state |= EP_RING_FULL;
32862306a36Sopenharmony_ci		trace_cdns2_no_room_on_ring("Ring full\n");
32962306a36Sopenharmony_ci		return -ENOBUFS;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if ((ring->enqueue + num_trbs)  >= (TRBS_PER_SEGMENT - 1)) {
33362306a36Sopenharmony_ci		doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
33462306a36Sopenharmony_ci		dma_index = cdns2_get_dma_pos(pdev, pep);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		/* Driver can't update LINK TRB if it is current processed. */
33762306a36Sopenharmony_ci		if (doorbell && dma_index == TRBS_PER_SEGMENT - 1) {
33862306a36Sopenharmony_ci			pep->ep_state |= EP_DEFERRED_DRDY;
33962306a36Sopenharmony_ci			return -ENOBUFS;
34062306a36Sopenharmony_ci		}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci		/* Update C bt in Link TRB before starting DMA. */
34362306a36Sopenharmony_ci		link_trb = ring->trbs + (TRBS_PER_SEGMENT - 1);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		/*
34662306a36Sopenharmony_ci		 * For TRs size equal 2 enabling TRB_CHAIN for epXin causes
34762306a36Sopenharmony_ci		 * that DMA stuck at the LINK TRB.
34862306a36Sopenharmony_ci		 * On the other hand, removing TRB_CHAIN for longer TRs for
34962306a36Sopenharmony_ci		 * epXout cause that DMA stuck after handling LINK TRB.
35062306a36Sopenharmony_ci		 * To eliminate this strange behavioral driver set TRB_CHAIN
35162306a36Sopenharmony_ci		 * bit only for TR size > 2.
35262306a36Sopenharmony_ci		 */
35362306a36Sopenharmony_ci		if (pep->type == USB_ENDPOINT_XFER_ISOC || TRBS_PER_SEGMENT > 2)
35462306a36Sopenharmony_ci			ch_bit = TRB_CHAIN;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		link_trb->control = cpu_to_le32(((ring->pcs) ? TRB_CYCLE : 0) |
35762306a36Sopenharmony_ci				    TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit);
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic void cdns2_dbg_request_trbs(struct cdns2_endpoint *pep,
36462306a36Sopenharmony_ci				   struct cdns2_request *preq)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct cdns2_trb *link_trb = pep->ring.trbs + (TRBS_PER_SEGMENT - 1);
36762306a36Sopenharmony_ci	struct cdns2_trb *trb = preq->trb;
36862306a36Sopenharmony_ci	int num_trbs = preq->num_of_trb;
36962306a36Sopenharmony_ci	int i = 0;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	while (i < num_trbs) {
37262306a36Sopenharmony_ci		trace_cdns2_queue_trb(pep, trb + i);
37362306a36Sopenharmony_ci		if (trb + i == link_trb) {
37462306a36Sopenharmony_ci			trb = pep->ring.trbs;
37562306a36Sopenharmony_ci			num_trbs = num_trbs - i;
37662306a36Sopenharmony_ci			i = 0;
37762306a36Sopenharmony_ci		} else {
37862306a36Sopenharmony_ci			i++;
37962306a36Sopenharmony_ci		}
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic unsigned int cdns2_count_trbs(struct cdns2_endpoint *pep,
38462306a36Sopenharmony_ci				     u64 addr, u64 len)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	unsigned int num_trbs = 1;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (pep->type == USB_ENDPOINT_XFER_ISOC) {
38962306a36Sopenharmony_ci		/*
39062306a36Sopenharmony_ci		 * To speed up DMA performance address should not exceed 4KB.
39162306a36Sopenharmony_ci		 * for high bandwidth transfer and driver will split
39262306a36Sopenharmony_ci		 * such buffer into two TRBs.
39362306a36Sopenharmony_ci		 */
39462306a36Sopenharmony_ci		num_trbs = DIV_ROUND_UP(len +
39562306a36Sopenharmony_ci					(addr & (TRB_MAX_ISO_BUFF_SIZE - 1)),
39662306a36Sopenharmony_ci					TRB_MAX_ISO_BUFF_SIZE);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		if (pep->interval > 1)
39962306a36Sopenharmony_ci			num_trbs = pep->dir ? num_trbs * pep->interval : 1;
40062306a36Sopenharmony_ci	} else if (pep->dir) {
40162306a36Sopenharmony_ci		/*
40262306a36Sopenharmony_ci		 * One extra link trb for IN direction.
40362306a36Sopenharmony_ci		 * Sometimes DMA doesn't want advance to next TD and transfer
40462306a36Sopenharmony_ci		 * hangs. This extra Link TRB force DMA to advance to next TD.
40562306a36Sopenharmony_ci		 */
40662306a36Sopenharmony_ci		num_trbs++;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return num_trbs;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic unsigned int cdns2_count_sg_trbs(struct cdns2_endpoint *pep,
41362306a36Sopenharmony_ci					struct usb_request *req)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	unsigned int i, len, full_len, num_trbs = 0;
41662306a36Sopenharmony_ci	struct scatterlist *sg;
41762306a36Sopenharmony_ci	int trb_len = 0;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	full_len = req->length;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	for_each_sg(req->sg, sg, req->num_sgs, i) {
42262306a36Sopenharmony_ci		len = sg_dma_len(sg);
42362306a36Sopenharmony_ci		num_trbs += cdns2_count_trbs(pep, sg_dma_address(sg), len);
42462306a36Sopenharmony_ci		len = min(len, full_len);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		/*
42762306a36Sopenharmony_ci		 * For HS ISO transfer TRBs should not exceed max packet size.
42862306a36Sopenharmony_ci		 * When DMA is working, and data exceed max packet size then
42962306a36Sopenharmony_ci		 * some data will be read in single mode instead burst mode.
43062306a36Sopenharmony_ci		 * This behavior will drastically reduce the copying speed.
43162306a36Sopenharmony_ci		 * To avoid this we need one or two extra TRBs.
43262306a36Sopenharmony_ci		 * This issue occurs for UVC class with sg_supported = 1
43362306a36Sopenharmony_ci		 * because buffers addresses are not aligned to 1024.
43462306a36Sopenharmony_ci		 */
43562306a36Sopenharmony_ci		if (pep->type == USB_ENDPOINT_XFER_ISOC) {
43662306a36Sopenharmony_ci			u8 temp;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci			trb_len += len;
43962306a36Sopenharmony_ci			temp = trb_len >> 10;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci			if (temp) {
44262306a36Sopenharmony_ci				if (trb_len % 1024)
44362306a36Sopenharmony_ci					num_trbs = num_trbs + temp;
44462306a36Sopenharmony_ci				else
44562306a36Sopenharmony_ci					num_trbs = num_trbs + temp - 1;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci				trb_len = trb_len - (temp << 10);
44862306a36Sopenharmony_ci			}
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		full_len -= len;
45262306a36Sopenharmony_ci		if (full_len == 0)
45362306a36Sopenharmony_ci			break;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return num_trbs;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci/*
46062306a36Sopenharmony_ci * Function prepares the array with optimized AXI burst value for different
46162306a36Sopenharmony_ci * transfer lengths. Controller handles the final data which are less
46262306a36Sopenharmony_ci * then AXI burst size as single byte transactions.
46362306a36Sopenharmony_ci * e.g.:
46462306a36Sopenharmony_ci * Let's assume that driver prepares trb with trb->length 700 and burst size
46562306a36Sopenharmony_ci * will be set to 128. In this case the controller will handle a first 512 as
46662306a36Sopenharmony_ci * single AXI transaction but the next 188 bytes will be handled
46762306a36Sopenharmony_ci * as 47 separate AXI transaction.
46862306a36Sopenharmony_ci * The better solution is to use the burst size equal 16 and then we will
46962306a36Sopenharmony_ci * have only 25 AXI transaction (10 * 64 + 15 *4).
47062306a36Sopenharmony_ci */
47162306a36Sopenharmony_cistatic void cdsn2_isoc_burst_opt(struct cdns2_device *pdev)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	int axi_burst_option[]  =  {1, 2, 4, 8, 16, 32, 64, 128};
47462306a36Sopenharmony_ci	int best_burst;
47562306a36Sopenharmony_ci	int array_size;
47662306a36Sopenharmony_ci	int opt_burst;
47762306a36Sopenharmony_ci	int trb_size;
47862306a36Sopenharmony_ci	int i, j;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	array_size = ARRAY_SIZE(axi_burst_option);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	for (i = 0; i <= MAX_ISO_SIZE; i++) {
48362306a36Sopenharmony_ci		trb_size = i / 4;
48462306a36Sopenharmony_ci		best_burst = trb_size ? trb_size : 1;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci		for (j = 0; j < array_size; j++) {
48762306a36Sopenharmony_ci			opt_burst = trb_size / axi_burst_option[j];
48862306a36Sopenharmony_ci			opt_burst += trb_size % axi_burst_option[j];
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci			if (opt_burst < best_burst) {
49162306a36Sopenharmony_ci				best_burst = opt_burst;
49262306a36Sopenharmony_ci				pdev->burst_opt[i] = axi_burst_option[j];
49362306a36Sopenharmony_ci			}
49462306a36Sopenharmony_ci		}
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic void cdns2_ep_tx_isoc(struct cdns2_endpoint *pep,
49962306a36Sopenharmony_ci			     struct cdns2_request *preq,
50062306a36Sopenharmony_ci			     int num_trbs)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct scatterlist *sg = NULL;
50362306a36Sopenharmony_ci	u32 remaining_packet_size = 0;
50462306a36Sopenharmony_ci	struct cdns2_trb *trb;
50562306a36Sopenharmony_ci	bool first_trb = true;
50662306a36Sopenharmony_ci	dma_addr_t trb_dma;
50762306a36Sopenharmony_ci	u32 trb_buff_len;
50862306a36Sopenharmony_ci	u32 block_length;
50962306a36Sopenharmony_ci	int td_idx = 0;
51062306a36Sopenharmony_ci	int split_size;
51162306a36Sopenharmony_ci	u32 full_len;
51262306a36Sopenharmony_ci	int enqd_len;
51362306a36Sopenharmony_ci	int sent_len;
51462306a36Sopenharmony_ci	int sg_iter;
51562306a36Sopenharmony_ci	u32 control;
51662306a36Sopenharmony_ci	int num_tds;
51762306a36Sopenharmony_ci	u32 length;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	/*
52062306a36Sopenharmony_ci	 * For OUT direction 1 TD per interval is enough
52162306a36Sopenharmony_ci	 * because TRBs are not dumped by controller.
52262306a36Sopenharmony_ci	 */
52362306a36Sopenharmony_ci	num_tds = pep->dir ? pep->interval : 1;
52462306a36Sopenharmony_ci	split_size = preq->request.num_sgs ? 1024 : 3072;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	for (td_idx = 0; td_idx < num_tds; td_idx++) {
52762306a36Sopenharmony_ci		if (preq->request.num_sgs) {
52862306a36Sopenharmony_ci			sg = preq->request.sg;
52962306a36Sopenharmony_ci			trb_dma = sg_dma_address(sg);
53062306a36Sopenharmony_ci			block_length = sg_dma_len(sg);
53162306a36Sopenharmony_ci		} else {
53262306a36Sopenharmony_ci			trb_dma = preq->request.dma;
53362306a36Sopenharmony_ci			block_length = preq->request.length;
53462306a36Sopenharmony_ci		}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		full_len = preq->request.length;
53762306a36Sopenharmony_ci		sg_iter = preq->request.num_sgs ? preq->request.num_sgs : 1;
53862306a36Sopenharmony_ci		remaining_packet_size = split_size;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		for (enqd_len = 0;  enqd_len < full_len;
54162306a36Sopenharmony_ci		     enqd_len += trb_buff_len) {
54262306a36Sopenharmony_ci			if (remaining_packet_size == 0)
54362306a36Sopenharmony_ci				remaining_packet_size = split_size;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci			/*
54662306a36Sopenharmony_ci			 * Calculate TRB length.- buffer can't across 4KB
54762306a36Sopenharmony_ci			 * and max packet size.
54862306a36Sopenharmony_ci			 */
54962306a36Sopenharmony_ci			trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(trb_dma);
55062306a36Sopenharmony_ci			trb_buff_len = min(trb_buff_len, remaining_packet_size);
55162306a36Sopenharmony_ci			trb_buff_len = min(trb_buff_len, block_length);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci			if (trb_buff_len > full_len - enqd_len)
55462306a36Sopenharmony_ci				trb_buff_len = full_len - enqd_len;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci			control = TRB_TYPE(TRB_NORMAL);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci			/*
55962306a36Sopenharmony_ci			 * For IN direction driver has to set the IOC for
56062306a36Sopenharmony_ci			 * last TRB in last TD.
56162306a36Sopenharmony_ci			 * For OUT direction driver must set IOC and ISP
56262306a36Sopenharmony_ci			 * only for last TRB in each TDs.
56362306a36Sopenharmony_ci			 */
56462306a36Sopenharmony_ci			if (enqd_len + trb_buff_len >= full_len || !pep->dir)
56562306a36Sopenharmony_ci				control |= TRB_IOC | TRB_ISP;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci			/*
56862306a36Sopenharmony_ci			 * Don't give the first TRB to the hardware (by toggling
56962306a36Sopenharmony_ci			 * the cycle bit) until we've finished creating all the
57062306a36Sopenharmony_ci			 * other TRBs.
57162306a36Sopenharmony_ci			 */
57262306a36Sopenharmony_ci			if (first_trb) {
57362306a36Sopenharmony_ci				first_trb = false;
57462306a36Sopenharmony_ci				if (pep->ring.pcs == 0)
57562306a36Sopenharmony_ci					control |= TRB_CYCLE;
57662306a36Sopenharmony_ci			} else {
57762306a36Sopenharmony_ci				control |= pep->ring.pcs;
57862306a36Sopenharmony_ci			}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci			if (enqd_len + trb_buff_len < full_len)
58162306a36Sopenharmony_ci				control |= TRB_CHAIN;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci			length = TRB_LEN(trb_buff_len) |
58462306a36Sopenharmony_ci				 TRB_BURST(pep->pdev->burst_opt[trb_buff_len]);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci			trb = pep->ring.trbs + pep->ring.enqueue;
58762306a36Sopenharmony_ci			trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma));
58862306a36Sopenharmony_ci			trb->length = cpu_to_le32(length);
58962306a36Sopenharmony_ci			trb->control = cpu_to_le32(control);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci			trb_dma += trb_buff_len;
59262306a36Sopenharmony_ci			sent_len = trb_buff_len;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci			if (sg && sent_len >= block_length) {
59562306a36Sopenharmony_ci				/* New sg entry */
59662306a36Sopenharmony_ci				--sg_iter;
59762306a36Sopenharmony_ci				sent_len -= block_length;
59862306a36Sopenharmony_ci				if (sg_iter != 0) {
59962306a36Sopenharmony_ci					sg = sg_next(sg);
60062306a36Sopenharmony_ci					trb_dma = sg_dma_address(sg);
60162306a36Sopenharmony_ci					block_length = sg_dma_len(sg);
60262306a36Sopenharmony_ci				}
60362306a36Sopenharmony_ci			}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci			remaining_packet_size -= trb_buff_len;
60662306a36Sopenharmony_ci			block_length -= sent_len;
60762306a36Sopenharmony_ci			preq->end_trb = pep->ring.enqueue;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci			cdns2_ep_inc_enq(&pep->ring);
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic void cdns2_ep_tx_bulk(struct cdns2_endpoint *pep,
61562306a36Sopenharmony_ci			     struct cdns2_request *preq,
61662306a36Sopenharmony_ci			     int trbs_per_td)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	struct scatterlist *sg = NULL;
61962306a36Sopenharmony_ci	struct cdns2_ring *ring;
62062306a36Sopenharmony_ci	struct cdns2_trb *trb;
62162306a36Sopenharmony_ci	dma_addr_t trb_dma;
62262306a36Sopenharmony_ci	int sg_iter = 0;
62362306a36Sopenharmony_ci	u32 control;
62462306a36Sopenharmony_ci	u32 length;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (preq->request.num_sgs) {
62762306a36Sopenharmony_ci		sg = preq->request.sg;
62862306a36Sopenharmony_ci		trb_dma = sg_dma_address(sg);
62962306a36Sopenharmony_ci		length = sg_dma_len(sg);
63062306a36Sopenharmony_ci	} else {
63162306a36Sopenharmony_ci		trb_dma = preq->request.dma;
63262306a36Sopenharmony_ci		length = preq->request.length;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	ring = &pep->ring;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	for (sg_iter = 0; sg_iter < trbs_per_td; sg_iter++) {
63862306a36Sopenharmony_ci		control = TRB_TYPE(TRB_NORMAL) | ring->pcs | TRB_ISP;
63962306a36Sopenharmony_ci		trb = pep->ring.trbs + ring->enqueue;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		if (pep->dir && sg_iter == trbs_per_td - 1) {
64262306a36Sopenharmony_ci			preq->end_trb = ring->enqueue;
64362306a36Sopenharmony_ci			control = ring->pcs | TRB_TYPE(TRB_LINK) | TRB_CHAIN
64462306a36Sopenharmony_ci				  | TRB_IOC;
64562306a36Sopenharmony_ci			cdns2_ep_inc_enq(&pep->ring);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci			if (ring->enqueue == 0)
64862306a36Sopenharmony_ci				control |= TRB_TOGGLE;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci			/* Point to next bad TRB. */
65162306a36Sopenharmony_ci			trb->buffer = cpu_to_le32(pep->ring.dma +
65262306a36Sopenharmony_ci						  (ring->enqueue * TRB_SIZE));
65362306a36Sopenharmony_ci			trb->length = 0;
65462306a36Sopenharmony_ci			trb->control = cpu_to_le32(control);
65562306a36Sopenharmony_ci			break;
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		/*
65962306a36Sopenharmony_ci		 * Don't give the first TRB to the hardware (by toggling
66062306a36Sopenharmony_ci		 * the cycle bit) until we've finished creating all the
66162306a36Sopenharmony_ci		 * other TRBs.
66262306a36Sopenharmony_ci		 */
66362306a36Sopenharmony_ci		if (sg_iter == 0)
66462306a36Sopenharmony_ci			control = control ^ TRB_CYCLE;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		/* For last TRB in TD. */
66762306a36Sopenharmony_ci		if (sg_iter == (trbs_per_td - (pep->dir ? 2 : 1)))
66862306a36Sopenharmony_ci			control |= TRB_IOC;
66962306a36Sopenharmony_ci		else
67062306a36Sopenharmony_ci			control |= TRB_CHAIN;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		trb->buffer = cpu_to_le32(trb_dma);
67362306a36Sopenharmony_ci		trb->length = cpu_to_le32(TRB_BURST(pep->trb_burst_size) |
67462306a36Sopenharmony_ci					   TRB_LEN(length));
67562306a36Sopenharmony_ci		trb->control = cpu_to_le32(control);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		if (sg && sg_iter < (trbs_per_td - 1)) {
67862306a36Sopenharmony_ci			sg = sg_next(sg);
67962306a36Sopenharmony_ci			trb_dma = sg_dma_address(sg);
68062306a36Sopenharmony_ci			length = sg_dma_len(sg);
68162306a36Sopenharmony_ci		}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		preq->end_trb = ring->enqueue;
68462306a36Sopenharmony_ci		cdns2_ep_inc_enq(&pep->ring);
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic void cdns2_set_drdy(struct cdns2_device *pdev,
68962306a36Sopenharmony_ci			   struct cdns2_endpoint *pep)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	trace_cdns2_ring(pep);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	/*
69462306a36Sopenharmony_ci	 * Memory barrier - Cycle Bit must be set before doorbell.
69562306a36Sopenharmony_ci	 */
69662306a36Sopenharmony_ci	dma_wmb();
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/* Clearing TRBERR and DESCMIS before setting DRDY. */
69962306a36Sopenharmony_ci	writel(DMA_EP_STS_TRBERR | DMA_EP_STS_DESCMIS,
70062306a36Sopenharmony_ci	       &pdev->adma_regs->ep_sts);
70162306a36Sopenharmony_ci	writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (readl(&pdev->adma_regs->ep_sts) & DMA_EP_STS_TRBERR) {
70462306a36Sopenharmony_ci		writel(DMA_EP_STS_TRBERR, &pdev->adma_regs->ep_sts);
70562306a36Sopenharmony_ci		writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd);
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	trace_cdns2_doorbell_epx(pep, readl(&pdev->adma_regs->ep_traddr));
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic int cdns2_prepare_first_isoc_transfer(struct cdns2_device *pdev,
71262306a36Sopenharmony_ci					     struct cdns2_endpoint *pep)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	struct cdns2_trb *trb;
71562306a36Sopenharmony_ci	u32 buffer;
71662306a36Sopenharmony_ci	u8 hw_ccs;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if ((readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY))
71962306a36Sopenharmony_ci		return -EBUSY;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	if (!pep->dir) {
72262306a36Sopenharmony_ci		set_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
72362306a36Sopenharmony_ci		writel(pep->ring.dma + pep->ring.dequeue,
72462306a36Sopenharmony_ci		       &pdev->adma_regs->ep_traddr);
72562306a36Sopenharmony_ci		return 0;
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	/*
72962306a36Sopenharmony_ci	 * The first packet after doorbell can be corrupted so,
73062306a36Sopenharmony_ci	 * driver prepares 0 length packet as first packet.
73162306a36Sopenharmony_ci	 */
73262306a36Sopenharmony_ci	buffer = pep->ring.dma + pep->ring.dequeue * TRB_SIZE;
73362306a36Sopenharmony_ci	hw_ccs = !!DMA_EP_STS_CCS(readl(&pdev->adma_regs->ep_sts));
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	trb = &pep->ring.trbs[TRBS_PER_SEGMENT];
73662306a36Sopenharmony_ci	trb->length = 0;
73762306a36Sopenharmony_ci	trb->buffer = cpu_to_le32(TRB_BUFFER(buffer));
73862306a36Sopenharmony_ci	trb->control = cpu_to_le32((hw_ccs ? TRB_CYCLE : 0) | TRB_TYPE(TRB_NORMAL));
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	/*
74162306a36Sopenharmony_ci	 * LINK TRB is used to force updating cycle bit in controller and
74262306a36Sopenharmony_ci	 * move to correct place in transfer ring.
74362306a36Sopenharmony_ci	 */
74462306a36Sopenharmony_ci	trb++;
74562306a36Sopenharmony_ci	trb->length = 0;
74662306a36Sopenharmony_ci	trb->buffer = cpu_to_le32(TRB_BUFFER(buffer));
74762306a36Sopenharmony_ci	trb->control = cpu_to_le32((hw_ccs ? TRB_CYCLE : 0) |
74862306a36Sopenharmony_ci				    TRB_TYPE(TRB_LINK) | TRB_CHAIN);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	if (hw_ccs !=  pep->ring.ccs)
75162306a36Sopenharmony_ci		trb->control |= cpu_to_le32(TRB_TOGGLE);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	set_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
75462306a36Sopenharmony_ci	writel(pep->ring.dma + (TRBS_PER_SEGMENT * TRB_SIZE),
75562306a36Sopenharmony_ci	       &pdev->adma_regs->ep_traddr);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	return 0;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci/* Prepare and start transfer on no-default endpoint. */
76162306a36Sopenharmony_cistatic int cdns2_ep_run_transfer(struct cdns2_endpoint *pep,
76262306a36Sopenharmony_ci				 struct cdns2_request *preq)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
76562306a36Sopenharmony_ci	struct cdns2_ring *ring;
76662306a36Sopenharmony_ci	u32 togle_pcs = 1;
76762306a36Sopenharmony_ci	int num_trbs;
76862306a36Sopenharmony_ci	int ret;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	cdns2_select_ep(pdev, pep->endpoint.address);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (preq->request.sg)
77362306a36Sopenharmony_ci		num_trbs = cdns2_count_sg_trbs(pep, &preq->request);
77462306a36Sopenharmony_ci	else
77562306a36Sopenharmony_ci		num_trbs = cdns2_count_trbs(pep, preq->request.dma,
77662306a36Sopenharmony_ci					    preq->request.length);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	ret = cdns2_prepare_ring(pdev, pep, num_trbs);
77962306a36Sopenharmony_ci	if (ret)
78062306a36Sopenharmony_ci		return ret;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	ring = &pep->ring;
78362306a36Sopenharmony_ci	preq->start_trb = ring->enqueue;
78462306a36Sopenharmony_ci	preq->trb = ring->trbs + ring->enqueue;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	if (usb_endpoint_xfer_isoc(pep->endpoint.desc)) {
78762306a36Sopenharmony_ci		cdns2_ep_tx_isoc(pep, preq, num_trbs);
78862306a36Sopenharmony_ci	} else {
78962306a36Sopenharmony_ci		togle_pcs = cdns2_wa1_update_guard(pep, ring->trbs + ring->enqueue);
79062306a36Sopenharmony_ci		cdns2_ep_tx_bulk(pep, preq, num_trbs);
79162306a36Sopenharmony_ci	}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	preq->num_of_trb = num_trbs;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	/*
79662306a36Sopenharmony_ci	 * Memory barrier - cycle bit must be set as the last operation.
79762306a36Sopenharmony_ci	 */
79862306a36Sopenharmony_ci	dma_wmb();
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	/* Give the TD to the consumer. */
80162306a36Sopenharmony_ci	if (togle_pcs)
80262306a36Sopenharmony_ci		preq->trb->control = preq->trb->control ^ cpu_to_le32(1);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	cdns2_wa1_tray_restore_cycle_bit(pdev, pep);
80562306a36Sopenharmony_ci	cdns2_dbg_request_trbs(pep, preq);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (!pep->wa1_set && !(pep->ep_state & EP_STALLED) && !pep->skip) {
80862306a36Sopenharmony_ci		if (pep->type == USB_ENDPOINT_XFER_ISOC) {
80962306a36Sopenharmony_ci			ret = cdns2_prepare_first_isoc_transfer(pdev, pep);
81062306a36Sopenharmony_ci			if (ret)
81162306a36Sopenharmony_ci				return 0;
81262306a36Sopenharmony_ci		}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci		cdns2_set_drdy(pdev, pep);
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	return 0;
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci/* Prepare and start transfer for all not started requests. */
82162306a36Sopenharmony_cistatic int cdns2_start_all_request(struct cdns2_device *pdev,
82262306a36Sopenharmony_ci				   struct cdns2_endpoint *pep)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	struct cdns2_request *preq;
82562306a36Sopenharmony_ci	int ret;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	while (!list_empty(&pep->deferred_list)) {
82862306a36Sopenharmony_ci		preq = cdns2_next_preq(&pep->deferred_list);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		ret = cdns2_ep_run_transfer(pep, preq);
83162306a36Sopenharmony_ci		if (ret)
83262306a36Sopenharmony_ci			return ret;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci		list_move_tail(&preq->list, &pep->pending_list);
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	pep->ep_state &= ~EP_RING_FULL;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return 0;
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci/*
84362306a36Sopenharmony_ci * Check whether trb has been handled by DMA.
84462306a36Sopenharmony_ci *
84562306a36Sopenharmony_ci * Endpoint must be selected before invoking this function.
84662306a36Sopenharmony_ci *
84762306a36Sopenharmony_ci * Returns false if request has not been handled by DMA, else returns true.
84862306a36Sopenharmony_ci *
84962306a36Sopenharmony_ci * SR - start ring
85062306a36Sopenharmony_ci * ER - end ring
85162306a36Sopenharmony_ci * DQ = ring->dequeue - dequeue position
85262306a36Sopenharmony_ci * EQ = ring->enqueue - enqueue position
85362306a36Sopenharmony_ci * ST = preq->start_trb - index of first TRB in transfer ring
85462306a36Sopenharmony_ci * ET = preq->end_trb - index of last TRB in transfer ring
85562306a36Sopenharmony_ci * CI = current_index - index of processed TRB by DMA.
85662306a36Sopenharmony_ci *
85762306a36Sopenharmony_ci * As first step, we check if the TRB between the ST and ET.
85862306a36Sopenharmony_ci * Then, we check if cycle bit for index pep->dequeue
85962306a36Sopenharmony_ci * is correct.
86062306a36Sopenharmony_ci *
86162306a36Sopenharmony_ci * some rules:
86262306a36Sopenharmony_ci * 1. ring->dequeue never equals to current_index.
86362306a36Sopenharmony_ci * 2  ring->enqueue never exceed ring->dequeue
86462306a36Sopenharmony_ci * 3. exception: ring->enqueue == ring->dequeue
86562306a36Sopenharmony_ci *    and ring->free_trbs is zero.
86662306a36Sopenharmony_ci *    This case indicate that TR is full.
86762306a36Sopenharmony_ci *
86862306a36Sopenharmony_ci * At below two cases, the request have been handled.
86962306a36Sopenharmony_ci * Case 1 - ring->dequeue < current_index
87062306a36Sopenharmony_ci *      SR ... EQ ... DQ ... CI ... ER
87162306a36Sopenharmony_ci *      SR ... DQ ... CI ... EQ ... ER
87262306a36Sopenharmony_ci *
87362306a36Sopenharmony_ci * Case 2 - ring->dequeue > current_index
87462306a36Sopenharmony_ci * This situation takes place when CI go through the LINK TRB at the end of
87562306a36Sopenharmony_ci * transfer ring.
87662306a36Sopenharmony_ci *      SR ... CI ... EQ ... DQ ... ER
87762306a36Sopenharmony_ci */
87862306a36Sopenharmony_cistatic bool cdns2_trb_handled(struct cdns2_endpoint *pep,
87962306a36Sopenharmony_ci			      struct cdns2_request *preq)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
88262306a36Sopenharmony_ci	struct cdns2_ring *ring;
88362306a36Sopenharmony_ci	struct cdns2_trb *trb;
88462306a36Sopenharmony_ci	int current_index = 0;
88562306a36Sopenharmony_ci	int handled = 0;
88662306a36Sopenharmony_ci	int doorbell;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	ring = &pep->ring;
88962306a36Sopenharmony_ci	current_index = cdns2_get_dma_pos(pdev, pep);
89062306a36Sopenharmony_ci	doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	/*
89362306a36Sopenharmony_ci	 * Only ISO transfer can use 2 entries outside the standard
89462306a36Sopenharmony_ci	 * Transfer Ring. First of them is used as zero length packet and the
89562306a36Sopenharmony_ci	 * second as LINK TRB.
89662306a36Sopenharmony_ci	 */
89762306a36Sopenharmony_ci	if (current_index >= TRBS_PER_SEGMENT)
89862306a36Sopenharmony_ci		goto finish;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	/* Current trb doesn't belong to this request. */
90162306a36Sopenharmony_ci	if (preq->start_trb < preq->end_trb) {
90262306a36Sopenharmony_ci		if (ring->dequeue > preq->end_trb)
90362306a36Sopenharmony_ci			goto finish;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		if (ring->dequeue < preq->start_trb)
90662306a36Sopenharmony_ci			goto finish;
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	if (preq->start_trb > preq->end_trb && ring->dequeue > preq->end_trb &&
91062306a36Sopenharmony_ci	    ring->dequeue < preq->start_trb)
91162306a36Sopenharmony_ci		goto finish;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (preq->start_trb == preq->end_trb && ring->dequeue != preq->end_trb)
91462306a36Sopenharmony_ci		goto finish;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	trb = &ring->trbs[ring->dequeue];
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if ((le32_to_cpu(trb->control) & TRB_CYCLE) != ring->ccs)
91962306a36Sopenharmony_ci		goto finish;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	if (doorbell == 1 && current_index == ring->dequeue)
92262306a36Sopenharmony_ci		goto finish;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	/* The corner case for TRBS_PER_SEGMENT equal 2). */
92562306a36Sopenharmony_ci	if (TRBS_PER_SEGMENT == 2 && pep->type != USB_ENDPOINT_XFER_ISOC) {
92662306a36Sopenharmony_ci		handled = 1;
92762306a36Sopenharmony_ci		goto finish;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (ring->enqueue == ring->dequeue &&
93162306a36Sopenharmony_ci	    ring->free_trbs == 0) {
93262306a36Sopenharmony_ci		handled = 1;
93362306a36Sopenharmony_ci	} else if (ring->dequeue < current_index) {
93462306a36Sopenharmony_ci		if ((current_index == (TRBS_PER_SEGMENT - 1)) &&
93562306a36Sopenharmony_ci		    !ring->dequeue)
93662306a36Sopenharmony_ci			goto finish;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci		handled = 1;
93962306a36Sopenharmony_ci	} else if (ring->dequeue  > current_index) {
94062306a36Sopenharmony_ci		handled = 1;
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cifinish:
94462306a36Sopenharmony_ci	trace_cdns2_request_handled(preq, current_index, handled);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	return handled;
94762306a36Sopenharmony_ci}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_cistatic void cdns2_skip_isoc_td(struct cdns2_device *pdev,
95062306a36Sopenharmony_ci			       struct cdns2_endpoint *pep,
95162306a36Sopenharmony_ci			       struct cdns2_request *preq)
95262306a36Sopenharmony_ci{
95362306a36Sopenharmony_ci	struct cdns2_trb *trb;
95462306a36Sopenharmony_ci	int i;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	trb = pep->ring.trbs + pep->ring.dequeue;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	for (i = preq->finished_trb ; i < preq->num_of_trb; i++) {
95962306a36Sopenharmony_ci		preq->finished_trb++;
96062306a36Sopenharmony_ci		trace_cdns2_complete_trb(pep, trb);
96162306a36Sopenharmony_ci		cdns2_ep_inc_deq(&pep->ring);
96262306a36Sopenharmony_ci		trb = cdns2_next_trb(pep, trb);
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	cdns2_gadget_giveback(pep, preq, 0);
96662306a36Sopenharmony_ci	cdns2_prepare_first_isoc_transfer(pdev, pep);
96762306a36Sopenharmony_ci	pep->skip = false;
96862306a36Sopenharmony_ci	cdns2_set_drdy(pdev, pep);
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cistatic void cdns2_transfer_completed(struct cdns2_device *pdev,
97262306a36Sopenharmony_ci				     struct cdns2_endpoint *pep)
97362306a36Sopenharmony_ci{
97462306a36Sopenharmony_ci	struct cdns2_request *preq = NULL;
97562306a36Sopenharmony_ci	bool request_handled = false;
97662306a36Sopenharmony_ci	struct cdns2_trb *trb;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	while (!list_empty(&pep->pending_list)) {
97962306a36Sopenharmony_ci		preq = cdns2_next_preq(&pep->pending_list);
98062306a36Sopenharmony_ci		trb = pep->ring.trbs + pep->ring.dequeue;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		/*
98362306a36Sopenharmony_ci		 * The TRB was changed as link TRB, and the request
98462306a36Sopenharmony_ci		 * was handled at ep_dequeue.
98562306a36Sopenharmony_ci		 */
98662306a36Sopenharmony_ci		while (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK &&
98762306a36Sopenharmony_ci		       le32_to_cpu(trb->length)) {
98862306a36Sopenharmony_ci			trace_cdns2_complete_trb(pep, trb);
98962306a36Sopenharmony_ci			cdns2_ep_inc_deq(&pep->ring);
99062306a36Sopenharmony_ci			trb = pep->ring.trbs + pep->ring.dequeue;
99162306a36Sopenharmony_ci		}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci		/*
99462306a36Sopenharmony_ci		 * Re-select endpoint. It could be changed by other CPU
99562306a36Sopenharmony_ci		 * during handling usb_gadget_giveback_request.
99662306a36Sopenharmony_ci		 */
99762306a36Sopenharmony_ci		cdns2_select_ep(pdev, pep->endpoint.address);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci		while (cdns2_trb_handled(pep, preq)) {
100062306a36Sopenharmony_ci			preq->finished_trb++;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci			if (preq->finished_trb >= preq->num_of_trb)
100362306a36Sopenharmony_ci				request_handled = true;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci			trb = pep->ring.trbs + pep->ring.dequeue;
100662306a36Sopenharmony_ci			trace_cdns2_complete_trb(pep, trb);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci			if (pep->dir && pep->type == USB_ENDPOINT_XFER_ISOC)
100962306a36Sopenharmony_ci				/*
101062306a36Sopenharmony_ci				 * For ISOC IN controller doens't update the
101162306a36Sopenharmony_ci				 * trb->length.
101262306a36Sopenharmony_ci				 */
101362306a36Sopenharmony_ci				preq->request.actual = preq->request.length;
101462306a36Sopenharmony_ci			else
101562306a36Sopenharmony_ci				preq->request.actual +=
101662306a36Sopenharmony_ci					TRB_LEN(le32_to_cpu(trb->length));
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci			cdns2_ep_inc_deq(&pep->ring);
101962306a36Sopenharmony_ci		}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci		if (request_handled) {
102262306a36Sopenharmony_ci			cdns2_gadget_giveback(pep, preq, 0);
102362306a36Sopenharmony_ci			request_handled = false;
102462306a36Sopenharmony_ci		} else {
102562306a36Sopenharmony_ci			goto prepare_next_td;
102662306a36Sopenharmony_ci		}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		if (pep->type != USB_ENDPOINT_XFER_ISOC &&
102962306a36Sopenharmony_ci		    TRBS_PER_SEGMENT == 2)
103062306a36Sopenharmony_ci			break;
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ciprepare_next_td:
103462306a36Sopenharmony_ci	if (pep->skip && preq)
103562306a36Sopenharmony_ci		cdns2_skip_isoc_td(pdev, pep, preq);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	if (!(pep->ep_state & EP_STALLED) &&
103862306a36Sopenharmony_ci	    !(pep->ep_state & EP_STALL_PENDING))
103962306a36Sopenharmony_ci		cdns2_start_all_request(pdev, pep);
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_cistatic void cdns2_wakeup(struct cdns2_device *pdev)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci	if (!pdev->may_wakeup)
104562306a36Sopenharmony_ci		return;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	/* Start driving resume signaling to indicate remote wakeup. */
104862306a36Sopenharmony_ci	set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_SIGRSUME);
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic void cdns2_rearm_transfer(struct cdns2_endpoint *pep, u8 rearm)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	cdns2_wa1_restore_cycle_bit(pep);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (rearm) {
105862306a36Sopenharmony_ci		trace_cdns2_ring(pep);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci		/* Cycle Bit must be updated before arming DMA. */
106162306a36Sopenharmony_ci		dma_wmb();
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci		writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd);
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci		cdns2_wakeup(pdev);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci		trace_cdns2_doorbell_epx(pep,
106862306a36Sopenharmony_ci					 readl(&pdev->adma_regs->ep_traddr));
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_cistatic void cdns2_handle_epx_interrupt(struct cdns2_endpoint *pep)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
107562306a36Sopenharmony_ci	u8 isoerror = 0;
107662306a36Sopenharmony_ci	u32 ep_sts_reg;
107762306a36Sopenharmony_ci	u32 val;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	cdns2_select_ep(pdev, pep->endpoint.address);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	trace_cdns2_epx_irq(pdev, pep);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	ep_sts_reg = readl(&pdev->adma_regs->ep_sts);
108462306a36Sopenharmony_ci	writel(ep_sts_reg, &pdev->adma_regs->ep_sts);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	if (pep->type == USB_ENDPOINT_XFER_ISOC) {
108762306a36Sopenharmony_ci		u8 mult;
108862306a36Sopenharmony_ci		u8 cs;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		mult = USB_EP_MAXP_MULT(pep->endpoint.desc->wMaxPacketSize);
109162306a36Sopenharmony_ci		cs = pep->dir ? readb(&pdev->epx_regs->ep[pep->num - 1].txcs) :
109262306a36Sopenharmony_ci				readb(&pdev->epx_regs->ep[pep->num - 1].rxcs);
109362306a36Sopenharmony_ci		if (mult > 0)
109462306a36Sopenharmony_ci			isoerror = EPX_CS_ERR(cs);
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	/*
109862306a36Sopenharmony_ci	 * Sometimes ISO Error for mult=1 or mult=2 is not propagated on time
109962306a36Sopenharmony_ci	 * from USB module to DMA module. To protect against this driver
110062306a36Sopenharmony_ci	 * checks also the txcs/rxcs registers.
110162306a36Sopenharmony_ci	 */
110262306a36Sopenharmony_ci	if ((ep_sts_reg & DMA_EP_STS_ISOERR) || isoerror) {
110362306a36Sopenharmony_ci		clear_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		/* Wait for DBUSY cleared. */
110662306a36Sopenharmony_ci		readl_poll_timeout_atomic(&pdev->adma_regs->ep_sts, val,
110762306a36Sopenharmony_ci					  !(val & DMA_EP_STS_DBUSY), 1, 125);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci		writel(DMA_EP_CMD_DFLUSH, &pep->pdev->adma_regs->ep_cmd);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci		/* Wait for DFLUSH cleared. */
111262306a36Sopenharmony_ci		readl_poll_timeout_atomic(&pep->pdev->adma_regs->ep_cmd, val,
111362306a36Sopenharmony_ci					  !(val & DMA_EP_CMD_DFLUSH), 1, 10);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci		pep->skip = true;
111662306a36Sopenharmony_ci	}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	if (ep_sts_reg & DMA_EP_STS_TRBERR || pep->skip) {
111962306a36Sopenharmony_ci		if (pep->ep_state & EP_STALL_PENDING &&
112062306a36Sopenharmony_ci		    !(ep_sts_reg & DMA_EP_STS_DESCMIS))
112162306a36Sopenharmony_ci			cdns2_ep_stall_flush(pep);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci		/*
112462306a36Sopenharmony_ci		 * For isochronous transfer driver completes request on
112562306a36Sopenharmony_ci		 * IOC or on TRBERR. IOC appears only when device receive
112662306a36Sopenharmony_ci		 * OUT data packet. If host disable stream or lost some packet
112762306a36Sopenharmony_ci		 * then the only way to finish all queued transfer is to do it
112862306a36Sopenharmony_ci		 * on TRBERR event.
112962306a36Sopenharmony_ci		 */
113062306a36Sopenharmony_ci		if (pep->type == USB_ENDPOINT_XFER_ISOC && !pep->wa1_set) {
113162306a36Sopenharmony_ci			if (!pep->dir)
113262306a36Sopenharmony_ci				clear_reg_bit_32(&pdev->adma_regs->ep_cfg,
113362306a36Sopenharmony_ci						 DMA_EP_CFG_ENABLE);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci			cdns2_transfer_completed(pdev, pep);
113662306a36Sopenharmony_ci			if (pep->ep_state & EP_DEFERRED_DRDY) {
113762306a36Sopenharmony_ci				pep->ep_state &= ~EP_DEFERRED_DRDY;
113862306a36Sopenharmony_ci				cdns2_set_drdy(pdev, pep);
113962306a36Sopenharmony_ci			}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci			return;
114262306a36Sopenharmony_ci		}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci		cdns2_transfer_completed(pdev, pep);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		if (!(pep->ep_state & EP_STALLED) &&
114762306a36Sopenharmony_ci		    !(pep->ep_state & EP_STALL_PENDING)) {
114862306a36Sopenharmony_ci			if (pep->ep_state & EP_DEFERRED_DRDY) {
114962306a36Sopenharmony_ci				pep->ep_state &= ~EP_DEFERRED_DRDY;
115062306a36Sopenharmony_ci				cdns2_start_all_request(pdev, pep);
115162306a36Sopenharmony_ci			} else {
115262306a36Sopenharmony_ci				cdns2_rearm_transfer(pep, pep->wa1_set);
115362306a36Sopenharmony_ci			}
115462306a36Sopenharmony_ci		}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci		return;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	if ((ep_sts_reg & DMA_EP_STS_IOC) || (ep_sts_reg & DMA_EP_STS_ISP))
116062306a36Sopenharmony_ci		cdns2_transfer_completed(pdev, pep);
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cistatic void cdns2_disconnect_gadget(struct cdns2_device *pdev)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	if (pdev->gadget_driver && pdev->gadget_driver->disconnect)
116662306a36Sopenharmony_ci		pdev->gadget_driver->disconnect(&pdev->gadget);
116762306a36Sopenharmony_ci}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_cistatic irqreturn_t cdns2_usb_irq_handler(int irq, void *data)
117062306a36Sopenharmony_ci{
117162306a36Sopenharmony_ci	struct cdns2_device *pdev = data;
117262306a36Sopenharmony_ci	unsigned long reg_ep_ists;
117362306a36Sopenharmony_ci	u8 reg_usb_irq_m;
117462306a36Sopenharmony_ci	u8 reg_ext_irq_m;
117562306a36Sopenharmony_ci	u8 reg_usb_irq;
117662306a36Sopenharmony_ci	u8 reg_ext_irq;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	if (pdev->in_lpm)
117962306a36Sopenharmony_ci		return IRQ_NONE;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	reg_usb_irq_m = readb(&pdev->interrupt_regs->usbien);
118262306a36Sopenharmony_ci	reg_ext_irq_m = readb(&pdev->interrupt_regs->extien);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	/* Mask all sources of interrupt. */
118562306a36Sopenharmony_ci	writeb(0, &pdev->interrupt_regs->usbien);
118662306a36Sopenharmony_ci	writeb(0, &pdev->interrupt_regs->extien);
118762306a36Sopenharmony_ci	writel(0, &pdev->adma_regs->ep_ien);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	/* Clear interrupt sources. */
119062306a36Sopenharmony_ci	writel(0, &pdev->adma_regs->ep_sts);
119162306a36Sopenharmony_ci	writeb(0, &pdev->interrupt_regs->usbirq);
119262306a36Sopenharmony_ci	writeb(0, &pdev->interrupt_regs->extirq);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	reg_ep_ists = readl(&pdev->adma_regs->ep_ists);
119562306a36Sopenharmony_ci	reg_usb_irq = readb(&pdev->interrupt_regs->usbirq);
119662306a36Sopenharmony_ci	reg_ext_irq = readb(&pdev->interrupt_regs->extirq);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	if (reg_ep_ists || (reg_usb_irq & reg_usb_irq_m) ||
119962306a36Sopenharmony_ci	    (reg_ext_irq & reg_ext_irq_m))
120062306a36Sopenharmony_ci		return IRQ_WAKE_THREAD;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien);
120362306a36Sopenharmony_ci	writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien);
120462306a36Sopenharmony_ci	writel(~0, &pdev->adma_regs->ep_ien);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	return IRQ_NONE;
120762306a36Sopenharmony_ci}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_cistatic irqreturn_t cdns2_thread_usb_irq_handler(struct cdns2_device *pdev)
121062306a36Sopenharmony_ci{
121162306a36Sopenharmony_ci	u8 usb_irq, ext_irq;
121262306a36Sopenharmony_ci	int speed;
121362306a36Sopenharmony_ci	int i;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	ext_irq = readb(&pdev->interrupt_regs->extirq) & EXTIRQ_WAKEUP;
121662306a36Sopenharmony_ci	writeb(ext_irq, &pdev->interrupt_regs->extirq);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	usb_irq = readb(&pdev->interrupt_regs->usbirq) & USB_IEN_INIT;
121962306a36Sopenharmony_ci	writeb(usb_irq, &pdev->interrupt_regs->usbirq);
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	if (!ext_irq && !usb_irq)
122262306a36Sopenharmony_ci		return IRQ_NONE;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	trace_cdns2_usb_irq(usb_irq, ext_irq);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	if (ext_irq & EXTIRQ_WAKEUP) {
122762306a36Sopenharmony_ci		if (pdev->gadget_driver && pdev->gadget_driver->resume) {
122862306a36Sopenharmony_ci			spin_unlock(&pdev->lock);
122962306a36Sopenharmony_ci			pdev->gadget_driver->resume(&pdev->gadget);
123062306a36Sopenharmony_ci			spin_lock(&pdev->lock);
123162306a36Sopenharmony_ci		}
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	if (usb_irq & USBIRQ_LPM) {
123562306a36Sopenharmony_ci		u8 reg = readb(&pdev->usb_regs->lpmctrl);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci		/* LPM1 enter */
123862306a36Sopenharmony_ci		if (!(reg & LPMCTRLLH_LPMNYET))
123962306a36Sopenharmony_ci			writeb(0, &pdev->usb_regs->sleep_clkgate);
124062306a36Sopenharmony_ci	}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	if (usb_irq & USBIRQ_SUSPEND) {
124362306a36Sopenharmony_ci		if (pdev->gadget_driver && pdev->gadget_driver->suspend) {
124462306a36Sopenharmony_ci			spin_unlock(&pdev->lock);
124562306a36Sopenharmony_ci			pdev->gadget_driver->suspend(&pdev->gadget);
124662306a36Sopenharmony_ci			spin_lock(&pdev->lock);
124762306a36Sopenharmony_ci		}
124862306a36Sopenharmony_ci	}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	if (usb_irq & USBIRQ_URESET) {
125162306a36Sopenharmony_ci		if (pdev->gadget_driver) {
125262306a36Sopenharmony_ci			pdev->dev_address = 0;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci			spin_unlock(&pdev->lock);
125562306a36Sopenharmony_ci			usb_gadget_udc_reset(&pdev->gadget,
125662306a36Sopenharmony_ci					     pdev->gadget_driver);
125762306a36Sopenharmony_ci			spin_lock(&pdev->lock);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci			/*
126062306a36Sopenharmony_ci			 * The USBIRQ_URESET is reported at the beginning of
126162306a36Sopenharmony_ci			 * reset signal. 100ms is enough time to finish reset
126262306a36Sopenharmony_ci			 * process. For high-speed reset procedure is completed
126362306a36Sopenharmony_ci			 * when controller detect HS mode.
126462306a36Sopenharmony_ci			 */
126562306a36Sopenharmony_ci			for (i = 0; i < 100; i++) {
126662306a36Sopenharmony_ci				mdelay(1);
126762306a36Sopenharmony_ci				speed = cdns2_get_speed(pdev);
126862306a36Sopenharmony_ci				if (speed == USB_SPEED_HIGH)
126962306a36Sopenharmony_ci					break;
127062306a36Sopenharmony_ci			}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci			pdev->gadget.speed = speed;
127362306a36Sopenharmony_ci			cdns2_enable_l1(pdev, 0);
127462306a36Sopenharmony_ci			cdns2_ep0_config(pdev);
127562306a36Sopenharmony_ci			pdev->may_wakeup = 0;
127662306a36Sopenharmony_ci		}
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	if (usb_irq & USBIRQ_SUDAV) {
128062306a36Sopenharmony_ci		pdev->ep0_stage = CDNS2_SETUP_STAGE;
128162306a36Sopenharmony_ci		cdns2_handle_setup_packet(pdev);
128262306a36Sopenharmony_ci	}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	return IRQ_HANDLED;
128562306a36Sopenharmony_ci}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci/* Deferred USB interrupt handler. */
128862306a36Sopenharmony_cistatic irqreturn_t cdns2_thread_irq_handler(int irq, void *data)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	struct cdns2_device *pdev = data;
129162306a36Sopenharmony_ci	unsigned long  dma_ep_ists;
129262306a36Sopenharmony_ci	unsigned long flags;
129362306a36Sopenharmony_ci	unsigned int bit;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	local_bh_disable();
129662306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	cdns2_thread_usb_irq_handler(pdev);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	dma_ep_ists = readl(&pdev->adma_regs->ep_ists);
130162306a36Sopenharmony_ci	if (!dma_ep_ists)
130262306a36Sopenharmony_ci		goto unlock;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	trace_cdns2_dma_ep_ists(dma_ep_ists);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	/* Handle default endpoint OUT. */
130762306a36Sopenharmony_ci	if (dma_ep_ists & DMA_EP_ISTS_EP_OUT0)
130862306a36Sopenharmony_ci		cdns2_handle_ep0_interrupt(pdev, USB_DIR_OUT);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	/* Handle default endpoint IN. */
131162306a36Sopenharmony_ci	if (dma_ep_ists & DMA_EP_ISTS_EP_IN0)
131262306a36Sopenharmony_ci		cdns2_handle_ep0_interrupt(pdev, USB_DIR_IN);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	dma_ep_ists &= ~(DMA_EP_ISTS_EP_OUT0 | DMA_EP_ISTS_EP_IN0);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	for_each_set_bit(bit, &dma_ep_ists, sizeof(u32) * BITS_PER_BYTE) {
131762306a36Sopenharmony_ci		u8 ep_idx = bit > 16 ? (bit - 16) * 2 : (bit * 2) - 1;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci		/*
132062306a36Sopenharmony_ci		 * Endpoints in pdev->eps[] are held in order:
132162306a36Sopenharmony_ci		 * ep0, ep1out, ep1in, ep2out, ep2in... ep15out, ep15in.
132262306a36Sopenharmony_ci		 * but in dma_ep_ists in order:
132362306a36Sopenharmony_ci		 * ep0 ep1out ep2out ... ep15out ep0in ep1in .. ep15in
132462306a36Sopenharmony_ci		 */
132562306a36Sopenharmony_ci		cdns2_handle_epx_interrupt(&pdev->eps[ep_idx]);
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ciunlock:
132962306a36Sopenharmony_ci	writel(~0, &pdev->adma_regs->ep_ien);
133062306a36Sopenharmony_ci	writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien);
133162306a36Sopenharmony_ci	writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
133462306a36Sopenharmony_ci	local_bh_enable();
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	return IRQ_HANDLED;
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci/* Calculates and assigns onchip memory for endpoints. */
134062306a36Sopenharmony_cistatic void cdns2_eps_onchip_buffer_init(struct cdns2_device *pdev)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	struct cdns2_endpoint *pep;
134362306a36Sopenharmony_ci	int min_buf_tx = 0;
134462306a36Sopenharmony_ci	int min_buf_rx = 0;
134562306a36Sopenharmony_ci	u16 tx_offset = 0;
134662306a36Sopenharmony_ci	u16 rx_offset = 0;
134762306a36Sopenharmony_ci	int free;
134862306a36Sopenharmony_ci	int i;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) {
135162306a36Sopenharmony_ci		pep = &pdev->eps[i];
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci		if (!(pep->ep_state & EP_CLAIMED))
135462306a36Sopenharmony_ci			continue;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci		if (pep->dir)
135762306a36Sopenharmony_ci			min_buf_tx += pep->buffering;
135862306a36Sopenharmony_ci		else
135962306a36Sopenharmony_ci			min_buf_rx += pep->buffering;
136062306a36Sopenharmony_ci	}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) {
136362306a36Sopenharmony_ci		pep = &pdev->eps[i];
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci		if (!(pep->ep_state & EP_CLAIMED))
136662306a36Sopenharmony_ci			continue;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci		if (pep->dir) {
136962306a36Sopenharmony_ci			free = pdev->onchip_tx_buf - min_buf_tx;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci			if (free + pep->buffering >= 4)
137262306a36Sopenharmony_ci				free = 4;
137362306a36Sopenharmony_ci			else
137462306a36Sopenharmony_ci				free = free + pep->buffering;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci			min_buf_tx = min_buf_tx - pep->buffering + free;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci			pep->buffering = free;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci			writel(tx_offset,
138162306a36Sopenharmony_ci			       &pdev->epx_regs->txstaddr[pep->num - 1]);
138262306a36Sopenharmony_ci			pdev->epx_regs->txstaddr[pep->num - 1] = tx_offset;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci			dev_dbg(pdev->dev, "%s onchip address %04x, buffering: %d\n",
138562306a36Sopenharmony_ci				pep->name, tx_offset, pep->buffering);
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci			tx_offset += pep->buffering * 1024;
138862306a36Sopenharmony_ci		} else {
138962306a36Sopenharmony_ci			free = pdev->onchip_rx_buf - min_buf_rx;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci			if (free + pep->buffering >= 4)
139262306a36Sopenharmony_ci				free = 4;
139362306a36Sopenharmony_ci			else
139462306a36Sopenharmony_ci				free = free + pep->buffering;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci			min_buf_rx = min_buf_rx - pep->buffering + free;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci			pep->buffering = free;
139962306a36Sopenharmony_ci			writel(rx_offset,
140062306a36Sopenharmony_ci			       &pdev->epx_regs->rxstaddr[pep->num - 1]);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci			dev_dbg(pdev->dev, "%s onchip address %04x, buffering: %d\n",
140362306a36Sopenharmony_ci				pep->name, rx_offset, pep->buffering);
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci			rx_offset += pep->buffering * 1024;
140662306a36Sopenharmony_ci		}
140762306a36Sopenharmony_ci	}
140862306a36Sopenharmony_ci}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci/* Configure hardware endpoint. */
141162306a36Sopenharmony_cistatic int cdns2_ep_config(struct cdns2_endpoint *pep, bool enable)
141262306a36Sopenharmony_ci{
141362306a36Sopenharmony_ci	bool is_iso_ep = (pep->type == USB_ENDPOINT_XFER_ISOC);
141462306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
141562306a36Sopenharmony_ci	u32 max_packet_size;
141662306a36Sopenharmony_ci	u8 dir = 0;
141762306a36Sopenharmony_ci	u8 ep_cfg;
141862306a36Sopenharmony_ci	u8 mult;
141962306a36Sopenharmony_ci	u32 val;
142062306a36Sopenharmony_ci	int ret;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	switch (pep->type) {
142362306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_INT:
142462306a36Sopenharmony_ci		ep_cfg = EPX_CON_TYPE_INT;
142562306a36Sopenharmony_ci		break;
142662306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_BULK:
142762306a36Sopenharmony_ci		ep_cfg = EPX_CON_TYPE_BULK;
142862306a36Sopenharmony_ci		break;
142962306a36Sopenharmony_ci	default:
143062306a36Sopenharmony_ci		mult = USB_EP_MAXP_MULT(pep->endpoint.desc->wMaxPacketSize);
143162306a36Sopenharmony_ci		ep_cfg = mult << EPX_CON_ISOD_SHIFT;
143262306a36Sopenharmony_ci		ep_cfg |= EPX_CON_TYPE_ISOC;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci		if (pep->dir) {
143562306a36Sopenharmony_ci			set_reg_bit_8(&pdev->epx_regs->isoautoarm, BIT(pep->num));
143662306a36Sopenharmony_ci			set_reg_bit_8(&pdev->epx_regs->isoautodump, BIT(pep->num));
143762306a36Sopenharmony_ci			set_reg_bit_8(&pdev->epx_regs->isodctrl, BIT(pep->num));
143862306a36Sopenharmony_ci		}
143962306a36Sopenharmony_ci	}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	switch (pdev->gadget.speed) {
144262306a36Sopenharmony_ci	case USB_SPEED_FULL:
144362306a36Sopenharmony_ci		max_packet_size = is_iso_ep ? 1023 : 64;
144462306a36Sopenharmony_ci		break;
144562306a36Sopenharmony_ci	case USB_SPEED_HIGH:
144662306a36Sopenharmony_ci		max_packet_size = is_iso_ep ? 1024 : 512;
144762306a36Sopenharmony_ci		break;
144862306a36Sopenharmony_ci	default:
144962306a36Sopenharmony_ci		/* All other speed are not supported. */
145062306a36Sopenharmony_ci		return -EINVAL;
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	ep_cfg |= (EPX_CON_VAL | (pep->buffering - 1));
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	if (pep->dir) {
145662306a36Sopenharmony_ci		dir = FIFOCTRL_IO_TX;
145762306a36Sopenharmony_ci		writew(max_packet_size, &pdev->epx_regs->txmaxpack[pep->num - 1]);
145862306a36Sopenharmony_ci		writeb(ep_cfg, &pdev->epx_regs->ep[pep->num - 1].txcon);
145962306a36Sopenharmony_ci	} else {
146062306a36Sopenharmony_ci		writew(max_packet_size, &pdev->epx_regs->rxmaxpack[pep->num - 1]);
146162306a36Sopenharmony_ci		writeb(ep_cfg, &pdev->epx_regs->ep[pep->num - 1].rxcon);
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	writeb(pep->num | dir | FIFOCTRL_FIFOAUTO,
146562306a36Sopenharmony_ci	       &pdev->usb_regs->fifoctrl);
146662306a36Sopenharmony_ci	writeb(pep->num | dir, &pdev->epx_regs->endprst);
146762306a36Sopenharmony_ci	writeb(pep->num | ENDPRST_FIFORST | ENDPRST_TOGRST | dir,
146862306a36Sopenharmony_ci	       &pdev->epx_regs->endprst);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	if (max_packet_size == 1024)
147162306a36Sopenharmony_ci		pep->trb_burst_size = 128;
147262306a36Sopenharmony_ci	else if (max_packet_size >= 512)
147362306a36Sopenharmony_ci		pep->trb_burst_size = 64;
147462306a36Sopenharmony_ci	else
147562306a36Sopenharmony_ci		pep->trb_burst_size = 16;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	cdns2_select_ep(pdev, pep->num | pep->dir);
147862306a36Sopenharmony_ci	writel(DMA_EP_CMD_EPRST | DMA_EP_CMD_DFLUSH, &pdev->adma_regs->ep_cmd);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	ret = readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
148162306a36Sopenharmony_ci					!(val & (DMA_EP_CMD_DFLUSH |
148262306a36Sopenharmony_ci					DMA_EP_CMD_EPRST)),
148362306a36Sopenharmony_ci					1, 1000);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	if (ret)
148662306a36Sopenharmony_ci		return ret;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	writel(DMA_EP_STS_TRBERR | DMA_EP_STS_ISOERR, &pdev->adma_regs->ep_sts_en);
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	if (enable)
149162306a36Sopenharmony_ci		writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg);
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	trace_cdns2_epx_hw_cfg(pdev, pep);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	dev_dbg(pdev->dev, "Configure %s: with MPS: %08x, ep con: %02x\n",
149662306a36Sopenharmony_ci		pep->name, max_packet_size, ep_cfg);
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	return 0;
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_cistruct usb_request *cdns2_gadget_ep_alloc_request(struct usb_ep *ep,
150262306a36Sopenharmony_ci						  gfp_t gfp_flags)
150362306a36Sopenharmony_ci{
150462306a36Sopenharmony_ci	struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
150562306a36Sopenharmony_ci	struct cdns2_request *preq;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	preq = kzalloc(sizeof(*preq), gfp_flags);
150862306a36Sopenharmony_ci	if (!preq)
150962306a36Sopenharmony_ci		return NULL;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	preq->pep = pep;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	trace_cdns2_alloc_request(preq);
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	return &preq->request;
151662306a36Sopenharmony_ci}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_civoid cdns2_gadget_ep_free_request(struct usb_ep *ep,
151962306a36Sopenharmony_ci				  struct usb_request *request)
152062306a36Sopenharmony_ci{
152162306a36Sopenharmony_ci	struct cdns2_request *preq = to_cdns2_request(request);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	trace_cdns2_free_request(preq);
152462306a36Sopenharmony_ci	kfree(preq);
152562306a36Sopenharmony_ci}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_cistatic int cdns2_gadget_ep_enable(struct usb_ep *ep,
152862306a36Sopenharmony_ci				  const struct usb_endpoint_descriptor *desc)
152962306a36Sopenharmony_ci{
153062306a36Sopenharmony_ci	u32 reg = DMA_EP_STS_EN_TRBERREN;
153162306a36Sopenharmony_ci	struct cdns2_endpoint *pep;
153262306a36Sopenharmony_ci	struct cdns2_device *pdev;
153362306a36Sopenharmony_ci	unsigned long flags;
153462306a36Sopenharmony_ci	int enable = 1;
153562306a36Sopenharmony_ci	int ret = 0;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT ||
153862306a36Sopenharmony_ci	    !desc->wMaxPacketSize) {
153962306a36Sopenharmony_ci		return -EINVAL;
154062306a36Sopenharmony_ci	}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	pep = ep_to_cdns2_ep(ep);
154362306a36Sopenharmony_ci	pdev = pep->pdev;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	if (dev_WARN_ONCE(pdev->dev, pep->ep_state & EP_ENABLED,
154662306a36Sopenharmony_ci			  "%s is already enabled\n", pep->name))
154762306a36Sopenharmony_ci		return 0;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	pep->type = usb_endpoint_type(desc);
155262306a36Sopenharmony_ci	pep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	if (pdev->gadget.speed == USB_SPEED_FULL)
155562306a36Sopenharmony_ci		if (pep->type == USB_ENDPOINT_XFER_INT)
155662306a36Sopenharmony_ci			pep->interval = desc->bInterval;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	if (pep->interval > ISO_MAX_INTERVAL &&
155962306a36Sopenharmony_ci	    pep->type == USB_ENDPOINT_XFER_ISOC) {
156062306a36Sopenharmony_ci		dev_err(pdev->dev, "ISO period is limited to %d (current: %d)\n",
156162306a36Sopenharmony_ci			ISO_MAX_INTERVAL, pep->interval);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci		ret =  -EINVAL;
156462306a36Sopenharmony_ci		goto exit;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	/*
156862306a36Sopenharmony_ci	 * During ISO OUT traffic DMA reads Transfer Ring for the EP which has
156962306a36Sopenharmony_ci	 * never got doorbell.
157062306a36Sopenharmony_ci	 * This issue was detected only on simulation, but to avoid this issue
157162306a36Sopenharmony_ci	 * driver add protection against it. To fix it driver enable ISO OUT
157262306a36Sopenharmony_ci	 * endpoint before setting DRBL. This special treatment of ISO OUT
157362306a36Sopenharmony_ci	 * endpoints are recommended by controller specification.
157462306a36Sopenharmony_ci	 */
157562306a36Sopenharmony_ci	if (pep->type == USB_ENDPOINT_XFER_ISOC  && !pep->dir)
157662306a36Sopenharmony_ci		enable = 0;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	ret = cdns2_alloc_tr_segment(pep);
157962306a36Sopenharmony_ci	if (ret)
158062306a36Sopenharmony_ci		goto exit;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	ret = cdns2_ep_config(pep, enable);
158362306a36Sopenharmony_ci	if (ret) {
158462306a36Sopenharmony_ci		cdns2_free_tr_segment(pep);
158562306a36Sopenharmony_ci		ret =  -EINVAL;
158662306a36Sopenharmony_ci		goto exit;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	trace_cdns2_gadget_ep_enable(pep);
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	pep->ep_state &= ~(EP_STALLED | EP_STALL_PENDING);
159262306a36Sopenharmony_ci	pep->ep_state |= EP_ENABLED;
159362306a36Sopenharmony_ci	pep->wa1_set = 0;
159462306a36Sopenharmony_ci	pep->ring.enqueue = 0;
159562306a36Sopenharmony_ci	pep->ring.dequeue = 0;
159662306a36Sopenharmony_ci	reg = readl(&pdev->adma_regs->ep_sts);
159762306a36Sopenharmony_ci	pep->ring.pcs = !!DMA_EP_STS_CCS(reg);
159862306a36Sopenharmony_ci	pep->ring.ccs = !!DMA_EP_STS_CCS(reg);
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	writel(pep->ring.dma, &pdev->adma_regs->ep_traddr);
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	/* one TRB is reserved for link TRB used in DMULT mode*/
160362306a36Sopenharmony_ci	pep->ring.free_trbs = TRBS_PER_SEGMENT - 1;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ciexit:
160662306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	return ret;
160962306a36Sopenharmony_ci}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_cistatic int cdns2_gadget_ep_disable(struct usb_ep *ep)
161262306a36Sopenharmony_ci{
161362306a36Sopenharmony_ci	struct cdns2_endpoint *pep;
161462306a36Sopenharmony_ci	struct cdns2_request *preq;
161562306a36Sopenharmony_ci	struct cdns2_device *pdev;
161662306a36Sopenharmony_ci	unsigned long flags;
161762306a36Sopenharmony_ci	int val;
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	if (!ep)
162062306a36Sopenharmony_ci		return -EINVAL;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	pep = ep_to_cdns2_ep(ep);
162362306a36Sopenharmony_ci	pdev = pep->pdev;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	if (dev_WARN_ONCE(pdev->dev, !(pep->ep_state & EP_ENABLED),
162662306a36Sopenharmony_ci			  "%s is already disabled\n", pep->name))
162762306a36Sopenharmony_ci		return 0;
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	trace_cdns2_gadget_ep_disable(pep);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	cdns2_select_ep(pdev, ep->desc->bEndpointAddress);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	clear_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	/*
163862306a36Sopenharmony_ci	 * Driver needs some time before resetting endpoint.
163962306a36Sopenharmony_ci	 * It need waits for clearing DBUSY bit or for timeout expired.
164062306a36Sopenharmony_ci	 * 10us is enough time for controller to stop transfer.
164162306a36Sopenharmony_ci	 */
164262306a36Sopenharmony_ci	readl_poll_timeout_atomic(&pdev->adma_regs->ep_sts, val,
164362306a36Sopenharmony_ci				  !(val & DMA_EP_STS_DBUSY), 1, 10);
164462306a36Sopenharmony_ci	writel(DMA_EP_CMD_EPRST, &pdev->adma_regs->ep_cmd);
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
164762306a36Sopenharmony_ci				  !(val & (DMA_EP_CMD_DFLUSH | DMA_EP_CMD_EPRST)),
164862306a36Sopenharmony_ci				  1, 1000);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	while (!list_empty(&pep->pending_list)) {
165162306a36Sopenharmony_ci		preq = cdns2_next_preq(&pep->pending_list);
165262306a36Sopenharmony_ci		cdns2_gadget_giveback(pep, preq, -ESHUTDOWN);
165362306a36Sopenharmony_ci	}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	while (!list_empty(&pep->deferred_list)) {
165662306a36Sopenharmony_ci		preq = cdns2_next_preq(&pep->deferred_list);
165762306a36Sopenharmony_ci		cdns2_gadget_giveback(pep, preq, -ESHUTDOWN);
165862306a36Sopenharmony_ci	}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	ep->desc = NULL;
166162306a36Sopenharmony_ci	pep->ep_state &= ~EP_ENABLED;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	return 0;
166662306a36Sopenharmony_ci}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_cistatic int cdns2_ep_enqueue(struct cdns2_endpoint *pep,
166962306a36Sopenharmony_ci			    struct cdns2_request *preq,
167062306a36Sopenharmony_ci			    gfp_t gfp_flags)
167162306a36Sopenharmony_ci{
167262306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
167362306a36Sopenharmony_ci	struct usb_request *request;
167462306a36Sopenharmony_ci	int ret;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	request = &preq->request;
167762306a36Sopenharmony_ci	request->actual = 0;
167862306a36Sopenharmony_ci	request->status = -EINPROGRESS;
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	ret = usb_gadget_map_request_by_dev(pdev->dev, request, pep->dir);
168162306a36Sopenharmony_ci	if (ret) {
168262306a36Sopenharmony_ci		trace_cdns2_request_enqueue_error(preq);
168362306a36Sopenharmony_ci		return ret;
168462306a36Sopenharmony_ci	}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	list_add_tail(&preq->list, &pep->deferred_list);
168762306a36Sopenharmony_ci	trace_cdns2_request_enqueue(preq);
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	if (!(pep->ep_state & EP_STALLED) && !(pep->ep_state & EP_STALL_PENDING))
169062306a36Sopenharmony_ci		cdns2_start_all_request(pdev, pep);
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	return 0;
169362306a36Sopenharmony_ci}
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_cistatic int cdns2_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
169662306a36Sopenharmony_ci				 gfp_t gfp_flags)
169762306a36Sopenharmony_ci{
169862306a36Sopenharmony_ci	struct usb_request *zlp_request;
169962306a36Sopenharmony_ci	struct cdns2_request *preq;
170062306a36Sopenharmony_ci	struct cdns2_endpoint *pep;
170162306a36Sopenharmony_ci	struct cdns2_device *pdev;
170262306a36Sopenharmony_ci	unsigned long flags;
170362306a36Sopenharmony_ci	int ret;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	if (!request || !ep)
170662306a36Sopenharmony_ci		return -EINVAL;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	pep = ep_to_cdns2_ep(ep);
170962306a36Sopenharmony_ci	pdev = pep->pdev;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	if (!(pep->ep_state & EP_ENABLED)) {
171262306a36Sopenharmony_ci		dev_err(pdev->dev, "%s: can't queue to disabled endpoint\n",
171362306a36Sopenharmony_ci			pep->name);
171462306a36Sopenharmony_ci		return -EINVAL;
171562306a36Sopenharmony_ci	}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	preq =  to_cdns2_request(request);
172062306a36Sopenharmony_ci	ret = cdns2_ep_enqueue(pep, preq, gfp_flags);
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	if (ret == 0 && request->zero && request->length &&
172362306a36Sopenharmony_ci	    (request->length % ep->maxpacket == 0)) {
172462306a36Sopenharmony_ci		struct cdns2_request *preq;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci		zlp_request = cdns2_gadget_ep_alloc_request(ep, GFP_ATOMIC);
172762306a36Sopenharmony_ci		zlp_request->buf = pdev->zlp_buf;
172862306a36Sopenharmony_ci		zlp_request->length = 0;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci		preq = to_cdns2_request(zlp_request);
173162306a36Sopenharmony_ci		ret = cdns2_ep_enqueue(pep, preq, gfp_flags);
173262306a36Sopenharmony_ci	}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
173562306a36Sopenharmony_ci	return ret;
173662306a36Sopenharmony_ci}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ciint cdns2_gadget_ep_dequeue(struct usb_ep *ep,
173962306a36Sopenharmony_ci			    struct usb_request *request)
174062306a36Sopenharmony_ci{
174162306a36Sopenharmony_ci	struct cdns2_request *preq, *preq_temp, *cur_preq;
174262306a36Sopenharmony_ci	struct cdns2_endpoint *pep;
174362306a36Sopenharmony_ci	struct cdns2_trb *link_trb;
174462306a36Sopenharmony_ci	u8 req_on_hw_ring = 0;
174562306a36Sopenharmony_ci	unsigned long flags;
174662306a36Sopenharmony_ci	u32 buffer;
174762306a36Sopenharmony_ci	int val, i;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	if (!ep || !request || !ep->desc)
175062306a36Sopenharmony_ci		return -EINVAL;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	pep = ep_to_cdns2_ep(ep);
175362306a36Sopenharmony_ci	if (!pep->endpoint.desc) {
175462306a36Sopenharmony_ci		dev_err(pep->pdev->dev, "%s: can't dequeue to disabled endpoint\n",
175562306a36Sopenharmony_ci			pep->name);
175662306a36Sopenharmony_ci		return -ESHUTDOWN;
175762306a36Sopenharmony_ci	}
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	/* Requests has been dequeued during disabling endpoint. */
176062306a36Sopenharmony_ci	if (!(pep->ep_state & EP_ENABLED))
176162306a36Sopenharmony_ci		return 0;
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	spin_lock_irqsave(&pep->pdev->lock, flags);
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	cur_preq = to_cdns2_request(request);
176662306a36Sopenharmony_ci	trace_cdns2_request_dequeue(cur_preq);
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	list_for_each_entry_safe(preq, preq_temp, &pep->pending_list, list) {
176962306a36Sopenharmony_ci		if (cur_preq == preq) {
177062306a36Sopenharmony_ci			req_on_hw_ring = 1;
177162306a36Sopenharmony_ci			goto found;
177262306a36Sopenharmony_ci		}
177362306a36Sopenharmony_ci	}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	list_for_each_entry_safe(preq, preq_temp, &pep->deferred_list, list) {
177662306a36Sopenharmony_ci		if (cur_preq == preq)
177762306a36Sopenharmony_ci			goto found;
177862306a36Sopenharmony_ci	}
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	goto not_found;
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_cifound:
178362306a36Sopenharmony_ci	link_trb = preq->trb;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	/* Update ring only if removed request is on pending_req_list list. */
178662306a36Sopenharmony_ci	if (req_on_hw_ring && link_trb) {
178762306a36Sopenharmony_ci		/* Stop DMA */
178862306a36Sopenharmony_ci		writel(DMA_EP_CMD_DFLUSH, &pep->pdev->adma_regs->ep_cmd);
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci		/* Wait for DFLUSH cleared. */
179162306a36Sopenharmony_ci		readl_poll_timeout_atomic(&pep->pdev->adma_regs->ep_cmd, val,
179262306a36Sopenharmony_ci					  !(val & DMA_EP_CMD_DFLUSH), 1, 1000);
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci		buffer = cpu_to_le32(TRB_BUFFER(pep->ring.dma +
179562306a36Sopenharmony_ci				    ((preq->end_trb + 1) * TRB_SIZE)));
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci		for (i = 0; i < preq->num_of_trb; i++) {
179862306a36Sopenharmony_ci			link_trb->buffer = buffer;
179962306a36Sopenharmony_ci			link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control)
180062306a36Sopenharmony_ci					    & TRB_CYCLE) | TRB_CHAIN |
180162306a36Sopenharmony_ci					    TRB_TYPE(TRB_LINK));
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci			trace_cdns2_queue_trb(pep, link_trb);
180462306a36Sopenharmony_ci			link_trb = cdns2_next_trb(pep, link_trb);
180562306a36Sopenharmony_ci		}
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci		if (pep->wa1_trb == preq->trb)
180862306a36Sopenharmony_ci			cdns2_wa1_restore_cycle_bit(pep);
180962306a36Sopenharmony_ci	}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	cdns2_gadget_giveback(pep, cur_preq, -ECONNRESET);
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	preq = cdns2_next_preq(&pep->pending_list);
181462306a36Sopenharmony_ci	if (preq)
181562306a36Sopenharmony_ci		cdns2_rearm_transfer(pep, 1);
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_cinot_found:
181862306a36Sopenharmony_ci	spin_unlock_irqrestore(&pep->pdev->lock, flags);
181962306a36Sopenharmony_ci	return 0;
182062306a36Sopenharmony_ci}
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ciint cdns2_halt_endpoint(struct cdns2_device *pdev,
182362306a36Sopenharmony_ci			struct cdns2_endpoint *pep,
182462306a36Sopenharmony_ci			int value)
182562306a36Sopenharmony_ci{
182662306a36Sopenharmony_ci	u8 __iomem *conf;
182762306a36Sopenharmony_ci	int dir = 0;
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	if (!(pep->ep_state & EP_ENABLED))
183062306a36Sopenharmony_ci		return -EPERM;
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	if (pep->dir) {
183362306a36Sopenharmony_ci		dir = ENDPRST_IO_TX;
183462306a36Sopenharmony_ci		conf = &pdev->epx_regs->ep[pep->num - 1].txcon;
183562306a36Sopenharmony_ci	} else {
183662306a36Sopenharmony_ci		conf = &pdev->epx_regs->ep[pep->num - 1].rxcon;
183762306a36Sopenharmony_ci	}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	if (!value) {
184062306a36Sopenharmony_ci		struct cdns2_trb *trb = NULL;
184162306a36Sopenharmony_ci		struct cdns2_request *preq;
184262306a36Sopenharmony_ci		struct cdns2_trb trb_tmp;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci		preq = cdns2_next_preq(&pep->pending_list);
184562306a36Sopenharmony_ci		if (preq) {
184662306a36Sopenharmony_ci			trb = preq->trb;
184762306a36Sopenharmony_ci			if (trb) {
184862306a36Sopenharmony_ci				trb_tmp = *trb;
184962306a36Sopenharmony_ci				trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE);
185062306a36Sopenharmony_ci			}
185162306a36Sopenharmony_ci		}
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci		trace_cdns2_ep_halt(pep, 0, 0);
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci		/* Resets Sequence Number */
185662306a36Sopenharmony_ci		writeb(dir | pep->num, &pdev->epx_regs->endprst);
185762306a36Sopenharmony_ci		writeb(dir | ENDPRST_TOGRST | pep->num,
185862306a36Sopenharmony_ci		       &pdev->epx_regs->endprst);
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci		clear_reg_bit_8(conf, EPX_CON_STALL);
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci		pep->ep_state &= ~(EP_STALLED | EP_STALL_PENDING);
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci		if (preq) {
186562306a36Sopenharmony_ci			if (trb)
186662306a36Sopenharmony_ci				*trb = trb_tmp;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci			cdns2_rearm_transfer(pep, 1);
186962306a36Sopenharmony_ci		}
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci		cdns2_start_all_request(pdev, pep);
187262306a36Sopenharmony_ci	} else {
187362306a36Sopenharmony_ci		trace_cdns2_ep_halt(pep, 1, 0);
187462306a36Sopenharmony_ci		set_reg_bit_8(conf, EPX_CON_STALL);
187562306a36Sopenharmony_ci		writeb(dir | pep->num, &pdev->epx_regs->endprst);
187662306a36Sopenharmony_ci		writeb(dir | ENDPRST_FIFORST | pep->num,
187762306a36Sopenharmony_ci		       &pdev->epx_regs->endprst);
187862306a36Sopenharmony_ci		pep->ep_state |= EP_STALLED;
187962306a36Sopenharmony_ci	}
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	return 0;
188262306a36Sopenharmony_ci}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci/* Sets/clears stall on selected endpoint. */
188562306a36Sopenharmony_cistatic int cdns2_gadget_ep_set_halt(struct usb_ep *ep, int value)
188662306a36Sopenharmony_ci{
188762306a36Sopenharmony_ci	struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
188862306a36Sopenharmony_ci	struct cdns2_device *pdev = pep->pdev;
188962306a36Sopenharmony_ci	struct cdns2_request *preq;
189062306a36Sopenharmony_ci	unsigned long flags = 0;
189162306a36Sopenharmony_ci	int ret;
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	preq = cdns2_next_preq(&pep->pending_list);
189662306a36Sopenharmony_ci	if (value && preq) {
189762306a36Sopenharmony_ci		trace_cdns2_ep_busy_try_halt_again(pep);
189862306a36Sopenharmony_ci		ret = -EAGAIN;
189962306a36Sopenharmony_ci		goto done;
190062306a36Sopenharmony_ci	}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	if (!value)
190362306a36Sopenharmony_ci		pep->ep_state &= ~EP_WEDGE;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	ret = cdns2_halt_endpoint(pdev, pep, value);
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_cidone:
190862306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
190962306a36Sopenharmony_ci	return ret;
191062306a36Sopenharmony_ci}
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_cistatic int cdns2_gadget_ep_set_wedge(struct usb_ep *ep)
191362306a36Sopenharmony_ci{
191462306a36Sopenharmony_ci	struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	cdns2_gadget_ep_set_halt(ep, 1);
191762306a36Sopenharmony_ci	pep->ep_state |= EP_WEDGE;
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	return 0;
192062306a36Sopenharmony_ci}
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_cistatic struct
192362306a36Sopenharmony_cicdns2_endpoint *cdns2_find_available_ep(struct cdns2_device *pdev,
192462306a36Sopenharmony_ci					struct usb_endpoint_descriptor *desc)
192562306a36Sopenharmony_ci{
192662306a36Sopenharmony_ci	struct cdns2_endpoint *pep;
192762306a36Sopenharmony_ci	struct usb_ep *ep;
192862306a36Sopenharmony_ci	int ep_correct;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	list_for_each_entry(ep, &pdev->gadget.ep_list, ep_list) {
193162306a36Sopenharmony_ci		unsigned long num;
193262306a36Sopenharmony_ci		int ret;
193362306a36Sopenharmony_ci		/* ep name pattern likes epXin or epXout. */
193462306a36Sopenharmony_ci		char c[2] = {ep->name[2], '\0'};
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci		ret = kstrtoul(c, 10, &num);
193762306a36Sopenharmony_ci		if (ret)
193862306a36Sopenharmony_ci			return ERR_PTR(ret);
193962306a36Sopenharmony_ci		pep = ep_to_cdns2_ep(ep);
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci		if (pep->num != num)
194262306a36Sopenharmony_ci			continue;
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci		ep_correct = (pep->endpoint.caps.dir_in &&
194562306a36Sopenharmony_ci			      usb_endpoint_dir_in(desc)) ||
194662306a36Sopenharmony_ci			     (pep->endpoint.caps.dir_out &&
194762306a36Sopenharmony_ci			      usb_endpoint_dir_out(desc));
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci		if (ep_correct && !(pep->ep_state & EP_CLAIMED))
195062306a36Sopenharmony_ci			return pep;
195162306a36Sopenharmony_ci	}
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	return ERR_PTR(-ENOENT);
195462306a36Sopenharmony_ci}
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci/*
195762306a36Sopenharmony_ci * Function used to recognize which endpoints will be used to optimize
195862306a36Sopenharmony_ci * on-chip memory usage.
195962306a36Sopenharmony_ci */
196062306a36Sopenharmony_cistatic struct
196162306a36Sopenharmony_ciusb_ep *cdns2_gadget_match_ep(struct usb_gadget *gadget,
196262306a36Sopenharmony_ci			      struct usb_endpoint_descriptor *desc,
196362306a36Sopenharmony_ci			      struct usb_ss_ep_comp_descriptor *comp_desc)
196462306a36Sopenharmony_ci{
196562306a36Sopenharmony_ci	struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
196662306a36Sopenharmony_ci	struct cdns2_endpoint *pep;
196762306a36Sopenharmony_ci	unsigned long flags;
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	pep = cdns2_find_available_ep(pdev, desc);
197062306a36Sopenharmony_ci	if (IS_ERR(pep)) {
197162306a36Sopenharmony_ci		dev_err(pdev->dev, "no available ep\n");
197262306a36Sopenharmony_ci		return NULL;
197362306a36Sopenharmony_ci	}
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_ISOC)
197862306a36Sopenharmony_ci		pep->buffering = 4;
197962306a36Sopenharmony_ci	else
198062306a36Sopenharmony_ci		pep->buffering = 1;
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci	pep->ep_state |= EP_CLAIMED;
198362306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci	return &pep->endpoint;
198662306a36Sopenharmony_ci}
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_cistatic const struct usb_ep_ops cdns2_gadget_ep_ops = {
198962306a36Sopenharmony_ci	.enable = cdns2_gadget_ep_enable,
199062306a36Sopenharmony_ci	.disable = cdns2_gadget_ep_disable,
199162306a36Sopenharmony_ci	.alloc_request = cdns2_gadget_ep_alloc_request,
199262306a36Sopenharmony_ci	.free_request = cdns2_gadget_ep_free_request,
199362306a36Sopenharmony_ci	.queue = cdns2_gadget_ep_queue,
199462306a36Sopenharmony_ci	.dequeue = cdns2_gadget_ep_dequeue,
199562306a36Sopenharmony_ci	.set_halt = cdns2_gadget_ep_set_halt,
199662306a36Sopenharmony_ci	.set_wedge = cdns2_gadget_ep_set_wedge,
199762306a36Sopenharmony_ci};
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_cistatic int cdns2_gadget_get_frame(struct usb_gadget *gadget)
200062306a36Sopenharmony_ci{
200162306a36Sopenharmony_ci	struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	return readw(&pdev->usb_regs->frmnr);
200462306a36Sopenharmony_ci}
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_cistatic int cdns2_gadget_wakeup(struct usb_gadget *gadget)
200762306a36Sopenharmony_ci{
200862306a36Sopenharmony_ci	struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
200962306a36Sopenharmony_ci	unsigned long flags;
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
201262306a36Sopenharmony_ci	cdns2_wakeup(pdev);
201362306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	return 0;
201662306a36Sopenharmony_ci}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_cistatic int cdns2_gadget_set_selfpowered(struct usb_gadget *gadget,
201962306a36Sopenharmony_ci					int is_selfpowered)
202062306a36Sopenharmony_ci{
202162306a36Sopenharmony_ci	struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
202262306a36Sopenharmony_ci	unsigned long flags;
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
202562306a36Sopenharmony_ci	pdev->is_selfpowered = !!is_selfpowered;
202662306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
202762306a36Sopenharmony_ci	return 0;
202862306a36Sopenharmony_ci}
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci/*  Disable interrupts and begin the controller halting process. */
203162306a36Sopenharmony_cistatic void cdns2_quiesce(struct cdns2_device *pdev)
203262306a36Sopenharmony_ci{
203362306a36Sopenharmony_ci	set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	/* Disable interrupt. */
203662306a36Sopenharmony_ci	writeb(0, &pdev->interrupt_regs->extien),
203762306a36Sopenharmony_ci	writeb(0, &pdev->interrupt_regs->usbien),
203862306a36Sopenharmony_ci	writew(0, &pdev->adma_regs->ep_ien);
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	/* Clear interrupt line. */
204162306a36Sopenharmony_ci	writeb(0x0, &pdev->interrupt_regs->usbirq);
204262306a36Sopenharmony_ci}
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_cistatic void cdns2_gadget_config(struct cdns2_device *pdev)
204562306a36Sopenharmony_ci{
204662306a36Sopenharmony_ci	cdns2_ep0_config(pdev);
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	/* Enable DMA interrupts for all endpoints. */
204962306a36Sopenharmony_ci	writel(~0x0, &pdev->adma_regs->ep_ien);
205062306a36Sopenharmony_ci	cdns2_enable_l1(pdev, 0);
205162306a36Sopenharmony_ci	writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien);
205262306a36Sopenharmony_ci	writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien);
205362306a36Sopenharmony_ci	writel(DMA_CONF_DMULT, &pdev->adma_regs->conf);
205462306a36Sopenharmony_ci}
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_cistatic int cdns2_gadget_pullup(struct usb_gadget *gadget, int is_on)
205762306a36Sopenharmony_ci{
205862306a36Sopenharmony_ci	struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
205962306a36Sopenharmony_ci	unsigned long flags;
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	trace_cdns2_pullup(is_on);
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	/*
206462306a36Sopenharmony_ci	 * Disable events handling while controller is being
206562306a36Sopenharmony_ci	 * enabled/disabled.
206662306a36Sopenharmony_ci	 */
206762306a36Sopenharmony_ci	disable_irq(pdev->irq);
206862306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	if (is_on) {
207162306a36Sopenharmony_ci		cdns2_gadget_config(pdev);
207262306a36Sopenharmony_ci		clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
207362306a36Sopenharmony_ci	} else {
207462306a36Sopenharmony_ci		cdns2_quiesce(pdev);
207562306a36Sopenharmony_ci	}
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
207862306a36Sopenharmony_ci	enable_irq(pdev->irq);
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	return 0;
208162306a36Sopenharmony_ci}
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_cistatic int cdns2_gadget_udc_start(struct usb_gadget *gadget,
208462306a36Sopenharmony_ci				  struct usb_gadget_driver *driver)
208562306a36Sopenharmony_ci{
208662306a36Sopenharmony_ci	struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
208762306a36Sopenharmony_ci	enum usb_device_speed max_speed = driver->max_speed;
208862306a36Sopenharmony_ci	unsigned long flags;
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
209162306a36Sopenharmony_ci	pdev->gadget_driver = driver;
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	/* Limit speed if necessary. */
209462306a36Sopenharmony_ci	max_speed = min(driver->max_speed, gadget->max_speed);
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	switch (max_speed) {
209762306a36Sopenharmony_ci	case USB_SPEED_FULL:
209862306a36Sopenharmony_ci		writeb(SPEEDCTRL_HSDISABLE, &pdev->usb_regs->speedctrl);
209962306a36Sopenharmony_ci		break;
210062306a36Sopenharmony_ci	case USB_SPEED_HIGH:
210162306a36Sopenharmony_ci		writeb(0, &pdev->usb_regs->speedctrl);
210262306a36Sopenharmony_ci		break;
210362306a36Sopenharmony_ci	default:
210462306a36Sopenharmony_ci		dev_err(pdev->dev, "invalid maximum_speed parameter %d\n",
210562306a36Sopenharmony_ci			max_speed);
210662306a36Sopenharmony_ci		fallthrough;
210762306a36Sopenharmony_ci	case USB_SPEED_UNKNOWN:
210862306a36Sopenharmony_ci		/* Default to highspeed. */
210962306a36Sopenharmony_ci		max_speed = USB_SPEED_HIGH;
211062306a36Sopenharmony_ci		break;
211162306a36Sopenharmony_ci	}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	/* Reset all USB endpoints. */
211462306a36Sopenharmony_ci	writeb(ENDPRST_IO_TX, &pdev->usb_regs->endprst);
211562306a36Sopenharmony_ci	writeb(ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX,
211662306a36Sopenharmony_ci	       &pdev->usb_regs->endprst);
211762306a36Sopenharmony_ci	writeb(ENDPRST_FIFORST | ENDPRST_TOGRST, &pdev->usb_regs->endprst);
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	cdns2_eps_onchip_buffer_init(pdev);
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	cdns2_gadget_config(pdev);
212262306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	return 0;
212562306a36Sopenharmony_ci}
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_cistatic int cdns2_gadget_udc_stop(struct usb_gadget *gadget)
212862306a36Sopenharmony_ci{
212962306a36Sopenharmony_ci	struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
213062306a36Sopenharmony_ci	struct cdns2_endpoint *pep;
213162306a36Sopenharmony_ci	u32 bEndpointAddress;
213262306a36Sopenharmony_ci	struct usb_ep *ep;
213362306a36Sopenharmony_ci	int val;
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	pdev->gadget_driver = NULL;
213662306a36Sopenharmony_ci	pdev->gadget.speed = USB_SPEED_UNKNOWN;
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci	list_for_each_entry(ep, &pdev->gadget.ep_list, ep_list) {
213962306a36Sopenharmony_ci		pep = ep_to_cdns2_ep(ep);
214062306a36Sopenharmony_ci		bEndpointAddress = pep->num | pep->dir;
214162306a36Sopenharmony_ci		cdns2_select_ep(pdev, bEndpointAddress);
214262306a36Sopenharmony_ci		writel(DMA_EP_CMD_EPRST, &pdev->adma_regs->ep_cmd);
214362306a36Sopenharmony_ci		readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
214462306a36Sopenharmony_ci					  !(val & DMA_EP_CMD_EPRST), 1, 100);
214562306a36Sopenharmony_ci	}
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	cdns2_quiesce(pdev);
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	writeb(ENDPRST_IO_TX, &pdev->usb_regs->endprst);
215062306a36Sopenharmony_ci	writeb(ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX,
215162306a36Sopenharmony_ci	       &pdev->epx_regs->endprst);
215262306a36Sopenharmony_ci	writeb(ENDPRST_FIFORST | ENDPRST_TOGRST, &pdev->epx_regs->endprst);
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	return 0;
215562306a36Sopenharmony_ci}
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_cistatic const struct usb_gadget_ops cdns2_gadget_ops = {
215862306a36Sopenharmony_ci	.get_frame = cdns2_gadget_get_frame,
215962306a36Sopenharmony_ci	.wakeup = cdns2_gadget_wakeup,
216062306a36Sopenharmony_ci	.set_selfpowered = cdns2_gadget_set_selfpowered,
216162306a36Sopenharmony_ci	.pullup = cdns2_gadget_pullup,
216262306a36Sopenharmony_ci	.udc_start = cdns2_gadget_udc_start,
216362306a36Sopenharmony_ci	.udc_stop = cdns2_gadget_udc_stop,
216462306a36Sopenharmony_ci	.match_ep = cdns2_gadget_match_ep,
216562306a36Sopenharmony_ci};
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_cistatic void cdns2_free_all_eps(struct cdns2_device *pdev)
216862306a36Sopenharmony_ci{
216962306a36Sopenharmony_ci	int i;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++)
217262306a36Sopenharmony_ci		cdns2_free_tr_segment(&pdev->eps[i]);
217362306a36Sopenharmony_ci}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci/* Initializes software endpoints of gadget. */
217662306a36Sopenharmony_cistatic int cdns2_init_eps(struct cdns2_device *pdev)
217762306a36Sopenharmony_ci{
217862306a36Sopenharmony_ci	struct cdns2_endpoint *pep;
217962306a36Sopenharmony_ci	int i;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) {
218262306a36Sopenharmony_ci		bool direction = !(i & 1); /* Start from OUT endpoint. */
218362306a36Sopenharmony_ci		u8 epnum = ((i + 1) >> 1);
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci		/*
218662306a36Sopenharmony_ci		 * Endpoints are being held in pdev->eps[] in form:
218762306a36Sopenharmony_ci		 * ep0, ep1out, ep1in ... ep15out, ep15in.
218862306a36Sopenharmony_ci		 */
218962306a36Sopenharmony_ci		if (!CDNS2_IF_EP_EXIST(pdev, epnum, direction))
219062306a36Sopenharmony_ci			continue;
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci		pep = &pdev->eps[i];
219362306a36Sopenharmony_ci		pep->pdev = pdev;
219462306a36Sopenharmony_ci		pep->num = epnum;
219562306a36Sopenharmony_ci		/* 0 for OUT, 1 for IN. */
219662306a36Sopenharmony_ci		pep->dir = direction ? USB_DIR_IN : USB_DIR_OUT;
219762306a36Sopenharmony_ci		pep->idx = i;
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci		/* Ep0in and ep0out are represented by pdev->eps[0]. */
220062306a36Sopenharmony_ci		if (!epnum) {
220162306a36Sopenharmony_ci			int ret;
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci			snprintf(pep->name, sizeof(pep->name), "ep%d%s",
220462306a36Sopenharmony_ci				 epnum, "BiDir");
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci			cdns2_init_ep0(pdev, pep);
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci			ret = cdns2_alloc_tr_segment(pep);
220962306a36Sopenharmony_ci			if (ret) {
221062306a36Sopenharmony_ci				dev_err(pdev->dev, "Failed to init ep0\n");
221162306a36Sopenharmony_ci				return ret;
221262306a36Sopenharmony_ci			}
221362306a36Sopenharmony_ci		} else {
221462306a36Sopenharmony_ci			snprintf(pep->name, sizeof(pep->name), "ep%d%s",
221562306a36Sopenharmony_ci				 epnum, !!direction ? "in" : "out");
221662306a36Sopenharmony_ci			pep->endpoint.name = pep->name;
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci			usb_ep_set_maxpacket_limit(&pep->endpoint, 1024);
221962306a36Sopenharmony_ci			pep->endpoint.ops = &cdns2_gadget_ep_ops;
222062306a36Sopenharmony_ci			list_add_tail(&pep->endpoint.ep_list, &pdev->gadget.ep_list);
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci			pep->endpoint.caps.dir_in = direction;
222362306a36Sopenharmony_ci			pep->endpoint.caps.dir_out = !direction;
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci			pep->endpoint.caps.type_iso = 1;
222662306a36Sopenharmony_ci			pep->endpoint.caps.type_bulk = 1;
222762306a36Sopenharmony_ci			pep->endpoint.caps.type_int = 1;
222862306a36Sopenharmony_ci		}
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci		pep->endpoint.name = pep->name;
223162306a36Sopenharmony_ci		pep->ep_state = 0;
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci		dev_dbg(pdev->dev, "Init %s, SupType: CTRL: %s, INT: %s, "
223462306a36Sopenharmony_ci			"BULK: %s, ISOC %s, SupDir IN: %s, OUT: %s\n",
223562306a36Sopenharmony_ci			pep->name,
223662306a36Sopenharmony_ci			(pep->endpoint.caps.type_control) ? "yes" : "no",
223762306a36Sopenharmony_ci			(pep->endpoint.caps.type_int) ? "yes" : "no",
223862306a36Sopenharmony_ci			(pep->endpoint.caps.type_bulk) ? "yes" : "no",
223962306a36Sopenharmony_ci			(pep->endpoint.caps.type_iso) ? "yes" : "no",
224062306a36Sopenharmony_ci			(pep->endpoint.caps.dir_in) ? "yes" : "no",
224162306a36Sopenharmony_ci			(pep->endpoint.caps.dir_out) ? "yes" : "no");
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci		INIT_LIST_HEAD(&pep->pending_list);
224462306a36Sopenharmony_ci		INIT_LIST_HEAD(&pep->deferred_list);
224562306a36Sopenharmony_ci	}
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	return 0;
224862306a36Sopenharmony_ci}
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_cistatic int cdns2_gadget_start(struct cdns2_device *pdev)
225162306a36Sopenharmony_ci{
225262306a36Sopenharmony_ci	u32 max_speed;
225362306a36Sopenharmony_ci	void *buf;
225462306a36Sopenharmony_ci	int val;
225562306a36Sopenharmony_ci	int ret;
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	pdev->usb_regs = pdev->regs;
225862306a36Sopenharmony_ci	pdev->ep0_regs = pdev->regs;
225962306a36Sopenharmony_ci	pdev->epx_regs = pdev->regs;
226062306a36Sopenharmony_ci	pdev->interrupt_regs = pdev->regs;
226162306a36Sopenharmony_ci	pdev->adma_regs = pdev->regs + CDNS2_ADMA_REGS_OFFSET;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	/* Reset controller. */
226462306a36Sopenharmony_ci	set_reg_bit_8(&pdev->usb_regs->cpuctrl, CPUCTRL_SW_RST);
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	ret = readl_poll_timeout_atomic(&pdev->usb_regs->cpuctrl, val,
226762306a36Sopenharmony_ci					!(val & CPUCTRL_SW_RST), 1, 10000);
226862306a36Sopenharmony_ci	if (ret) {
226962306a36Sopenharmony_ci		dev_err(pdev->dev, "Error: reset controller timeout\n");
227062306a36Sopenharmony_ci		return -EINVAL;
227162306a36Sopenharmony_ci	}
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	usb_initialize_gadget(pdev->dev, &pdev->gadget, NULL);
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci	device_property_read_u16(pdev->dev, "cdns,on-chip-tx-buff-size",
227662306a36Sopenharmony_ci				 &pdev->onchip_tx_buf);
227762306a36Sopenharmony_ci	device_property_read_u16(pdev->dev, "cdns,on-chip-rx-buff-size",
227862306a36Sopenharmony_ci				 &pdev->onchip_rx_buf);
227962306a36Sopenharmony_ci	device_property_read_u32(pdev->dev, "cdns,avail-endpoints",
228062306a36Sopenharmony_ci				 &pdev->eps_supported);
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci	/*
228362306a36Sopenharmony_ci	 * Driver assumes that each USBHS controller has at least
228462306a36Sopenharmony_ci	 * one IN and one OUT non control endpoint.
228562306a36Sopenharmony_ci	 */
228662306a36Sopenharmony_ci	if (!pdev->onchip_tx_buf && !pdev->onchip_rx_buf) {
228762306a36Sopenharmony_ci		ret = -EINVAL;
228862306a36Sopenharmony_ci		dev_err(pdev->dev, "Invalid on-chip memory configuration\n");
228962306a36Sopenharmony_ci		goto put_gadget;
229062306a36Sopenharmony_ci	}
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	if (!(pdev->eps_supported & ~0x00010001)) {
229362306a36Sopenharmony_ci		ret = -EINVAL;
229462306a36Sopenharmony_ci		dev_err(pdev->dev, "No hardware endpoints available\n");
229562306a36Sopenharmony_ci		goto put_gadget;
229662306a36Sopenharmony_ci	}
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	max_speed = usb_get_maximum_speed(pdev->dev);
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	switch (max_speed) {
230162306a36Sopenharmony_ci	case USB_SPEED_FULL:
230262306a36Sopenharmony_ci	case USB_SPEED_HIGH:
230362306a36Sopenharmony_ci		break;
230462306a36Sopenharmony_ci	default:
230562306a36Sopenharmony_ci		dev_err(pdev->dev, "invalid maximum_speed parameter %d\n",
230662306a36Sopenharmony_ci			max_speed);
230762306a36Sopenharmony_ci		fallthrough;
230862306a36Sopenharmony_ci	case USB_SPEED_UNKNOWN:
230962306a36Sopenharmony_ci		max_speed = USB_SPEED_HIGH;
231062306a36Sopenharmony_ci		break;
231162306a36Sopenharmony_ci	}
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	pdev->gadget.max_speed = max_speed;
231462306a36Sopenharmony_ci	pdev->gadget.speed = USB_SPEED_UNKNOWN;
231562306a36Sopenharmony_ci	pdev->gadget.ops = &cdns2_gadget_ops;
231662306a36Sopenharmony_ci	pdev->gadget.name = "usbhs-gadget";
231762306a36Sopenharmony_ci	pdev->gadget.quirk_avoids_skb_reserve = 1;
231862306a36Sopenharmony_ci	pdev->gadget.irq = pdev->irq;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	spin_lock_init(&pdev->lock);
232162306a36Sopenharmony_ci	INIT_WORK(&pdev->pending_status_wq, cdns2_pending_setup_status_handler);
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	/* Initialize endpoint container. */
232462306a36Sopenharmony_ci	INIT_LIST_HEAD(&pdev->gadget.ep_list);
232562306a36Sopenharmony_ci	pdev->eps_dma_pool = dma_pool_create("cdns2_eps_dma_pool", pdev->dev,
232662306a36Sopenharmony_ci					     TR_SEG_SIZE, 8, 0);
232762306a36Sopenharmony_ci	if (!pdev->eps_dma_pool) {
232862306a36Sopenharmony_ci		dev_err(pdev->dev, "Failed to create TRB dma pool\n");
232962306a36Sopenharmony_ci		ret = -ENOMEM;
233062306a36Sopenharmony_ci		goto put_gadget;
233162306a36Sopenharmony_ci	}
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_ci	ret = cdns2_init_eps(pdev);
233462306a36Sopenharmony_ci	if (ret) {
233562306a36Sopenharmony_ci		dev_err(pdev->dev, "Failed to create endpoints\n");
233662306a36Sopenharmony_ci		goto destroy_dma_pool;
233762306a36Sopenharmony_ci	}
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	pdev->gadget.sg_supported = 1;
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	pdev->zlp_buf = kzalloc(CDNS2_EP_ZLP_BUF_SIZE, GFP_KERNEL);
234262306a36Sopenharmony_ci	if (!pdev->zlp_buf) {
234362306a36Sopenharmony_ci		ret = -ENOMEM;
234462306a36Sopenharmony_ci		goto destroy_dma_pool;
234562306a36Sopenharmony_ci	}
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	/* Allocate memory for setup packet buffer. */
234862306a36Sopenharmony_ci	buf = dma_alloc_coherent(pdev->dev, 8, &pdev->ep0_preq.request.dma,
234962306a36Sopenharmony_ci				 GFP_DMA);
235062306a36Sopenharmony_ci	pdev->ep0_preq.request.buf = buf;
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	if (!pdev->ep0_preq.request.buf) {
235362306a36Sopenharmony_ci		ret = -ENOMEM;
235462306a36Sopenharmony_ci		goto free_zlp_buf;
235562306a36Sopenharmony_ci	}
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	/* Add USB gadget device. */
235862306a36Sopenharmony_ci	ret = usb_add_gadget(&pdev->gadget);
235962306a36Sopenharmony_ci	if (ret < 0) {
236062306a36Sopenharmony_ci		dev_err(pdev->dev, "Failed to add gadget\n");
236162306a36Sopenharmony_ci		goto free_ep0_buf;
236262306a36Sopenharmony_ci	}
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	return 0;
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_cifree_ep0_buf:
236762306a36Sopenharmony_ci	dma_free_coherent(pdev->dev, 8, pdev->ep0_preq.request.buf,
236862306a36Sopenharmony_ci			  pdev->ep0_preq.request.dma);
236962306a36Sopenharmony_cifree_zlp_buf:
237062306a36Sopenharmony_ci	kfree(pdev->zlp_buf);
237162306a36Sopenharmony_cidestroy_dma_pool:
237262306a36Sopenharmony_ci	dma_pool_destroy(pdev->eps_dma_pool);
237362306a36Sopenharmony_ciput_gadget:
237462306a36Sopenharmony_ci	usb_put_gadget(&pdev->gadget);
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	return ret;
237762306a36Sopenharmony_ci}
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ciint cdns2_gadget_suspend(struct cdns2_device *pdev)
238062306a36Sopenharmony_ci{
238162306a36Sopenharmony_ci	unsigned long flags;
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	cdns2_disconnect_gadget(pdev);
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
238662306a36Sopenharmony_ci	pdev->gadget.speed = USB_SPEED_UNKNOWN;
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	trace_cdns2_device_state("notattached");
238962306a36Sopenharmony_ci	usb_gadget_set_state(&pdev->gadget, USB_STATE_NOTATTACHED);
239062306a36Sopenharmony_ci	cdns2_enable_l1(pdev, 0);
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	/* Disable interrupt for device. */
239362306a36Sopenharmony_ci	writeb(0, &pdev->interrupt_regs->usbien);
239462306a36Sopenharmony_ci	writel(0, &pdev->adma_regs->ep_ien);
239562306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	return 0;
239862306a36Sopenharmony_ci}
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ciint cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated)
240162306a36Sopenharmony_ci{
240262306a36Sopenharmony_ci	unsigned long flags;
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->lock, flags);
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci	if (!pdev->gadget_driver) {
240762306a36Sopenharmony_ci		spin_unlock_irqrestore(&pdev->lock, flags);
240862306a36Sopenharmony_ci		return 0;
240962306a36Sopenharmony_ci	}
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	cdns2_gadget_config(pdev);
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	if (hibernated)
241462306a36Sopenharmony_ci		clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->lock, flags);
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	return 0;
241962306a36Sopenharmony_ci}
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_civoid cdns2_gadget_remove(struct cdns2_device *pdev)
242262306a36Sopenharmony_ci{
242362306a36Sopenharmony_ci	pm_runtime_mark_last_busy(pdev->dev);
242462306a36Sopenharmony_ci	pm_runtime_put_autosuspend(pdev->dev);
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci	usb_del_gadget(&pdev->gadget);
242762306a36Sopenharmony_ci	cdns2_free_all_eps(pdev);
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_ci	dma_pool_destroy(pdev->eps_dma_pool);
243062306a36Sopenharmony_ci	kfree(pdev->zlp_buf);
243162306a36Sopenharmony_ci	usb_put_gadget(&pdev->gadget);
243262306a36Sopenharmony_ci}
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ciint cdns2_gadget_init(struct cdns2_device *pdev)
243562306a36Sopenharmony_ci{
243662306a36Sopenharmony_ci	int ret;
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci	/* Ensure 32-bit DMA Mask. */
243962306a36Sopenharmony_ci	ret = dma_set_mask_and_coherent(pdev->dev, DMA_BIT_MASK(32));
244062306a36Sopenharmony_ci	if (ret) {
244162306a36Sopenharmony_ci		dev_err(pdev->dev, "Failed to set dma mask: %d\n", ret);
244262306a36Sopenharmony_ci		return ret;
244362306a36Sopenharmony_ci	}
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	pm_runtime_get_sync(pdev->dev);
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	cdsn2_isoc_burst_opt(pdev);
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	ret = cdns2_gadget_start(pdev);
245062306a36Sopenharmony_ci	if (ret) {
245162306a36Sopenharmony_ci		pm_runtime_put_sync(pdev->dev);
245262306a36Sopenharmony_ci		return ret;
245362306a36Sopenharmony_ci	}
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	/*
245662306a36Sopenharmony_ci	 * Because interrupt line can be shared with other components in
245762306a36Sopenharmony_ci	 * driver it can't use IRQF_ONESHOT flag here.
245862306a36Sopenharmony_ci	 */
245962306a36Sopenharmony_ci	ret = devm_request_threaded_irq(pdev->dev, pdev->irq,
246062306a36Sopenharmony_ci					cdns2_usb_irq_handler,
246162306a36Sopenharmony_ci					cdns2_thread_irq_handler,
246262306a36Sopenharmony_ci					IRQF_SHARED,
246362306a36Sopenharmony_ci					dev_name(pdev->dev),
246462306a36Sopenharmony_ci					pdev);
246562306a36Sopenharmony_ci	if (ret)
246662306a36Sopenharmony_ci		goto err0;
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	return 0;
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_cierr0:
247162306a36Sopenharmony_ci	cdns2_gadget_remove(pdev);
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	return ret;
247462306a36Sopenharmony_ci}
2475