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