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