2019-06-19 02:06:40 -06:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Cadence USBSS DRD Driver - gadget side.
|
2017-07-24 19:39:40 -06:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* Copyright (C) 2018-2019 Cadence Design Systems.
|
|
|
|
* Copyright (C) 2017-2019 NXP
|
2017-07-24 19:39:40 -06:00
|
|
|
*
|
|
|
|
* Authors: Pawel Jez <pjez@cadence.com>,
|
2019-06-19 02:06:40 -06:00
|
|
|
* Pawel Laszczak <pawell@cadence.com>
|
2017-07-24 19:39:40 -06:00
|
|
|
* Peter Chen <peter.chen@nxp.com>
|
2019-06-19 02:06:40 -06:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Work around 1:
|
|
|
|
* At some situations, the controller may get stale data address in TRB
|
|
|
|
* at below sequences:
|
|
|
|
* 1. Controller read TRB includes data address
|
|
|
|
* 2. Software updates TRBs includes data address and Cycle bit
|
|
|
|
* 3. Controller read TRB which includes Cycle bit
|
|
|
|
* 4. DMA run with stale data address
|
|
|
|
*
|
|
|
|
* To fix this problem, driver needs to make the first TRB in TD as invalid.
|
|
|
|
* After preparing all TRBs driver needs to check the position of DMA and
|
|
|
|
* if the DMA point to the first just added TRB and doorbell is 1,
|
|
|
|
* then driver must defer making this TRB as valid. This TRB will be make
|
|
|
|
* as valid during adding next TRB only if DMA is stopped or at TRBERR
|
|
|
|
* interrupt.
|
|
|
|
*
|
|
|
|
* Work around 2:
|
|
|
|
* Controller for OUT endpoints has shared on-chip buffers for all incoming
|
|
|
|
* packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
|
|
|
|
* in correct order. If the first packet in the buffer will not be handled,
|
|
|
|
* then the following packets directed for other endpoints and functions
|
|
|
|
* will be blocked.
|
|
|
|
* Additionally the packets directed to one endpoint can block entire on-chip
|
|
|
|
* buffers. In this case transfer to other endpoints also will blocked.
|
|
|
|
*
|
|
|
|
* To resolve this issue after raising the descriptor missing interrupt
|
|
|
|
* driver prepares internal usb_request object and use it to arm DMA transfer.
|
|
|
|
*
|
|
|
|
* The problematic situation was observed in case when endpoint has been enabled
|
|
|
|
* but no usb_request were queued. Driver try detects such endpoints and will
|
|
|
|
* use this workaround only for these endpoint.
|
|
|
|
*
|
|
|
|
* Driver use limited number of buffer. This number can be set by macro
|
|
|
|
* CDNS_WA2_NUM_BUFFERS.
|
2017-07-24 19:39:40 -06:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* Such blocking situation was observed on ACM gadget. For this function
|
|
|
|
* host send OUT data packet but ACM function is not prepared for this packet.
|
|
|
|
* It's cause that buffer placed in on chip memory block transfer to other
|
|
|
|
* endpoints.
|
2017-07-24 19:39:40 -06:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* It's limitation of controller but maybe this issues should be fixed in
|
|
|
|
* function driver.
|
|
|
|
*
|
|
|
|
* This work around can be disabled/enabled by means of quirk_internal_buffer
|
|
|
|
* module parameter. By default feature is enabled. It can has impact to
|
|
|
|
* transfer performance and in most case this feature can be disabled.
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/dma-mapping.h>
|
2019-06-19 02:06:40 -06:00
|
|
|
#include <linux/interrupt.h>
|
2017-07-24 19:39:40 -06:00
|
|
|
#include <linux/usb/gadget.h>
|
2019-06-19 02:06:40 -06:00
|
|
|
#include <linux/pm_runtime.h>
|
|
|
|
#include <linux/module.h>
|
2017-07-24 19:39:40 -06:00
|
|
|
|
|
|
|
#include "core.h"
|
|
|
|
#include "gadget-export.h"
|
|
|
|
#include "gadget.h"
|
2019-06-19 02:06:40 -06:00
|
|
|
#include "trace.h"
|
2017-08-21 03:43:37 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
|
|
|
|
struct usb_request *request,
|
|
|
|
gfp_t gfp_flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parameter allows to disable/enable handling of work around 2 feature.
|
|
|
|
* By default this value is enabled.
|
|
|
|
*/
|
|
|
|
static bool quirk_internal_buffer = 1;
|
|
|
|
module_param(quirk_internal_buffer, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(quirk_internal_buffer, "Disable/enable WA2 algorithm");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* cdns3_set_register_bit - set bit in given register.
|
|
|
|
* @ptr: address of device controller register to be read and changed
|
|
|
|
* @mask: bits requested to set
|
|
|
|
*/
|
|
|
|
void cdns3_set_register_bit(void __iomem *ptr, u32 mask)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
mask = readl(ptr) | mask;
|
|
|
|
writel(mask, ptr);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/**
|
|
|
|
* cdns3_ep_addr_to_index - Macro converts endpoint address to
|
|
|
|
* index of endpoint object in cdns3_device.eps[] container
|
|
|
|
* @ep_addr: endpoint address for which endpoint object is required
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
u8 cdns3_ep_addr_to_index(u8 ep_addr)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_next_request - returns next request from list
|
2017-07-24 19:39:40 -06:00
|
|
|
* @list: list containing requests
|
|
|
|
*
|
2017-12-26 20:22:27 -07:00
|
|
|
* Returns request or NULL if no requests in list
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
struct usb_request *cdns3_next_request(struct list_head *list)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
return list_first_entry_or_null(list, struct usb_request, list);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2018-05-07 02:38:43 -06:00
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_aligned_buf - returns next buffer from list
|
|
|
|
* @list: list containing buffers
|
2018-05-07 02:38:43 -06:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* Returns buffer or NULL if no buffers in list
|
2018-05-07 02:38:43 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_aligned_buf *cdns3_next_align_buf(struct list_head *list)
|
2018-05-07 02:38:43 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
return list_first_entry_or_null(list, struct cdns3_aligned_buf, list);
|
2018-05-07 02:38:43 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/**
|
|
|
|
* cdns3_next_priv_request - returns next request from list
|
|
|
|
* @list: list containing requests
|
|
|
|
*
|
|
|
|
* Returns request or NULL if no requests in list
|
|
|
|
*/
|
|
|
|
struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
|
2018-05-07 02:38:43 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
return list_first_entry_or_null(list, struct cdns3_request, list);
|
2018-05-07 02:38:43 -06:00
|
|
|
}
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
/**
|
|
|
|
* select_ep - selects endpoint
|
2019-06-19 02:06:40 -06:00
|
|
|
* @priv_dev: extended gadget object
|
2017-07-24 19:39:40 -06:00
|
|
|
* @ep: endpoint address
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
if (priv_dev->selected_ep == ep)
|
2017-07-24 19:39:40 -06:00
|
|
|
return;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev->selected_ep = ep;
|
|
|
|
writel(ep, &priv_dev->regs->ep_sel);
|
|
|
|
}
|
|
|
|
|
|
|
|
dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
|
|
|
|
struct cdns3_trb *trb)
|
|
|
|
{
|
|
|
|
u32 offset = (char *)trb - (char *)priv_ep->trb_pool;
|
|
|
|
|
|
|
|
return priv_ep->trb_pool_dma + offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
|
|
|
|
{
|
|
|
|
switch (priv_ep->type) {
|
|
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
|
|
return TRB_ISO_RING_SIZE;
|
|
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
|
|
return TRB_CTRL_RING_SIZE;
|
|
|
|
default:
|
|
|
|
return TRB_RING_SIZE;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint
|
|
|
|
* @priv_ep: endpoint object
|
2017-07-24 19:39:40 -06:00
|
|
|
*
|
|
|
|
* Function will return 0 on success or -ENOMEM on allocation error
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
|
|
|
|
{
|
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
|
|
|
int ring_size = cdns3_ring_size(priv_ep);
|
|
|
|
struct cdns3_trb *link_trb;
|
|
|
|
|
|
|
|
if (!priv_ep->trb_pool) {
|
|
|
|
priv_ep->trb_pool = dma_alloc_coherent(priv_dev->sysdev,
|
|
|
|
ring_size,
|
|
|
|
&priv_ep->trb_pool_dma,
|
|
|
|
GFP_DMA);
|
|
|
|
if (!priv_ep->trb_pool)
|
2018-06-15 00:09:33 -06:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
usb: cdns3: gadget: always zeroed TRB buffer when enable endpoint
[ Upstream commit 95f5acfc4f58f01a22b66d8c9c0ffb72aa96271c ]
During the endpoint dequeue operation, it changes dequeued TRB as link
TRB, when the endpoint is disabled and re-enabled, the DMA fetches the
TRB before the link TRB, after it handles current TRB, the DMA pointer
will advance to the TRB after link TRB, but enqueue and dequene
variables don't know it due to no hardware interrupt at the time, when
the next TRB is added to link TRB position, the DMA will not handle
this TRB due to its pointer is already at the next TRB. See the trace
log like below:
file-storage-675 [001] d..1 86.585657: usb_ep_queue: ep0: req 00000000df9b3a4f length 0/0 sgs 0/0 stream 0 zsI status 0 --> 0
file-storage-675 [001] d..1 86.585663: cdns3_ep_queue: ep1out: req: 000000002ebce364, req buff 00000000f5bc96b4, length: 0/1024 zsi, status: -115, trb: [start:0, end:0: virt addr (null)], flags:0 SID: 0
file-storage-675 [001] d..1 86.585671: cdns3_prepare_trb: ep1out: trb 000000007f770303, dma buf: 0xbd195800, size: 1024, burst: 128 ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal) SID:0 LAST_SID:0
file-storage-675 [001] d..1 86.585676: cdns3_ring:
Ring contents for ep1out:
Ring deq index: 0, trb: 000000007f770303 (virt), 0xc4003000 (dma)
Ring enq index: 1, trb: 0000000049c1ba21 (virt), 0xc400300c (dma)
free trbs: 38, CCS=1, PCS=1
@0x00000000c4003000 bd195800 80020400 00000425
@0x00000000c400300c c4003018 80020400 00001811
@0x00000000c4003018 bcfcc000 0000001f 00000426
@0x00000000c4003024 bcfce800 0000001f 00000426
...
irq/144-5b13000-698 [000] d... 87.619286: usb_gadget_giveback_request: ep1in: req 0000000031b832eb length 13/13 sgs 0/0 stream 0 zsI status 0 --> 0
file-storage-675 [001] d..1 87.619287: cdns3_ep_queue: ep1out: req: 000000002ebce364, req buff 00000000f5bc96b4, length: 0/1024 zsi, status: -115, trb: [start:0, end:0: virt addr 0x80020400c400300c], flags:0 SID: 0
file-storage-675 [001] d..1 87.619294: cdns3_prepare_trb: ep1out: trb 0000000049c1ba21, dma buf: 0xbd198000, size: 1024, burst: 128 ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal) SID:0 LAST_SID:0
file-storage-675 [001] d..1 87.619297: cdns3_ring:
Ring contents for ep1out:
Ring deq index: 1, trb: 0000000049c1ba21 (virt), 0xc400300c (dma)
Ring enq index: 2, trb: 0000000059b34b67 (virt), 0xc4003018 (dma)
free trbs: 38, CCS=1, PCS=1
@0x00000000c4003000 bd195800 0000001f 00000427
@0x00000000c400300c bd198000 80020400 00000425
@0x00000000c4003018 bcfcc000 0000001f 00000426
@0x00000000c4003024 bcfce800 0000001f 00000426
...
file-storage-675 [001] d..1 87.619305: cdns3_doorbell_epx: ep1out, ep_trbaddr c4003018
file-storage-675 [001] .... 87.619308: usb_ep_queue: ep1out: req 000000002ebce364 length 0/1024 sgs 0/0 stream 0 zsI status -115 --> 0
irq/144-5b13000-698 [000] d..1 87.619315: cdns3_epx_irq: IRQ for ep1out: 01000c80 TRBERR , ep_traddr: c4003018 ep_last_sid: 00000000 use_streams: 0
irq/144-5b13000-698 [000] d..1 87.619395: cdns3_usb_irq: IRQ 00000008 = Hot Reset
Fixes: f616c3bda47e ("usb: cdns3: Fix dequeue implementation")
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2020-08-28 12:01:48 -06:00
|
|
|
memset(priv_ep->trb_pool, 0, ring_size);
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (!priv_ep->num)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
priv_ep->num_trbs = ring_size / TRB_SIZE;
|
|
|
|
/* Initialize the last TRB as Link TRB */
|
|
|
|
link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
|
|
|
|
link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
|
|
|
|
link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) |
|
|
|
|
TRB_CHAIN | TRB_TOGGLE;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
|
2018-04-26 20:46:46 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
2018-04-26 20:46:46 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (priv_ep->trb_pool) {
|
|
|
|
dma_free_coherent(priv_dev->sysdev,
|
|
|
|
cdns3_ring_size(priv_ep),
|
|
|
|
priv_ep->trb_pool, priv_ep->trb_pool_dma);
|
|
|
|
priv_ep->trb_pool = NULL;
|
|
|
|
}
|
2018-04-26 20:46:46 -06:00
|
|
|
}
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_ep_stall_flush - Stalls and flushes selected endpoint
|
|
|
|
* @priv_ep: endpoint object
|
2017-07-24 19:39:40 -06:00
|
|
|
*
|
|
|
|
* Endpoint must be selected before call to this function
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
|
|
|
|
|
|
|
cdns3_dbg(priv_ep->cdns3_dev, "Stall & flush endpoint %s\n",
|
|
|
|
priv_ep->name);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
|
|
|
|
&priv_dev->regs->ep_cmd);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
|
|
|
/* wait for DFLUSH cleared */
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 1000);
|
|
|
|
priv_ep->flags |= EP_STALL;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller.
|
|
|
|
* @priv_dev: extended gadget object
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_allow_enable_l1(priv_dev, 0);
|
|
|
|
priv_dev->hw_configured_flag = 0;
|
|
|
|
priv_dev->onchip_mem_allocated_size = 0;
|
|
|
|
priv_dev->out_mem_is_allocated = 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_ep_inc_trb - increment a trb index.
|
|
|
|
* @index: Pointer to the TRB index to increment.
|
|
|
|
* @cs: Cycle state
|
|
|
|
* @trb_in_seg: number of TRBs in segment
|
|
|
|
*
|
|
|
|
* The index should never point to the link TRB. After incrementing,
|
|
|
|
* if it is point to the link TRB, wrap around to the beginning and revert
|
|
|
|
* cycle state bit The
|
|
|
|
* link TRB is always at the last TRB entry.
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static void cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
(*index)++;
|
|
|
|
if (*index == (trb_in_seg - 1)) {
|
|
|
|
*index = 0;
|
|
|
|
*cs ^= 1;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_ep_inc_enq - increment endpoint's enqueue pointer
|
|
|
|
* @priv_ep: The endpoint whose enqueue pointer we're incrementing
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->free_trbs--;
|
|
|
|
cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_ep_inc_deq - increment endpoint's dequeue pointer
|
|
|
|
* @priv_ep: The endpoint whose dequeue pointer we're incrementing
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->free_trbs++;
|
|
|
|
cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
void cdns3_move_deq_to_next_trb(struct cdns3_request *priv_req)
|
|
|
|
{
|
|
|
|
struct cdns3_endpoint *priv_ep = priv_req->priv_ep;
|
|
|
|
int current_trb = priv_req->start_trb;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
while (current_trb != priv_req->end_trb) {
|
|
|
|
cdns3_ep_inc_deq(priv_ep);
|
|
|
|
current_trb = priv_ep->dequeue;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_ep_inc_deq(priv_ep);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_allow_enable_l1 - enable/disable permits to transition to L1.
|
|
|
|
* @priv_dev: Extended gadget object
|
|
|
|
* @enable: Enable/disable permit to transition to L1.
|
2017-12-26 20:22:27 -07:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* If bit USB_CONF_L1EN is set and device receive Extended Token packet,
|
|
|
|
* then controller answer with ACK handshake.
|
|
|
|
* If bit USB_CONF_L1DS is set and device receive Extended Token packet,
|
|
|
|
* then controller answer with NYET handshake.
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
if (enable)
|
|
|
|
writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf);
|
|
|
|
else
|
|
|
|
writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
|
2017-12-26 20:09:11 -07:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
u32 reg;
|
|
|
|
|
|
|
|
reg = readl(&priv_dev->regs->usb_sts);
|
|
|
|
|
|
|
|
if (DEV_SUPERSPEED(reg))
|
|
|
|
return USB_SPEED_SUPER;
|
|
|
|
else if (DEV_HIGHSPEED(reg))
|
|
|
|
return USB_SPEED_HIGH;
|
|
|
|
else if (DEV_FULLSPEED(reg))
|
|
|
|
return USB_SPEED_FULL;
|
|
|
|
else if (DEV_LOWSPEED(reg))
|
|
|
|
return USB_SPEED_LOW;
|
|
|
|
return USB_SPEED_UNKNOWN;
|
2017-12-26 20:09:11 -07:00
|
|
|
}
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_start_all_request - add to ring all request not started
|
|
|
|
* @priv_dev: Extended gadget object
|
|
|
|
* @priv_ep: The endpoint for whom request will be started.
|
2017-07-24 19:39:40 -06:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* Returns return ENOMEM if transfer ring i not enough TRBs to start
|
|
|
|
* all requests.
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_start_all_request(struct cdns3_device *priv_dev,
|
|
|
|
struct cdns3_endpoint *priv_ep)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_request *priv_req;
|
|
|
|
struct usb_request *request;
|
|
|
|
int ret = 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
while (!list_empty(&priv_ep->deferred_req_list)) {
|
|
|
|
request = cdns3_next_request(&priv_ep->deferred_req_list);
|
|
|
|
priv_req = to_cdns3_request(request);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
ret = cdns3_ep_run_transfer(priv_ep, request);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
list_del(&request->list);
|
|
|
|
list_add_tail(&request->list,
|
|
|
|
&priv_ep->pending_req_list);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->flags &= ~EP_RING_FULL;
|
|
|
|
return ret;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2020-01-14 00:15:00 -07:00
|
|
|
static void __cdns3_descmiss_copy_data(struct usb_request *request,
|
|
|
|
struct usb_request *descmiss_req, struct scatterlist *s)
|
|
|
|
{
|
|
|
|
int length = request->actual + descmiss_req->actual;
|
|
|
|
|
|
|
|
if (!s) {
|
|
|
|
if (length <= request->length) {
|
|
|
|
memcpy(&((u8 *)request->buf)[request->actual],
|
|
|
|
descmiss_req->buf,
|
|
|
|
descmiss_req->actual);
|
|
|
|
request->actual = length;
|
|
|
|
} else {
|
|
|
|
/* It should never occures */
|
|
|
|
request->status = -ENOMEM;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (length <= sg_dma_len(s)) {
|
|
|
|
void *p = phys_to_virt(sg_dma_address(s));
|
|
|
|
|
|
|
|
memcpy(&((u8 *)p)[request->actual],
|
|
|
|
descmiss_req->buf,
|
|
|
|
descmiss_req->actual);
|
|
|
|
request->actual = length;
|
|
|
|
} else {
|
|
|
|
request->status = -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_descmiss_copy_data copy data from internal requests to request queued
|
|
|
|
* by class driver.
|
|
|
|
* @priv_ep: extended endpoint object
|
|
|
|
* @request: request object
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static void cdns3_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
|
|
|
|
struct usb_request *request)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct usb_request *descmiss_req;
|
|
|
|
struct cdns3_request *descmiss_priv_req;
|
2020-01-14 00:15:00 -07:00
|
|
|
struct scatterlist *s = NULL;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
while (!list_empty(&priv_ep->descmiss_req_list)) {
|
|
|
|
int chunk_end;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
descmiss_priv_req =
|
|
|
|
cdns3_next_priv_request(&priv_ep->descmiss_req_list);
|
|
|
|
descmiss_req = &descmiss_priv_req->request;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* driver can't touch pending request */
|
|
|
|
if (descmiss_priv_req->flags & REQUEST_PENDING)
|
|
|
|
break;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
|
2020-01-14 00:15:00 -07:00
|
|
|
if (request->num_sgs)
|
|
|
|
s = request->sg;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2020-01-14 00:15:00 -07:00
|
|
|
__cdns3_descmiss_copy_data(request, descmiss_req, s);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
list_del_init(&descmiss_priv_req->list);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
kfree(descmiss_req->buf);
|
|
|
|
cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (!chunk_end)
|
|
|
|
break;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_gadget_giveback - call struct usb_request's ->complete callback
|
|
|
|
* @priv_ep: The endpoint to whom the request belongs to
|
|
|
|
* @priv_req: The request we're giving back
|
|
|
|
* @status: completion code for the request
|
2017-12-26 20:22:27 -07:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* Must be called with controller's lock held and interrupts disabled. This
|
|
|
|
* function will unmap @req and call its ->complete() callback to notify upper
|
|
|
|
* layers that it has completed.
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
|
|
|
|
struct cdns3_request *priv_req,
|
|
|
|
int status)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
|
|
|
struct usb_request *request = &priv_req->request;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
list_del_init(&request->list);
|
2017-12-03 22:49:03 -07:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (request->status == -EINPROGRESS)
|
|
|
|
request->status = status;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
|
|
|
|
priv_ep->dir);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if ((priv_req->flags & REQUEST_UNALIGNED) &&
|
|
|
|
priv_ep->dir == USB_DIR_OUT && !request->status)
|
|
|
|
memcpy(request->buf, priv_req->aligned_buf->buf,
|
|
|
|
request->length);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED);
|
2020-01-06 21:03:57 -07:00
|
|
|
/* All TRBs have finished, clear the flag */
|
|
|
|
priv_req->finished_trb = 0;
|
2019-06-19 02:06:40 -06:00
|
|
|
trace_cdns3_gadget_giveback(priv_req);
|
|
|
|
|
|
|
|
/* WA2: */
|
|
|
|
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
|
|
|
|
priv_req->flags & REQUEST_INTERNAL) {
|
|
|
|
struct usb_request *req;
|
|
|
|
|
|
|
|
req = cdns3_next_request(&priv_ep->deferred_req_list);
|
|
|
|
request = req;
|
|
|
|
priv_ep->descmis_req = NULL;
|
|
|
|
|
|
|
|
if (!req)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cdns3_descmiss_copy_data(priv_ep, req);
|
|
|
|
if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
|
|
|
|
req->length != req->actual) {
|
|
|
|
/* wait for next part of transfer */
|
|
|
|
return;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (req->status == -EINPROGRESS)
|
|
|
|
req->status = 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
list_del_init(&req->list);
|
|
|
|
cdns3_start_all_request(priv_dev, priv_ep);
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* Start all not pending request */
|
|
|
|
if (priv_ep->flags & EP_RING_FULL)
|
|
|
|
cdns3_start_all_request(priv_dev, priv_ep);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (request->complete) {
|
|
|
|
spin_unlock(&priv_dev->lock);
|
|
|
|
usb_gadget_giveback_request(&priv_ep->endpoint,
|
|
|
|
request);
|
|
|
|
spin_lock(&priv_dev->lock);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (request->buf == priv_dev->zlp_buf)
|
|
|
|
cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* Work around for stale data address in TRB*/
|
|
|
|
if (priv_ep->wa1_set) {
|
|
|
|
cdns3_dbg(priv_dev, "WA1: update cycle bit\n");
|
|
|
|
priv_ep->wa1_set = 0;
|
|
|
|
priv_ep->wa1_trb_index = 0xFFFF;
|
|
|
|
if (priv_ep->wa1_cycle_bit) {
|
|
|
|
priv_ep->wa1_trb->control =
|
|
|
|
priv_ep->wa1_trb->control | 0x1;
|
|
|
|
} else {
|
|
|
|
priv_ep->wa1_trb->control =
|
|
|
|
priv_ep->wa1_trb->control & ~0x1;
|
|
|
|
}
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req)
|
2018-05-03 20:21:02 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_endpoint *priv_ep = priv_req->priv_ep;
|
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
|
|
|
struct cdns3_aligned_buf *buf;
|
2018-05-03 20:21:02 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* check if buffer is aligned to 8. */
|
|
|
|
if (!((uintptr_t)priv_req->request.buf & 0x7))
|
|
|
|
return 0;
|
2018-12-18 23:24:03 -07:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
buf = priv_req->aligned_buf;
|
2018-12-18 23:24:03 -07:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (!buf || priv_req->request.length > buf->size) {
|
|
|
|
buf = kzalloc(sizeof(*buf), GFP_ATOMIC);
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
buf->size = priv_req->request.length;
|
|
|
|
|
|
|
|
buf->buf = dma_alloc_coherent(priv_dev->sysdev,
|
|
|
|
buf->size,
|
|
|
|
&buf->dma,
|
|
|
|
GFP_ATOMIC);
|
|
|
|
if (!buf->buf) {
|
|
|
|
kfree(buf);
|
|
|
|
return -ENOMEM;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
2019-06-19 02:06:40 -06:00
|
|
|
|
|
|
|
if (priv_req->aligned_buf) {
|
|
|
|
trace_cdns3_free_aligned_request(priv_req);
|
|
|
|
priv_req->aligned_buf->in_use = 0;
|
|
|
|
priv_dev->run_garbage_colector = 1;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
buf->in_use = 1;
|
|
|
|
priv_req->aligned_buf = buf;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
list_add_tail(&buf->list,
|
|
|
|
&priv_dev->aligned_buf_list);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (priv_ep->dir == USB_DIR_IN) {
|
|
|
|
memcpy(buf->buf, priv_req->request.buf,
|
|
|
|
priv_req->request.length);
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_req->flags |= REQUEST_UNALIGNED;
|
|
|
|
trace_cdns3_prepare_aligned_request(priv_req);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
return 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
|
|
|
|
* @priv_ep: endpoint object
|
2017-12-26 20:22:27 -07:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* Returns zero on success or negative value on failure
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
|
|
|
|
struct usb_request *request)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
|
|
|
struct cdns3_request *priv_req;
|
2020-03-16 03:30:28 -06:00
|
|
|
struct cdns3_trb *trb, *link_trb = NULL;
|
2019-06-19 02:06:40 -06:00
|
|
|
dma_addr_t trb_dma;
|
|
|
|
int prev_enqueue;
|
|
|
|
u32 togle_pcs = 1;
|
|
|
|
int sg_iter = 0;
|
|
|
|
int dma_index;
|
|
|
|
u32 doorbell;
|
|
|
|
int num_trb;
|
|
|
|
int address;
|
|
|
|
u32 control;
|
|
|
|
int pcs;
|
2020-01-01 18:30:56 -07:00
|
|
|
struct scatterlist *s = NULL;
|
2020-05-26 01:01:55 -06:00
|
|
|
u16 td_size;
|
2019-06-19 02:06:40 -06:00
|
|
|
|
|
|
|
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
|
|
|
|
num_trb = priv_ep->interval;
|
|
|
|
else
|
2020-01-01 18:30:56 -07:00
|
|
|
num_trb = request->num_mapped_sgs ? request->num_mapped_sgs : 1;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (num_trb > priv_ep->free_trbs) {
|
|
|
|
priv_ep->flags |= EP_RING_FULL;
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_req = to_cdns3_request(request);
|
|
|
|
address = priv_ep->endpoint.desc->bEndpointAddress;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->flags |= EP_PENDING_REQUEST;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* must allocate buffer aligned to 8 */
|
|
|
|
if (priv_req->flags & REQUEST_UNALIGNED)
|
|
|
|
trb_dma = priv_req->aligned_buf->dma;
|
|
|
|
else
|
|
|
|
trb_dma = request->dma;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
trb = priv_ep->trb_pool + priv_ep->enqueue;
|
|
|
|
priv_req->start_trb = priv_ep->enqueue;
|
|
|
|
priv_req->trb = trb;
|
|
|
|
prev_enqueue = priv_ep->enqueue;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* prepare ring */
|
|
|
|
if ((priv_ep->enqueue + num_trb) >= (priv_ep->num_trbs - 1)) {
|
|
|
|
/*updating C bt in Link TRB before starting DMA*/
|
2020-03-16 03:30:28 -06:00
|
|
|
link_trb = priv_ep->trb_pool + (priv_ep->num_trbs - 1);
|
2019-06-19 02:06:40 -06:00
|
|
|
link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
|
|
|
|
TRB_TYPE(TRB_LINK) | TRB_CHAIN |
|
|
|
|
TRB_TOGGLE;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* arm transfer on selected endpoint */
|
|
|
|
cdns3_select_ep(priv_ep->cdns3_dev, address);
|
|
|
|
|
|
|
|
doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
|
|
|
|
|
|
|
|
if (!priv_ep->wa1_set) {
|
|
|
|
if (doorbell) {
|
|
|
|
priv_ep->wa1_cycle_bit = priv_ep->pcs ? TRB_CYCLE : 0;
|
|
|
|
priv_ep->wa1_set = 1;
|
|
|
|
priv_ep->wa1_trb = trb;
|
|
|
|
priv_ep->wa1_trb_index = priv_ep->enqueue;
|
|
|
|
togle_pcs = 0;
|
|
|
|
cdns3_dbg(priv_dev, "WA1 set guard\n");
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* set incorrect Cycle Bit for first trb*/
|
|
|
|
control = priv_ep->pcs ? 0 : TRB_CYCLE;
|
2020-03-19 18:34:53 -06:00
|
|
|
trb->length = 0;
|
2020-01-01 18:30:56 -07:00
|
|
|
if (request->num_mapped_sgs)
|
|
|
|
s = request->sg;
|
|
|
|
|
2020-03-16 03:20:11 -06:00
|
|
|
if (priv_dev->dev_ver == DEV_VER_V2) {
|
|
|
|
td_size = DIV_ROUND_UP(request->length,
|
|
|
|
priv_ep->endpoint.maxpacket);
|
|
|
|
|
|
|
|
if (priv_dev->gadget.speed == USB_SPEED_SUPER)
|
|
|
|
trb->length = TRB_TDL_SS_SIZE(td_size);
|
|
|
|
else
|
|
|
|
control |= TRB_TDL_HS_SIZE(td_size);
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
do {
|
2019-09-26 03:11:18 -06:00
|
|
|
u32 length;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* fill TRB */
|
|
|
|
control |= TRB_TYPE(TRB_NORMAL);
|
|
|
|
trb->buffer = TRB_BUFFER(request->num_sgs == 0
|
2020-01-01 18:30:56 -07:00
|
|
|
? trb_dma : sg_dma_address(s));
|
|
|
|
if (!request->num_sgs)
|
2019-09-26 03:11:18 -06:00
|
|
|
length = request->length;
|
|
|
|
else
|
2020-01-01 18:30:56 -07:00
|
|
|
length = sg_dma_len(s);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2020-03-16 03:20:11 -06:00
|
|
|
trb->length |= TRB_BURST_LEN(16) | TRB_LEN(length);
|
2019-06-19 02:06:40 -06:00
|
|
|
pcs = priv_ep->pcs ? TRB_CYCLE : 0;
|
2018-05-14 03:02:35 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/*
|
|
|
|
* first trb should be prepared as last to avoid processing
|
|
|
|
* transfer to early
|
|
|
|
*/
|
|
|
|
if (sg_iter != 0)
|
|
|
|
control |= pcs;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) {
|
|
|
|
control |= TRB_IOC | TRB_ISP;
|
|
|
|
} else {
|
|
|
|
/* for last element in TD or in SG list */
|
|
|
|
if (sg_iter == (num_trb - 1) && sg_iter != 0)
|
|
|
|
control |= pcs | TRB_IOC | TRB_ISP;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-11-28 19:40:50 -07:00
|
|
|
trb->control = control;
|
|
|
|
control = 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2020-03-16 03:27:24 -06:00
|
|
|
if (request->num_mapped_sgs) {
|
|
|
|
trb->control |= TRB_ISP;
|
|
|
|
/* Don't set chain bit for last TRB */
|
|
|
|
if (sg_iter < num_trb - 1)
|
|
|
|
trb->control |= TRB_CHAIN;
|
|
|
|
|
2020-01-01 18:30:56 -07:00
|
|
|
s = sg_next(s);
|
2020-03-16 03:27:24 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_req->end_trb = priv_ep->enqueue;
|
|
|
|
cdns3_ep_inc_enq(priv_ep);
|
|
|
|
trb = priv_ep->trb_pool + priv_ep->enqueue;
|
2020-03-16 03:20:11 -06:00
|
|
|
trb->length = 0;
|
2020-03-16 03:27:24 -06:00
|
|
|
} while (++sg_iter < num_trb);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
trb = priv_req->trb;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_req->flags |= REQUEST_PENDING;
|
2020-01-06 21:03:57 -07:00
|
|
|
priv_req->num_of_trb = num_trb;
|
2018-06-21 04:35:46 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* give the TD to the consumer*/
|
|
|
|
if (sg_iter == 1)
|
2019-11-28 19:40:50 -07:00
|
|
|
trb->control |= TRB_IOC | TRB_ISP;
|
2019-06-19 02:06:40 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Memory barrier - cycle bit must be set before other filds in trb.
|
|
|
|
*/
|
|
|
|
wmb();
|
|
|
|
|
|
|
|
if (togle_pcs)
|
2019-11-28 19:40:50 -07:00
|
|
|
trb->control = trb->control ^ 1;
|
2019-06-19 02:06:40 -06:00
|
|
|
|
|
|
|
doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
|
|
|
|
dma_index = (readl(&priv_dev->regs->ep_traddr) -
|
|
|
|
priv_ep->trb_pool_dma) / TRB_SIZE;
|
|
|
|
|
2020-01-01 18:30:56 -07:00
|
|
|
cdns3_dbg(priv_dev, "dorbel %d, dma_index %d, prev_enqueu %d, num_trb %d",
|
|
|
|
doorbell, dma_index, prev_enqueue, num_trb);
|
2019-06-19 02:06:40 -06:00
|
|
|
|
|
|
|
if (!doorbell || dma_index != priv_ep->wa1_trb_index)
|
|
|
|
cdns3_wa1_restore_cycle_bit(priv_ep);
|
|
|
|
|
2020-03-16 03:30:28 -06:00
|
|
|
if (num_trb > 1) {
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while (i < num_trb) {
|
|
|
|
trace_cdns3_prepare_trb(priv_ep, trb + i);
|
|
|
|
if (trb + i == link_trb) {
|
|
|
|
trb = priv_ep->trb_pool;
|
|
|
|
num_trb = num_trb - i;
|
|
|
|
i = 0;
|
|
|
|
} else {
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/*
|
|
|
|
* Memory barrier - Cycle Bit must be set before trb->length and
|
|
|
|
* trb->buffer fields.
|
|
|
|
*/
|
|
|
|
wmb();
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/*
|
|
|
|
* For DMULT mode we can set address to transfer ring only once after
|
|
|
|
* enabling endpoint.
|
|
|
|
*/
|
|
|
|
if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
|
|
|
|
writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma +
|
|
|
|
priv_req->start_trb * TRB_SIZE),
|
|
|
|
&priv_dev->regs->ep_traddr);
|
|
|
|
|
|
|
|
cdns3_dbg(priv_ep->cdns3_dev, "Update ep_trbaddr for %s to %08x\n",
|
|
|
|
priv_ep->name, readl(&priv_dev->regs->ep_traddr));
|
|
|
|
|
|
|
|
priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (!priv_ep->wa1_set && !(priv_ep->flags & EP_STALL)) {
|
|
|
|
trace_cdns3_ring(priv_ep);
|
|
|
|
/*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
|
|
|
|
writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
|
2020-05-23 19:41:01 -06:00
|
|
|
__cdns3_gadget_wakeup(priv_dev);
|
2019-06-19 02:06:40 -06:00
|
|
|
writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
|
|
|
|
trace_cdns3_doorbell_epx(priv_ep->name,
|
|
|
|
readl(&priv_dev->regs->ep_traddr));
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_endpoint *priv_ep;
|
|
|
|
struct usb_ep *ep;
|
|
|
|
int result = 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (priv_dev->hw_configured_flag)
|
|
|
|
return;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_set_register_bit(&priv_dev->regs->usb_conf,
|
|
|
|
USB_CONF_U1EN | USB_CONF_U2EN);
|
2018-06-25 03:41:30 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* wait until configuration set */
|
|
|
|
result = cdns3_handshake(&priv_dev->regs->usb_sts,
|
|
|
|
USB_STS_CFGSTS_MASK, 1, 100);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev->hw_configured_flag = 1;
|
2020-05-23 19:41:01 -06:00
|
|
|
cdns3_allow_enable_l1(priv_dev, 1);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
|
|
|
|
if (ep->enabled) {
|
|
|
|
priv_ep = ep_to_cdns3_ep(ep);
|
|
|
|
cdns3_start_all_request(priv_dev, priv_ep);
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
2019-06-19 02:06:40 -06:00
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/**
|
|
|
|
* cdns3_request_handled - check whether request has been handled by DMA
|
|
|
|
*
|
|
|
|
* @priv_ep: extended endpoint object.
|
|
|
|
* @priv_req: request object for checking
|
|
|
|
*
|
|
|
|
* Endpoint must be selected before invoking this function.
|
|
|
|
*
|
|
|
|
* Returns false if request has not been handled by DMA, else returns true.
|
|
|
|
*
|
|
|
|
* SR - start ring
|
|
|
|
* ER - end ring
|
|
|
|
* DQ = priv_ep->dequeue - dequeue position
|
|
|
|
* EQ = priv_ep->enqueue - enqueue position
|
|
|
|
* ST = priv_req->start_trb - index of first TRB in transfer ring
|
|
|
|
* ET = priv_req->end_trb - index of last TRB in transfer ring
|
|
|
|
* CI = current_index - index of processed TRB by DMA.
|
|
|
|
*
|
|
|
|
* As first step, function checks if cycle bit for priv_req->start_trb is
|
|
|
|
* correct.
|
|
|
|
*
|
|
|
|
* some rules:
|
|
|
|
* 1. priv_ep->dequeue never exceed current_index.
|
|
|
|
* 2 priv_ep->enqueue never exceed priv_ep->dequeue
|
|
|
|
*
|
|
|
|
* Then We can split recognition into two parts:
|
|
|
|
* Case 1 - priv_ep->dequeue < current_index
|
|
|
|
* SR ... EQ ... DQ ... CI ... ER
|
|
|
|
* SR ... DQ ... CI ... EQ ... ER
|
|
|
|
*
|
|
|
|
* Request has been handled by DMA if ST and ET is between DQ and CI.
|
|
|
|
*
|
|
|
|
* Case 2 - priv_ep->dequeue > current_index
|
|
|
|
* This situation take place when CI go through the LINK TRB at the end of
|
|
|
|
* transfer ring.
|
|
|
|
* SR ... CI ... EQ ... DQ ... ER
|
|
|
|
*
|
|
|
|
* Request has been handled by DMA if ET is less then CI or
|
|
|
|
* ET is greater or equal DQ.
|
|
|
|
*/
|
|
|
|
static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
|
|
|
|
struct cdns3_request *priv_req)
|
|
|
|
{
|
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
2019-12-13 01:52:06 -07:00
|
|
|
struct cdns3_trb *trb;
|
2019-06-19 02:06:40 -06:00
|
|
|
int current_index = 0;
|
|
|
|
int handled = 0;
|
|
|
|
|
|
|
|
current_index = (readl(&priv_dev->regs->ep_traddr) -
|
|
|
|
priv_ep->trb_pool_dma) / TRB_SIZE;
|
|
|
|
|
2019-12-13 01:52:06 -07:00
|
|
|
/* current trb doesn't belong to this request */
|
2020-01-06 21:15:01 -07:00
|
|
|
if (priv_req->start_trb < priv_req->end_trb) {
|
|
|
|
if (priv_ep->dequeue > priv_req->end_trb)
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
if (priv_ep->dequeue < priv_req->start_trb)
|
|
|
|
goto finish;
|
|
|
|
}
|
2019-12-13 01:52:06 -07:00
|
|
|
|
|
|
|
if ((priv_req->start_trb > priv_req->end_trb) &&
|
|
|
|
(priv_ep->dequeue > priv_req->end_trb) &&
|
|
|
|
(priv_ep->dequeue < priv_req->start_trb))
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
if ((priv_req->start_trb == priv_req->end_trb) &&
|
|
|
|
(priv_ep->dequeue != priv_req->end_trb))
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
trb = &priv_ep->trb_pool[priv_ep->dequeue];
|
2019-06-19 02:06:40 -06:00
|
|
|
|
|
|
|
if ((trb->control & TRB_CYCLE) != priv_ep->ccs)
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
if (priv_ep->dequeue < current_index) {
|
|
|
|
if ((current_index == (priv_ep->num_trbs - 1)) &&
|
|
|
|
!priv_ep->dequeue)
|
|
|
|
goto finish;
|
|
|
|
|
2019-12-13 01:52:06 -07:00
|
|
|
handled = 1;
|
2019-06-19 02:06:40 -06:00
|
|
|
} else if (priv_ep->dequeue > current_index) {
|
|
|
|
handled = 1;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
finish:
|
|
|
|
trace_cdns3_request_handled(priv_req, current_index, handled);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
return handled;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
|
2020-04-20 19:42:25 -06:00
|
|
|
struct cdns3_endpoint *priv_ep)
|
2019-06-19 02:06:40 -06:00
|
|
|
{
|
|
|
|
struct cdns3_request *priv_req;
|
|
|
|
struct usb_request *request;
|
|
|
|
struct cdns3_trb *trb;
|
2020-01-06 21:03:57 -07:00
|
|
|
bool request_handled = false;
|
2020-03-16 03:47:01 -06:00
|
|
|
bool transfer_end = false;
|
2019-06-19 02:06:40 -06:00
|
|
|
|
|
|
|
while (!list_empty(&priv_ep->pending_req_list)) {
|
|
|
|
request = cdns3_next_request(&priv_ep->pending_req_list);
|
|
|
|
priv_req = to_cdns3_request(request);
|
|
|
|
|
2019-10-13 03:20:20 -06:00
|
|
|
trb = priv_ep->trb_pool + priv_ep->dequeue;
|
|
|
|
|
|
|
|
/* Request was dequeued and TRB was changed to TRB_LINK. */
|
|
|
|
if (TRB_FIELD_TO_TYPE(trb->control) == TRB_LINK) {
|
|
|
|
trace_cdns3_complete_trb(priv_ep, trb);
|
|
|
|
cdns3_move_deq_to_next_trb(priv_req);
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* Re-select endpoint. It could be changed by other CPU during
|
|
|
|
* handling usb_gadget_giveback_request.
|
|
|
|
*/
|
|
|
|
cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
|
|
|
|
|
2019-12-13 01:52:06 -07:00
|
|
|
while (cdns3_request_handled(priv_ep, priv_req)) {
|
2020-01-06 21:03:57 -07:00
|
|
|
priv_req->finished_trb++;
|
|
|
|
if (priv_req->finished_trb >= priv_req->num_of_trb)
|
|
|
|
request_handled = true;
|
2019-12-13 01:52:06 -07:00
|
|
|
trb = priv_ep->trb_pool + priv_ep->dequeue;
|
|
|
|
trace_cdns3_complete_trb(priv_ep, trb);
|
2019-06-19 02:06:40 -06:00
|
|
|
|
2020-03-16 03:47:01 -06:00
|
|
|
if (!transfer_end)
|
|
|
|
request->actual +=
|
|
|
|
TRB_LEN(le32_to_cpu(trb->length));
|
|
|
|
|
2020-04-20 19:42:25 -06:00
|
|
|
if (priv_req->num_of_trb > 1 &&
|
2020-03-16 03:47:01 -06:00
|
|
|
le32_to_cpu(trb->control) & TRB_SMM)
|
|
|
|
transfer_end = true;
|
|
|
|
|
2019-12-13 01:52:06 -07:00
|
|
|
cdns3_ep_inc_deq(priv_ep);
|
|
|
|
}
|
2019-06-19 02:06:40 -06:00
|
|
|
|
2020-01-06 21:03:57 -07:00
|
|
|
if (request_handled) {
|
2019-12-13 01:52:06 -07:00
|
|
|
cdns3_gadget_giveback(priv_ep, priv_req, 0);
|
2020-01-06 21:03:57 -07:00
|
|
|
request_handled = false;
|
2020-04-19 21:39:23 -06:00
|
|
|
transfer_end = false;
|
2019-12-13 01:52:06 -07:00
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
2019-12-13 01:52:06 -07:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->flags &= ~EP_PENDING_REQUEST;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm)
|
|
|
|
{
|
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_wa1_restore_cycle_bit(priv_ep);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (rearm) {
|
|
|
|
trace_cdns3_ring(priv_ep);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2020-05-23 19:41:01 -06:00
|
|
|
__cdns3_gadget_wakeup(priv_dev);
|
2019-06-19 02:06:40 -06:00
|
|
|
/* Cycle Bit must be updated before arming DMA. */
|
|
|
|
wmb();
|
|
|
|
writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
|
|
|
|
|
|
|
|
trace_cdns3_doorbell_epx(priv_ep->name,
|
|
|
|
readl(&priv_dev->regs->ep_traddr));
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_descmissing_packet - handles descriptor missing event.
|
|
|
|
* @priv_dev: extended gadget object
|
|
|
|
*
|
|
|
|
* This function is used only for WA2. For more information see Work around 2
|
|
|
|
* description.
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_descmissing_packet(struct cdns3_endpoint *priv_ep)
|
|
|
|
{
|
|
|
|
struct cdns3_request *priv_req;
|
|
|
|
struct usb_request *request;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
|
|
|
|
priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
|
|
|
|
priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
|
|
|
|
}
|
|
|
|
|
|
|
|
cdns3_dbg(priv_ep->cdns3_dev, "WA2: Description Missing detected\n");
|
|
|
|
|
|
|
|
request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
|
|
|
|
GFP_ATOMIC);
|
|
|
|
if (!request)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
priv_req = to_cdns3_request(request);
|
|
|
|
priv_req->flags |= REQUEST_INTERNAL;
|
|
|
|
|
|
|
|
/* if this field is still assigned it indicate that transfer related
|
|
|
|
* with this request has not been finished yet. Driver in this
|
|
|
|
* case simply allocate next request and assign flag REQUEST_INTERNAL_CH
|
|
|
|
* flag to previous one. It will indicate that current request is
|
|
|
|
* part of the previous one.
|
|
|
|
*/
|
|
|
|
if (priv_ep->descmis_req)
|
|
|
|
priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
|
|
|
|
|
|
|
|
priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
|
|
|
|
GFP_ATOMIC);
|
|
|
|
if (!priv_req->request.buf) {
|
|
|
|
cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
|
|
|
|
return -ENOMEM;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
|
|
|
|
priv_ep->descmis_req = priv_req;
|
|
|
|
|
|
|
|
__cdns3_gadget_ep_queue(&priv_ep->endpoint,
|
|
|
|
&priv_ep->descmis_req->request,
|
|
|
|
GFP_ATOMIC);
|
|
|
|
|
|
|
|
return 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
|
|
|
|
* @priv_ep: endpoint object
|
2017-07-24 19:39:40 -06:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* Returns 0
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
|
|
|
u32 ep_sts_reg;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
trace_cdns3_epx_irq(priv_dev, priv_ep);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
ep_sts_reg = readl(&priv_dev->regs->ep_sts);
|
|
|
|
writel(ep_sts_reg, &priv_dev->regs->ep_sts);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (ep_sts_reg & EP_STS_TRBERR) {
|
|
|
|
/*
|
|
|
|
* For isochronous transfer driver completes request on
|
|
|
|
* IOC or on TRBERR. IOC appears only when device receive
|
|
|
|
* OUT data packet. If host disable stream or lost some packet
|
|
|
|
* then the only way to finish all queued transfer is to do it
|
|
|
|
* on TRBERR event.
|
|
|
|
*/
|
|
|
|
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC &&
|
|
|
|
!priv_ep->wa1_set)
|
2020-04-20 19:42:25 -06:00
|
|
|
cdns3_transfer_completed(priv_dev, priv_ep);
|
2019-06-19 02:06:40 -06:00
|
|
|
else
|
|
|
|
cdns3_rearm_transfer(priv_ep, priv_ep->wa1_set);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
|
2020-03-16 03:47:01 -06:00
|
|
|
bool is_short = !!(ep_sts_reg & EP_STS_ISP);
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
|
2020-03-16 03:47:01 -06:00
|
|
|
if (is_short)
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->flags |= EP_QUIRK_END_TRANSFER;
|
|
|
|
else
|
|
|
|
priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
|
|
|
|
}
|
|
|
|
|
2020-04-20 19:42:25 -06:00
|
|
|
cdns3_transfer_completed(priv_dev, priv_ep);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/*
|
|
|
|
* WA2: this condition should only be meet when
|
|
|
|
* priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
|
|
|
|
* priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
|
|
|
|
* In other cases this interrupt will be disabled/
|
|
|
|
*/
|
|
|
|
if (ep_sts_reg & EP_STS_DESCMIS) {
|
|
|
|
int err;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
err = cdns3_descmissing_packet(priv_ep);
|
|
|
|
if (err)
|
|
|
|
dev_err(priv_dev->dev,
|
|
|
|
"Failed: No sufficient memory for DESCMIS\n");
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
return 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
|
|
|
|
* @priv_dev: extended gadget object
|
|
|
|
* @usb_ists: bitmap representation of device's reported interrupts
|
|
|
|
* (usb_ists register value)
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
|
|
|
|
u32 usb_ists)
|
|
|
|
{
|
|
|
|
int speed = 0;
|
|
|
|
|
|
|
|
trace_cdns3_usb_irq(priv_dev, usb_ists);
|
2020-05-23 19:41:01 -06:00
|
|
|
if (usb_ists & USB_ISTS_L1ENTI) {
|
|
|
|
/*
|
|
|
|
* WORKAROUND: CDNS3 controller has issue with hardware resuming
|
|
|
|
* from L1. To fix it, if any DMA transfer is pending driver
|
|
|
|
* must starts driving resume signal immediately.
|
|
|
|
*/
|
|
|
|
if (readl(&priv_dev->regs->drbl))
|
|
|
|
__cdns3_gadget_wakeup(priv_dev);
|
|
|
|
}
|
2019-06-19 02:06:40 -06:00
|
|
|
/* Connection detected */
|
|
|
|
if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
|
|
|
|
speed = cdns3_get_speed(priv_dev);
|
|
|
|
priv_dev->gadget.speed = speed;
|
|
|
|
usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED);
|
|
|
|
cdns3_ep0_config(priv_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disconnection detected */
|
|
|
|
if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
|
|
|
|
if (priv_dev->gadget_driver &&
|
2019-08-06 19:47:06 -06:00
|
|
|
priv_dev->gadget_driver->disconnect &&
|
|
|
|
priv_dev->gadget.state ==
|
|
|
|
USB_STATE_CONFIGURED) {
|
2019-06-19 02:06:40 -06:00
|
|
|
spin_unlock(&priv_dev->lock);
|
|
|
|
priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
|
|
|
|
spin_lock(&priv_dev->lock);
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
|
|
|
|
usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
|
|
|
|
cdns3_hw_reset_eps_config(priv_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reset*/
|
|
|
|
if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
|
|
|
|
if (priv_dev->gadget_driver &&
|
2019-07-31 21:23:24 -06:00
|
|
|
priv_dev->gadget_driver->reset &&
|
|
|
|
priv_dev->gadget.state ==
|
|
|
|
USB_STATE_CONFIGURED) {
|
2019-06-19 02:06:40 -06:00
|
|
|
spin_unlock(&priv_dev->lock);
|
|
|
|
priv_dev->gadget_driver->reset(&priv_dev->gadget);
|
|
|
|
spin_lock(&priv_dev->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*read again to check the actual speed*/
|
|
|
|
speed = cdns3_get_speed(priv_dev);
|
|
|
|
usb_gadget_set_state(&priv_dev->gadget, USB_STATE_DEFAULT);
|
|
|
|
priv_dev->gadget.speed = speed;
|
|
|
|
cdns3_hw_reset_eps_config(priv_dev);
|
|
|
|
cdns3_ep0_config(priv_dev);
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_device_irq_handler- interrupt handler for device part of controller
|
2017-07-24 19:39:40 -06:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* @cdns: structure of cdns3
|
|
|
|
*
|
|
|
|
* Returns IRQ_HANDLED or IRQ_NONE
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static irqreturn_t cdns3_device_irq_handler(struct cdns3 *cdns)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev;
|
|
|
|
irqreturn_t ret = IRQ_NONE;
|
|
|
|
unsigned long flags;
|
|
|
|
u32 reg;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev = cdns->gadget_dev;
|
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
2018-06-25 03:41:30 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* check USB device interrupt */
|
|
|
|
reg = readl(&priv_dev->regs->usb_ists);
|
|
|
|
if (reg) {
|
2019-11-06 22:49:51 -07:00
|
|
|
/* After masking interrupts the new interrupts won't be
|
|
|
|
* reported in usb_ists/ep_ists. In order to not lose some
|
|
|
|
* of them driver disables only detected interrupts.
|
|
|
|
* They will be enabled ASAP after clearing source of
|
|
|
|
* interrupt. This an unusual behavior only applies to
|
|
|
|
* usb_ists register.
|
|
|
|
*/
|
|
|
|
reg = ~reg & readl(&priv_dev->regs->usb_ien);
|
|
|
|
/* mask deferred interrupt. */
|
|
|
|
writel(reg, &priv_dev->regs->usb_ien);
|
|
|
|
ret = IRQ_WAKE_THREAD;
|
2018-06-25 03:41:30 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* check endpoint interrupt */
|
|
|
|
reg = readl(&priv_dev->regs->ep_ists);
|
2018-06-25 03:41:30 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (reg) {
|
|
|
|
priv_dev->shadow_ep_en |= reg;
|
|
|
|
reg = ~reg & readl(&priv_dev->regs->ep_ien);
|
|
|
|
/* mask deferred interrupt. */
|
|
|
|
writel(reg, &priv_dev->regs->ep_ien);
|
|
|
|
ret = IRQ_WAKE_THREAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
|
|
|
return ret;
|
2018-06-25 03:41:30 -06:00
|
|
|
}
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_device_thread_irq_handler- interrupt handler for device part
|
|
|
|
* of controller
|
2017-07-24 19:39:40 -06:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* @irq: irq number for cdns3 core device
|
|
|
|
* @data: structure of cdns3
|
|
|
|
*
|
|
|
|
* Returns IRQ_HANDLED or IRQ_NONE
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static irqreturn_t cdns3_device_thread_irq_handler(struct cdns3 *cdns)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev;
|
|
|
|
irqreturn_t ret = IRQ_NONE;
|
2017-07-24 19:39:40 -06:00
|
|
|
unsigned long flags;
|
2019-06-19 02:06:40 -06:00
|
|
|
u32 ep_ien;
|
|
|
|
int bit;
|
|
|
|
u32 reg;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev = cdns->gadget_dev;
|
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-11-06 22:49:51 -07:00
|
|
|
reg = readl(&priv_dev->regs->usb_ists);
|
|
|
|
if (reg) {
|
|
|
|
writel(reg, &priv_dev->regs->usb_ists);
|
|
|
|
writel(USB_IEN_INIT, &priv_dev->regs->usb_ien);
|
|
|
|
cdns3_check_usb_interrupt_proceed(priv_dev, reg);
|
|
|
|
ret = IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
reg = readl(&priv_dev->regs->ep_ists);
|
|
|
|
|
|
|
|
/* handle default endpoint OUT */
|
|
|
|
if (reg & EP_ISTS_EP_OUT0) {
|
|
|
|
cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
|
|
|
|
ret = IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle default endpoint IN */
|
|
|
|
if (reg & EP_ISTS_EP_IN0) {
|
|
|
|
cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
|
|
|
|
ret = IRQ_HANDLED;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* check if interrupt from non default endpoint, if no exit */
|
|
|
|
reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
|
|
|
|
if (!reg)
|
|
|
|
goto irqend;
|
|
|
|
|
|
|
|
for_each_set_bit(bit, (unsigned long *)®,
|
|
|
|
sizeof(u32) * BITS_PER_BYTE) {
|
|
|
|
priv_dev->shadow_ep_en |= BIT(bit);
|
|
|
|
cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
|
|
|
|
ret = IRQ_HANDLED;
|
2018-07-02 21:29:22 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (priv_dev->run_garbage_colector) {
|
|
|
|
struct cdns3_aligned_buf *buf, *tmp;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(buf, tmp, &priv_dev->aligned_buf_list,
|
|
|
|
list) {
|
|
|
|
if (!buf->in_use) {
|
|
|
|
list_del(&buf->list);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
|
|
|
dma_free_coherent(priv_dev->sysdev, buf->size,
|
|
|
|
buf->buf,
|
|
|
|
buf->dma);
|
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
|
|
|
|
|
|
|
kfree(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
priv_dev->run_garbage_colector = 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
irqend:
|
|
|
|
ep_ien = readl(&priv_dev->regs->ep_ien) | priv_dev->shadow_ep_en;
|
|
|
|
priv_dev->shadow_ep_en = 0;
|
|
|
|
/* Unmask all handled EP interrupts */
|
|
|
|
writel(ep_ien, &priv_dev->regs->ep_ien);
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
2018-05-07 02:38:43 -06:00
|
|
|
return ret;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
2019-06-19 02:06:40 -06:00
|
|
|
|
2018-05-06 20:59:55 -06:00
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP
|
2018-05-06 20:59:55 -06:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* The real reservation will occur during write to EP_CFG register,
|
|
|
|
* this function is used to check if the 'size' reservation is allowed.
|
2018-05-06 20:59:55 -06:00
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* @priv_dev: extended gadget object
|
2018-05-06 20:59:55 -06:00
|
|
|
* @size: the size (KB) for EP would like to allocate
|
|
|
|
*
|
2019-06-19 02:06:40 -06:00
|
|
|
* Return 0 if the required size can met or negative value on failure
|
2018-05-06 20:59:55 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
#define CDNS3_ONCHIP_BUF_SIZE 16
|
|
|
|
static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
|
|
|
|
int size, int is_in)
|
2018-05-06 20:59:55 -06:00
|
|
|
{
|
|
|
|
if (is_in) {
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev->onchip_mem_allocated_size += size;
|
|
|
|
} else if (!priv_dev->out_mem_is_allocated) {
|
2018-05-06 20:59:55 -06:00
|
|
|
/* ALL OUT EPs are shared the same chunk onchip memory */
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev->onchip_mem_allocated_size += size;
|
|
|
|
priv_dev->out_mem_is_allocated = 1;
|
2018-05-06 20:59:55 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (priv_dev->onchip_mem_allocated_size > CDNS3_ONCHIP_BUF_SIZE) {
|
|
|
|
priv_dev->onchip_mem_allocated_size -= size;
|
2018-05-06 20:59:55 -06:00
|
|
|
return -EPERM;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_ep_config Configure hardware endpoint
|
|
|
|
* @priv_ep: extended endpoint object
|
2017-07-24 19:39:40 -06:00
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
|
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
|
|
|
u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
|
2017-07-24 19:39:40 -06:00
|
|
|
u32 max_packet_size = 0;
|
2019-06-19 02:06:40 -06:00
|
|
|
u8 buffering;
|
|
|
|
u8 maxburst = 0;
|
|
|
|
u32 ep_cfg = 0;
|
|
|
|
u8 mult = 0;
|
|
|
|
int ret;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
buffering = CDNS3_EP_BUF_SIZE - 1;
|
|
|
|
|
|
|
|
switch (priv_ep->type) {
|
|
|
|
case USB_ENDPOINT_XFER_INT:
|
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
2019-09-26 03:11:18 -06:00
|
|
|
ep_cfg = EP_CFG_EPTYPE(priv_ep->type);
|
|
|
|
if (priv_dev->dev_ver == DEV_VER_V2 && !priv_ep->dir)
|
|
|
|
ep_cfg |= EP_CFG_TDL_CHK;
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
break;
|
2019-06-19 02:06:40 -06:00
|
|
|
default:
|
|
|
|
ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
|
|
|
|
mult = CDNS3_EP_ISO_HS_MULT - 1;
|
|
|
|
buffering = mult + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (priv_dev->gadget.speed) {
|
2017-07-24 19:39:40 -06:00
|
|
|
case USB_SPEED_FULL:
|
2019-06-19 02:06:40 -06:00
|
|
|
max_packet_size = is_iso_ep ? 1023 : 64;
|
2017-07-24 19:39:40 -06:00
|
|
|
break;
|
|
|
|
case USB_SPEED_HIGH:
|
2019-06-19 02:06:40 -06:00
|
|
|
max_packet_size = is_iso_ep ? 1024 : 512;
|
2017-07-24 19:39:40 -06:00
|
|
|
break;
|
|
|
|
case USB_SPEED_SUPER:
|
2019-06-19 02:06:40 -06:00
|
|
|
/* It's limitation that driver assumes in driver. */
|
|
|
|
mult = 0;
|
|
|
|
max_packet_size = 1024;
|
|
|
|
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
|
|
|
|
maxburst = CDNS3_EP_ISO_SS_BURST - 1;
|
|
|
|
buffering = (mult + 1) *
|
|
|
|
(maxburst + 1);
|
|
|
|
|
|
|
|
if (priv_ep->interval > 1)
|
|
|
|
buffering++;
|
|
|
|
} else {
|
|
|
|
maxburst = CDNS3_EP_BUF_SIZE - 1;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
break;
|
2019-06-19 02:06:40 -06:00
|
|
|
default:
|
|
|
|
/* all other speed are not supported */
|
|
|
|
return;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (max_packet_size == 1024)
|
|
|
|
priv_ep->trb_burst_size = 128;
|
|
|
|
else if (max_packet_size >= 512)
|
|
|
|
priv_ep->trb_burst_size = 64;
|
|
|
|
else
|
|
|
|
priv_ep->trb_burst_size = 16;
|
|
|
|
|
|
|
|
ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering,
|
|
|
|
!!priv_ep->dir);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
|
2018-05-06 20:59:55 -06:00
|
|
|
return;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
|
|
|
|
EP_CFG_MULT(mult) |
|
|
|
|
EP_CFG_BUFFERING(buffering) |
|
|
|
|
EP_CFG_MAXBURST(maxburst);
|
2018-05-06 20:59:55 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_select_ep(priv_dev, bEndpointAddress);
|
|
|
|
writel(ep_cfg, &priv_dev->regs->ep_cfg);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
|
|
|
|
priv_ep->name, ep_cfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find correct direction for HW endpoint according to description */
|
|
|
|
static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc,
|
|
|
|
struct cdns3_endpoint *priv_ep)
|
|
|
|
{
|
|
|
|
return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) ||
|
|
|
|
(priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc));
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct
|
|
|
|
cdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev,
|
|
|
|
struct usb_endpoint_descriptor *desc)
|
|
|
|
{
|
|
|
|
struct usb_ep *ep;
|
|
|
|
struct cdns3_endpoint *priv_ep;
|
|
|
|
|
|
|
|
list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
|
|
|
|
unsigned long num;
|
|
|
|
int ret;
|
|
|
|
/* ep name pattern likes epXin or epXout */
|
|
|
|
char c[2] = {ep->name[2], '\0'};
|
|
|
|
|
|
|
|
ret = kstrtoul(c, 10, &num);
|
|
|
|
if (ret)
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
|
|
|
|
priv_ep = ep_to_cdns3_ep(ep);
|
|
|
|
if (cdns3_ep_dir_is_correct(desc, priv_ep)) {
|
|
|
|
if (!(priv_ep->flags & EP_CLAIMED)) {
|
|
|
|
priv_ep->num = num;
|
|
|
|
return priv_ep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ERR_PTR(-ENOENT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cadence IP has one limitation that all endpoints must be configured
|
|
|
|
* (Type & MaxPacketSize) before setting configuration through hardware
|
|
|
|
* register, it means we can't change endpoints configuration after
|
|
|
|
* set_configuration.
|
|
|
|
*
|
|
|
|
* This function set EP_CLAIMED flag which is added when the gadget driver
|
|
|
|
* uses usb_ep_autoconfig to configure specific endpoint;
|
|
|
|
* When the udc driver receives set_configurion request,
|
|
|
|
* it goes through all claimed endpoints, and configure all endpoints
|
|
|
|
* accordingly.
|
|
|
|
*
|
|
|
|
* At usb_ep_ops.enable/disable, we only enable and disable endpoint through
|
|
|
|
* ep_cfg register which can be changed after set_configuration, and do
|
|
|
|
* some software operation accordingly.
|
|
|
|
*/
|
|
|
|
static struct
|
|
|
|
usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
|
|
|
|
struct usb_endpoint_descriptor *desc,
|
|
|
|
struct usb_ss_ep_comp_descriptor *comp_desc)
|
|
|
|
{
|
|
|
|
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
|
|
|
|
struct cdns3_endpoint *priv_ep;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
priv_ep = cdns3_find_available_ep(priv_dev, desc);
|
|
|
|
if (IS_ERR(priv_ep)) {
|
|
|
|
dev_err(priv_dev->dev, "no available ep\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
|
|
|
priv_ep->endpoint.desc = desc;
|
|
|
|
priv_ep->dir = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT;
|
|
|
|
priv_ep->type = usb_endpoint_type(desc);
|
|
|
|
priv_ep->flags |= EP_CLAIMED;
|
|
|
|
priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
|
|
|
return &priv_ep->endpoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* cdns3_gadget_ep_alloc_request Allocates request
|
|
|
|
* @ep: endpoint object associated with request
|
|
|
|
* @gfp_flags: gfp flags
|
|
|
|
*
|
|
|
|
* Returns allocated request address, NULL on allocation error
|
|
|
|
*/
|
|
|
|
struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
|
|
|
|
gfp_t gfp_flags)
|
|
|
|
{
|
|
|
|
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
|
|
|
|
struct cdns3_request *priv_req;
|
|
|
|
|
|
|
|
priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
|
|
|
|
if (!priv_req)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
priv_req->priv_ep = priv_ep;
|
|
|
|
|
|
|
|
trace_cdns3_alloc_request(priv_req);
|
|
|
|
return &priv_req->request;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_gadget_ep_free_request Free memory occupied by request
|
|
|
|
* @ep: endpoint object associated with request
|
|
|
|
* @request: request to free memory
|
|
|
|
*/
|
|
|
|
void cdns3_gadget_ep_free_request(struct usb_ep *ep,
|
|
|
|
struct usb_request *request)
|
|
|
|
{
|
|
|
|
struct cdns3_request *priv_req = to_cdns3_request(request);
|
|
|
|
|
|
|
|
if (priv_req->aligned_buf)
|
|
|
|
priv_req->aligned_buf->in_use = 0;
|
|
|
|
|
|
|
|
trace_cdns3_free_request(priv_req);
|
2019-07-31 20:36:54 -06:00
|
|
|
request = NULL;
|
2019-06-19 02:06:40 -06:00
|
|
|
kfree(priv_req);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* cdns3_gadget_ep_enable Enable endpoint
|
2017-07-24 19:39:40 -06:00
|
|
|
* @ep: endpoint object
|
|
|
|
* @desc: endpoint descriptor
|
|
|
|
*
|
|
|
|
* Returns 0 on success, error code elsewhere
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_gadget_ep_enable(struct usb_ep *ep,
|
|
|
|
const struct usb_endpoint_descriptor *desc)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_endpoint *priv_ep;
|
|
|
|
struct cdns3_device *priv_dev;
|
|
|
|
u32 reg = EP_STS_EN_TRBERREN;
|
|
|
|
u32 bEndpointAddress;
|
2017-07-24 19:39:40 -06:00
|
|
|
unsigned long flags;
|
|
|
|
int ret;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep = ep_to_cdns3_ep(ep);
|
|
|
|
priv_dev = priv_ep->cdns3_dev;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
|
|
|
if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
|
2019-06-19 02:06:40 -06:00
|
|
|
dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
|
2017-07-24 19:39:40 -06:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!desc->wMaxPacketSize) {
|
2019-06-19 02:06:40 -06:00
|
|
|
dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n");
|
2017-07-24 19:39:40 -06:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED,
|
|
|
|
"%s is already enabled\n", priv_ep->name))
|
|
|
|
return 0;
|
2017-12-27 02:00:22 -07:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
2017-12-27 02:00:22 -07:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->endpoint.desc = desc;
|
|
|
|
priv_ep->type = usb_endpoint_type(desc);
|
|
|
|
priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
|
2017-12-27 02:00:22 -07:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (priv_ep->interval > ISO_MAX_INTERVAL &&
|
|
|
|
priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
|
|
|
|
dev_err(priv_dev->dev, "Driver is limited to %d period\n",
|
|
|
|
ISO_MAX_INTERVAL);
|
2017-12-27 02:00:22 -07:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto exit;
|
2017-12-27 02:00:22 -07:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
ret = cdns3_allocate_trb_pool(priv_ep);
|
2017-08-01 23:45:14 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (ret)
|
|
|
|
goto exit;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
bEndpointAddress = priv_ep->num | priv_ep->dir;
|
|
|
|
cdns3_select_ep(priv_dev, bEndpointAddress);
|
2017-08-21 01:00:05 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
trace_cdns3_gadget_ep_enable(priv_ep);
|
2017-08-21 01:00:05 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
|
|
|
|
|
|
|
|
ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
|
|
|
|
EP_CMD_CSTALL | EP_CMD_EPRST, 0, 1000);
|
|
|
|
|
|
|
|
/* enable interrupt for selected endpoint */
|
|
|
|
cdns3_set_register_bit(&priv_dev->regs->ep_ien,
|
|
|
|
BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
|
|
|
|
/*
|
|
|
|
* WA2: Set flag for all not ISOC OUT endpoints. If this flag is set
|
|
|
|
* driver try to detect whether endpoint need additional internal
|
|
|
|
* buffer for unblocking on-chip FIFO buffer. This flag will be cleared
|
|
|
|
* if before first DESCMISS interrupt the DMA will be armed.
|
|
|
|
*/
|
2019-09-26 03:11:18 -06:00
|
|
|
if (quirk_internal_buffer && (priv_dev->dev_ver < DEV_VER_V2)) {
|
2019-06-19 02:06:40 -06:00
|
|
|
if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
|
|
|
|
priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
|
|
|
|
reg |= EP_STS_EN_DESCMISEN;
|
|
|
|
}
|
2017-08-21 01:00:05 -06:00
|
|
|
}
|
2019-06-19 02:06:40 -06:00
|
|
|
|
|
|
|
writel(reg, &priv_dev->regs->ep_sts_en);
|
|
|
|
|
|
|
|
cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
|
|
|
|
|
|
|
|
ep->desc = desc;
|
|
|
|
priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
|
|
|
|
priv_ep->wa1_set = 0;
|
|
|
|
priv_ep->enqueue = 0;
|
|
|
|
priv_ep->dequeue = 0;
|
|
|
|
reg = readl(&priv_dev->regs->ep_sts);
|
|
|
|
priv_ep->pcs = !!EP_STS_CCS(reg);
|
|
|
|
priv_ep->ccs = !!EP_STS_CCS(reg);
|
|
|
|
/* one TRB is reserved for link TRB used in DMULT mode*/
|
|
|
|
priv_ep->free_trbs = priv_ep->num_trbs - 1;
|
|
|
|
exit:
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
|
|
|
|
|
|
|
return ret;
|
2017-08-21 01:00:05 -06:00
|
|
|
}
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_gadget_ep_disable Disable endpoint
|
2017-07-24 19:39:40 -06:00
|
|
|
* @ep: endpoint object
|
|
|
|
*
|
|
|
|
* Returns 0 on success, error code elsewhere
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_gadget_ep_disable(struct usb_ep *ep)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_endpoint *priv_ep;
|
|
|
|
struct cdns3_request *priv_req;
|
|
|
|
struct cdns3_device *priv_dev;
|
|
|
|
struct usb_request *request;
|
2017-07-24 19:39:40 -06:00
|
|
|
unsigned long flags;
|
|
|
|
int ret = 0;
|
2017-12-27 02:00:22 -07:00
|
|
|
u32 ep_cfg;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
|
|
|
if (!ep) {
|
2019-06-19 02:06:40 -06:00
|
|
|
pr_err("usbss: invalid parameters\n");
|
2017-07-24 19:39:40 -06:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep = ep_to_cdns3_ep(ep);
|
|
|
|
priv_dev = priv_ep->cdns3_dev;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-07-29 00:34:18 -06:00
|
|
|
if (!(priv_ep->flags & EP_ENABLED) || (priv_ep->endpoint.desc == NULL))
|
2017-11-30 23:13:06 -07:00
|
|
|
return 0;
|
2017-12-27 02:00:22 -07:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
pm_runtime_get_sync(priv_dev->dev);
|
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
trace_cdns3_gadget_ep_disable(priv_ep);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
|
|
|
|
|
|
|
|
ep_cfg = readl(&priv_dev->regs->ep_cfg);
|
|
|
|
ep_cfg &= ~EP_CFG_ENABLE;
|
|
|
|
writel(ep_cfg, &priv_dev->regs->ep_cfg);
|
|
|
|
|
|
|
|
writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
|
|
|
|
|
|
|
|
ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
|
|
|
|
EP_CMD_CSTALL | EP_CMD_EPRST, 0, 1000);
|
|
|
|
while (!list_empty(&priv_ep->pending_req_list)) {
|
|
|
|
request = cdns3_next_request(&priv_ep->pending_req_list);
|
|
|
|
|
|
|
|
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
|
|
|
|
-ESHUTDOWN);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
while (!list_empty(&priv_ep->descmiss_req_list)) {
|
|
|
|
priv_req = cdns3_next_priv_request(&priv_ep->descmiss_req_list);
|
|
|
|
kfree(priv_req->request.buf);
|
2019-07-31 20:36:54 -06:00
|
|
|
list_del_init(&priv_req->list);
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_gadget_ep_free_request(&priv_ep->endpoint,
|
|
|
|
&priv_req->request);
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
while (!list_empty(&priv_ep->deferred_req_list)) {
|
|
|
|
request = cdns3_next_request(&priv_ep->deferred_req_list);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
|
|
|
|
-ESHUTDOWN);
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->descmis_req = NULL;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-07-29 00:34:18 -06:00
|
|
|
priv_ep->endpoint.desc = NULL;
|
|
|
|
priv_ep->flags = 0;
|
|
|
|
priv_ep->flags |= EP_CLAIMED;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
|
|
|
pm_runtime_put_sync(priv_dev->dev);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
return ret;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_gadget_ep_queue Transfer data on endpoint
|
2017-07-24 19:39:40 -06:00
|
|
|
* @ep: endpoint object
|
|
|
|
* @request: request object
|
|
|
|
* @gfp_flags: gfp flags
|
|
|
|
*
|
|
|
|
* Returns 0 on success, error code elsewhere
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
|
|
|
|
struct usb_request *request,
|
|
|
|
gfp_t gfp_flags)
|
|
|
|
{
|
|
|
|
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
|
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
|
|
|
struct cdns3_request *priv_req;
|
|
|
|
int deferred = 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
request->actual = 0;
|
|
|
|
request->status = -EINPROGRESS;
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_req = to_cdns3_request(request);
|
|
|
|
trace_cdns3_ep_queue(priv_req);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* WA2: if transfer was queued before DESCMISS appear than we
|
|
|
|
* can disable handling of DESCMISS interrupt. Driver assumes that it
|
|
|
|
* can disable special treatment for this endpoint.
|
|
|
|
*/
|
|
|
|
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
|
|
|
|
u32 reg;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
|
|
|
|
priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
|
|
|
|
reg = readl(&priv_dev->regs->ep_sts_en);
|
|
|
|
reg &= ~EP_STS_EN_DESCMISEN;
|
|
|
|
writel(reg, &priv_dev->regs->ep_sts_en);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* WA2 */
|
|
|
|
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
|
|
|
|
u8 pending_empty = list_empty(&priv_ep->pending_req_list);
|
|
|
|
u8 descmiss_empty = list_empty(&priv_ep->descmiss_req_list);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/*
|
|
|
|
* DESCMISS transfer has been finished, so data will be
|
|
|
|
* directly copied from internal allocated usb_request
|
|
|
|
* objects.
|
|
|
|
*/
|
|
|
|
if (pending_empty && !descmiss_empty &&
|
|
|
|
!(priv_req->flags & REQUEST_INTERNAL)) {
|
|
|
|
cdns3_descmiss_copy_data(priv_ep, request);
|
|
|
|
list_add_tail(&request->list,
|
|
|
|
&priv_ep->pending_req_list);
|
|
|
|
cdns3_gadget_giveback(priv_ep, priv_req,
|
|
|
|
request->status);
|
|
|
|
return ret;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/*
|
|
|
|
* WA2 driver will wait for completion DESCMISS transfer,
|
|
|
|
* before starts new, not DESCMISS transfer.
|
|
|
|
*/
|
|
|
|
if (!pending_empty && !descmiss_empty)
|
|
|
|
deferred = 1;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (priv_req->flags & REQUEST_INTERNAL)
|
|
|
|
list_add_tail(&priv_req->list,
|
|
|
|
&priv_ep->descmiss_req_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = cdns3_prepare_aligned_request_buf(priv_req);
|
|
|
|
if (ret < 0)
|
2017-07-24 19:39:40 -06:00
|
|
|
return ret;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
|
|
|
|
usb_endpoint_dir_in(ep->desc));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/*
|
|
|
|
* If hardware endpoint configuration has not been set yet then
|
|
|
|
* just queue request in deferred list. Transfer will be started in
|
|
|
|
* cdns3_set_hw_configuration.
|
|
|
|
*/
|
|
|
|
if (!priv_dev->hw_configured_flag)
|
|
|
|
deferred = 1;
|
|
|
|
else
|
|
|
|
ret = cdns3_ep_run_transfer(priv_ep, request);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (ret || deferred)
|
|
|
|
list_add_tail(&request->list, &priv_ep->deferred_req_list);
|
|
|
|
else
|
|
|
|
list_add_tail(&request->list, &priv_ep->pending_req_list);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2018-06-21 04:35:46 -06:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
|
|
|
gfp_t gfp_flags)
|
2018-06-21 04:35:46 -06:00
|
|
|
{
|
|
|
|
struct usb_request *zlp_request;
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_endpoint *priv_ep;
|
|
|
|
struct cdns3_device *priv_dev;
|
2018-06-21 04:35:46 -06:00
|
|
|
unsigned long flags;
|
|
|
|
int ret;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (!request || !ep)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
priv_ep = ep_to_cdns3_ep(ep);
|
|
|
|
priv_dev = priv_ep->cdns3_dev;
|
|
|
|
|
MLK-22366-7 usb: cdns3: gadget: quit if endpoint has already disabled
It fixed below oops:
audit: audit_lost=500851 audit_rate_limit=5 audit_backlog_limit=64
audit: rate limit exceeded
libprocessgroup: Successfully killed process cgroup uid 0 pid 25371 in 49ms
init: Service 'adbd' (pid 25371) received signal 9
init: processing action (init.svc.adbd=stopped) from (/init.usb.configfs.rc:14)
init: Received control message 'start' for 'adbd' from pid: 3308 (/vendor/bin/hw/android.hardware.usb@1.1-service.imx)
init: starting service 'adbd'...
init: Created socket '/dev/socket/adbd', mode 660, user 1000, group 1000
read descriptors
read strings
init: Received control message 'start' for 'adbd' from pid: 3447 (system_server)
android_work: did not send uevent (0 0 (null))
audit: audit_lost=500888 audit_rate_limit=5 audit_backlog_limit=64
audit: rate limit exceeded
read descriptors
read strings
android_work: did not send uevent (0 0 (null))
audit: audit_lost=500925 audit_rate_limit=5 audit_backlog_limit=64
audit: rate limit exceeded
using random self ethernet address
using random host ethernet address
read descriptors
read strings
usb0: HOST MAC 32:e7:67:29:5f:d8
usb0: MAC 02:5c:48:b3:2b:d7
android_work: sent uevent USB_STATE=CONNECTED
configfs-gadget gadget: high-speed config #1: b
android_work: sent uevent USB_STATE=CONFIGURED
Unable to handle kernel NULL pointer dereference at virtual address 00000002
Mem abort info:
Exception class = DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
Data abort info:
ISV = 0, ISS = 0x00000004
CM = 0, WnR = 0
user pgtable: 4k pages, 48-bit VAs, pgd = ffff8008f7bed000
[0000000000000002] *pgd=0000000000000000
Internal error: Oops: 96000004 [#1] PREEMPT SMP
Modules linked in:
CPU: 2 PID: 3326 Comm: Binder:3276_2 Not tainted 4.14.98-07848-gfb2a5a8 #1
Hardware name: Freescale i.MX8QM MEK (DT)
task: ffff8008f77f3800 task.stack: ffff00000b378000
PC is at __cdns3_gadget_ep_queue.isra.18+0x238/0x524
LR is at cdns3_gadget_ep_queue+0x44/0xd8
pc : [<ffff0000089bbf90>] lr : [<ffff0000089bc2c0>] pstate: 400001c5
sp : ffff00000b37ba40
x29: ffff00000b37ba40 x28: ffff8008f235fa00
x27: ffff8008f2f11600 x26: ffff8008f2dee118
x25: ffff8008f2df0000 x24: 0000000000000000
x23: ffff000009c8e000 x22: ffff8008f2df0000
x21: 0000000000000000 x20: ffff8008f2dee118
x19: ffff8008f1f1ac00 x18: 0000fcc72810fc1a
x17: 0000fcc729e2d0e0 x16: ffff0000082b4430
x15: 0000000000000000 x14: ffffffffffffffff
x13: 0000000000000000 x12: 0000000000000020
x11: 0000000000000020 x10: 0101010101010101
x9 : 0000000000000000 x8 : 0000000000000024
x7 : 0000000040000000 x6 : 0000000000000020
x5 : 0000000000000000 x4 : 0000000000000000
x3 : 0000000000000140 x2 : 0000000000000000
x1 : ffff8008f1f1ac00 x0 : ffff8008f7738010
X0: 0xffff8008f7737f90:
7f90 f75baef8 ffff8008 00000000 00000000 3305708c 00000000 f7332aa0 ffff8008
7fb0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
7fd0 00005408 00000001 a1ff0014 00000000 00000000 00000000 00000000 00000000
7ff0 00000000 00000000 00000000 00000000 f7548780 ffff8008 ffffffff 00000000
8010 09dc50d8 ffff0000 f76fc800 ffff8008 f7548780 ffff8008 f7738428 ffff8008
8030 f76e7c28 ffff8008 09dc50e8 ffff0000 f6e6ae80 ffff8008 09dc4ac8 ffff0000
8050 f753e660 ffff8008 00000007 00000007 00000000 00000000 00000000 00000000
8070 00000000 00000000 00000000 00000000 f7738080 ffff8008 f7738080 ffff8008
X1: 0xffff8008f1f1ab80:
ab80 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
aba0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
abc0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
abe0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
ac00 ec128080 ffff8008 0000062a 00000000 f5383800 00000000 00000000 00000000
ac20 00000000 00000000 00000000 00000000 08a179d8 ffff0000 e85aeb00 ffff8008
ac40 f1f1ac40 ffff8008 f1f1ac40 ffff8008 ffffff8d 00000000 f2dee118 ffff8008
ac60 0afdb024 ffff0000 00000003 00000003 00000000 00000000 00000000 00000000
X19: 0xffff8008f1f1ab80:
ab80 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
aba0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
abc0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
abe0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
ac00 ec128080 ffff8008 0000062a 00000000 f5383800 00000000 00000000 00000000
ac20 00000000 00000000 00000000 00000000 08a179d8 ffff0000 e85aeb00 ffff8008
ac40 f1f1ac40 ffff8008 f1f1ac40 ffff8008 ffffff8d 00000000 f2dee118 ffff8008
ac60 0afdb024 ffff0000 00000003 00000003 00000000 00000000 00000000 00000000
X20: 0xffff8008f2dee098:
e098 ae000000 00000000 f2df0000 ffff8008 00307065 00000000 00000000 00000000
e0b8 00000000 00000010 00000000 00000000 00000080 00000000 00000002 00000000
e0d8 00000000 00000000 00000002 00000000 00000000 00000000 00000000 00000000
e0f8 00000000 00000000 f2dee200 ffff8008 f2dee000 ffff8008 087793a4 ffff0000
e118 f0475900 ffff8008 f2dee1a8 ffff8008 091c36b0 ffff0000 f2dee230 ffff8008
e138 f2df0048 ffff8008 0000002e 02000000 000f0400 00000101 00000000 00000000
e158 00000000 00000000 f2dee160 ffff8008 f2dee160 ffff8008 f2dee170 ffff8008
e178 f2dee170 ffff8008 f2dee180 ffff8008 f2dee180 ffff8008 0afdb000 ffff0000
X22: 0xffff8008f2deff80:
ff80 f33b40a0 ffff8008 f3dd6810 ffff8008 f03f8de8 ffff8008 00000000 00000000
ffa0 27ac430e 00000000 00000000 00000000 f2aec128 ffff8008 f32e0680 ffff8008
ffc0 00000000 00000000 f3623400 ffff8008 000000ac 00000001 41e80011 00000000
ffe0 f3b18000 ffff8008 00000000 00000000 00000000 00000000 00000000 00000000
0000 f7738010 ffff8008 f7738010 ffff8008 000000c0 00000000 f2df0018 ffff8008
0020 f2df0018 ffff8008 08a077e4 ffff0000 f2d42c00 ffff8008 091c3650 ffff0000
0040 f2dee018 ffff8008 f2dee130 ffff8008 f2deee30 ffff8008 00000003 00000005
0060 00000007 00000000 095d1550 ffff0000 f7738010 ffff8008 f2deef00 ffff8008
X25: 0xffff8008f2deff80:
ff80 f33b40a0 ffff8008 f3dd6810 ffff8008 f03f8de8 ffff8008 00000000 00000000
ffa0 27ac430e 00000000 00000000 00000000 f2aec128 ffff8008 f32e0680 ffff8008
ffc0 00000000 00000000 f3623400 ffff8008 000000ac 00000001 41e80011 00000000
ffe0 f3b18000 ffff8008 00000000 00000000 00000000 00000000 00000000 00000000
0000 f7738010 ffff8008 f7738010 ffff8008 000000c0 00000000 f2df0018 ffff8008
0020 f2df0018 ffff8008 08a077e4 ffff0000 f2d42c00 ffff8008 091c3650 ffff0000
0040 f2dee018 ffff8008 f2dee130 ffff8008 f2deee30 ffff8008 00000003 00000005
0060 00000007 00000000 095d1550 ffff0000 f7738010 ffff8008 f2deef00 ffff8008
X26: 0xffff8008f2dee098:
e098 ae000000 00000000 f2df0000 ffff8008 00307065 00000000 00000000 00000000
e0b8 00000000 00000010 00000000 00000000 00000080 00000000 00000002 00000000
e0d8 00000000 00000000 00000002 00000000 00000000 00000000 00000000 00000000
e0f8 00000000 00000000 f2dee200 ffff8008 f2dee000 ffff8008 087793a4 ffff0000
e118 f0475900 ffff8008 f2dee1a8 ffff8008 091c36b0 ffff0000 f2dee230 ffff8008
e138 f2df0048 ffff8008 0000002e 02000000 000f0400 00000101 00000000 00000000
e158 00000000 00000000 f2dee160 ffff8008 f2dee160 ffff8008 f2dee170 ffff8008
e178 f2dee170 ffff8008 f2dee180 ffff8008 f2dee180 ffff8008 0afdb000 ffff0000
X27: 0xffff8008f2f11580:
1580 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
15a0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
15c0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
15e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1600 f0475000 ffff8008 00000003 00000000 f235fa00 ffff8008 00000000 00000000
1620 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1640 00000000 00000000 00000000 00000000 00000002 00000000 00000000 00000000
1660 00000000 00000000 00000000 00000000 08e3b158 ffff0000 f2f11600 ffff8008
X28: 0xffff8008f235f980:
f980 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
f9a0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
f9c0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
f9e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
fa00 00000000 00000000 09f6e3d8 ffff0000 00000000 00000000 f2f11600 ffff8008
fa20 00000000 00000000 00000000 00000000 812aa8c0 812aa8c0 00ffffff ff2aa8c0
fa40 00001800 00000080 30627375 00000000 00000000 00000000 00000000 00000000
fa60 0046ac7c 00000001 0046ac7c 00000001 00000000 00000000 00000000 00000000
Process Binder:3276_2 (pid: 3326, stack limit = 0xffff00000b378000)
Call trace:
Exception stack(0xffff00000b37b900 to 0xffff00000b37ba40)
b900: ffff8008f7738010 ffff8008f1f1ac00 0000000000000000 0000000000000140
b920: 0000000000000000 0000000000000000 0000000000000020 0000000040000000
b940: 0000000000000024 0000000000000000 0101010101010101 0000000000000020
b960: 0000000000000020 0000000000000000 ffffffffffffffff 0000000000000000
b980: ffff0000082b4430 0000fcc729e2d0e0 0000fcc72810fc1a ffff8008f1f1ac00
b9a0: ffff8008f2dee118 0000000000000000 ffff8008f2df0000 ffff000009c8e000
b9c0: 0000000000000000 ffff8008f2df0000 ffff8008f2dee118 ffff8008f2f11600
b9e0: ffff8008f235fa00 ffff00000b37ba40 ffff0000089bc2c0 ffff00000b37ba40
ba00: ffff0000089bbf90 00000000400001c5 ffff00000b37ba20 ffff00000827c354
ba20: 0000ffffffffffff ffff000008d578c4 ffff00000b37ba40 ffff0000089bbf90
[<ffff0000089bbf90>] __cdns3_gadget_ep_queue.isra.18+0x238/0x524
[<ffff0000089bc2c0>] cdns3_gadget_ep_queue+0x44/0xd8
[<ffff000008a06f70>] usb_ep_queue+0x5c/0xfc
[<ffff000008a1762c>] rx_submit+0xec/0x1bc
[<ffff000008a1775c>] rx_fill+0x60/0xb4
[<ffff000008a17c7c>] eth_open+0x64/0x78
[<ffff000008d72cf4>] __dev_open+0xe0/0x164
[<ffff000008d73128>] __dev_change_flags+0x160/0x18c
[<ffff000008d73174>] dev_change_flags+0x20/0x5c
[<ffff000008e359f0>] devinet_ioctl+0x68c/0x724
[<ffff000008e37cc4>] inet_ioctl+0x8c/0xa8
[<ffff000008d48a48>] sock_do_ioctl+0x34/0x70
[<ffff000008d49da0>] sock_ioctl+0x21c/0x320
[<ffff0000082b3b98>] do_vfs_ioctl+0xbc/0x954
[<ffff0000082b44b4>] SyS_ioctl+0x84/0x98
Exception stack(0xffff00000b37bec0 to 0xffff00000b37c000)
bec0: 000000000000003d 0000000000008914 0000fcc728110840 0000000000000003
bee0: 0000fcc728110850 6f43ffffffffffff 0000000000000000 0000000000800000
bf00: 000000000000001d 0000fcc7281107f8 0000fcc7281107f8 0000fcc728110840
bf20: 0000fcc728110458 0000000000000028 ffffffffffffffff 00002d18ec000000
bf40: 0000fcc72962af68 0000fcc729e2d0e0 0000fcc72810fc1a 0000fcc728111588
bf60: 0000fcc72962b000 0000ab36f6e99ac5 0000ab36f6e99a8e 0000000000000008
bf80: 0000ab36f6e99ac8 0000fcc728111588 0000ab36f6e99c0e 0000000000000006
bfa0: 0000fcc72902b108 0000fcc728110830 0000fcc729e2d16c 0000fcc728110740
bfc0: 0000fcc729e73758 00000000a0000000 000000000000003d 000000000000001d
bfe0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[<ffff000008083ac0>] el0_svc_naked+0x34/0x38
Code: d503201f f9401e82 aa1303e1 f94006c0 (39c00842)
---[ end trace f7b45310362d53ac ]---
Kernel panic - not syncing: Fatal exception
SMP: stopping secondary CPUs
SMP: failed to stop secondary CPUs 0,2,4
Kernel Offset: disabled
CPU features: 0x180200c
Memory Limit: none
Rebooting in 5 seconds..
SMP: stopping secondary CPUs
SMP: failed to stop secondary CPUs 0,2,4
Signed-off-by: Peter Chen <peter.chen@nxp.com>
2019-08-08 01:52:49 -06:00
|
|
|
if (!priv_ep->endpoint.desc)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
|
|
|
|
|
|
|
ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
|
2018-06-21 04:35:46 -06:00
|
|
|
|
|
|
|
if (ret == 0 && request->zero && request->length &&
|
2019-06-19 02:06:40 -06:00
|
|
|
(request->length % ep->maxpacket == 0)) {
|
|
|
|
struct cdns3_request *priv_req;
|
|
|
|
|
|
|
|
zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
|
|
|
|
zlp_request->buf = priv_dev->zlp_buf;
|
2018-06-21 04:35:46 -06:00
|
|
|
zlp_request->length = 0;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_req = to_cdns3_request(zlp_request);
|
|
|
|
priv_req->flags |= REQUEST_ZLP;
|
|
|
|
|
|
|
|
dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n",
|
|
|
|
priv_ep->name);
|
|
|
|
ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags);
|
2018-06-21 04:35:46 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
2017-07-24 19:39:40 -06:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_gadget_ep_dequeue Remove request from transfer queue
|
2017-07-24 19:39:40 -06:00
|
|
|
* @ep: endpoint object associated with request
|
|
|
|
* @request: request object
|
|
|
|
*
|
|
|
|
* Returns 0 on success, error code elsewhere
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
|
|
|
|
struct usb_request *request)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
|
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
2018-03-05 23:52:35 -07:00
|
|
|
struct usb_request *req, *req_temp;
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_request *priv_req;
|
|
|
|
struct cdns3_trb *link_trb;
|
2019-10-13 03:20:20 -06:00
|
|
|
u8 req_on_hw_ring = 0;
|
2019-06-19 02:06:40 -06:00
|
|
|
unsigned long flags;
|
2018-04-26 20:46:46 -06:00
|
|
|
int ret = 0;
|
2018-03-05 23:52:35 -07:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (!ep || !request || !ep->desc)
|
2018-03-05 23:52:35 -07:00
|
|
|
return -EINVAL;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
|
|
|
|
|
|
|
priv_req = to_cdns3_request(request);
|
|
|
|
|
|
|
|
trace_cdns3_ep_dequeue(priv_req);
|
|
|
|
|
|
|
|
cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
|
|
|
|
|
|
|
|
list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
|
|
|
|
list) {
|
2019-10-13 03:20:20 -06:00
|
|
|
if (request == req) {
|
|
|
|
req_on_hw_ring = 1;
|
2019-06-19 02:06:40 -06:00
|
|
|
goto found;
|
2019-10-13 03:20:20 -06:00
|
|
|
}
|
2019-06-19 02:06:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
|
|
|
|
list) {
|
|
|
|
if (request == req)
|
|
|
|
goto found;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
goto not_found;
|
|
|
|
|
|
|
|
found:
|
|
|
|
link_trb = priv_req->trb;
|
|
|
|
|
2019-10-13 03:20:20 -06:00
|
|
|
/* Update ring only if removed request is on pending_req_list list */
|
2020-04-30 00:39:12 -06:00
|
|
|
if (req_on_hw_ring && link_trb) {
|
2019-06-19 02:06:40 -06:00
|
|
|
link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma +
|
2020-02-14 00:14:13 -07:00
|
|
|
((priv_req->end_trb + 1) * TRB_SIZE));
|
2019-06-19 02:06:40 -06:00
|
|
|
link_trb->control = (link_trb->control & TRB_CYCLE) |
|
2019-10-13 03:20:20 -06:00
|
|
|
TRB_TYPE(TRB_LINK) | TRB_CHAIN;
|
|
|
|
|
|
|
|
if (priv_ep->wa1_trb == priv_req->trb)
|
|
|
|
cdns3_wa1_restore_cycle_bit(priv_ep);
|
2019-06-19 02:06:40 -06:00
|
|
|
}
|
2018-12-23 23:59:50 -07:00
|
|
|
|
2019-10-13 03:20:20 -06:00
|
|
|
cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
not_found:
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
2019-07-31 20:36:54 -06:00
|
|
|
if (ep == priv_dev->gadget.ep0)
|
|
|
|
flush_work(&priv_dev->pending_status_wq);
|
|
|
|
|
2018-04-26 20:46:46 -06:00
|
|
|
return ret;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
|
2017-07-24 19:39:40 -06:00
|
|
|
* @ep: endpoint object to set/clear stall on
|
|
|
|
* @value: 1 for set stall, 0 for clear stall
|
|
|
|
*
|
|
|
|
* Returns 0 on success, error code elsewhere
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
|
|
|
|
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
|
2017-07-24 19:39:40 -06:00
|
|
|
unsigned long flags;
|
2018-05-07 02:38:43 -06:00
|
|
|
int ret = 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (!(priv_ep->flags & EP_ENABLED))
|
2017-07-24 19:39:40 -06:00
|
|
|
return -EPERM;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
|
2017-07-24 19:39:40 -06:00
|
|
|
if (value) {
|
2019-09-09 00:37:34 -06:00
|
|
|
if (!list_empty(&priv_ep->pending_req_list)) {
|
|
|
|
ret = -EAGAIN;
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_ep_stall_flush(priv_ep);
|
2017-07-24 19:39:40 -06:00
|
|
|
} else {
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->flags &= ~EP_WEDGE;
|
|
|
|
|
|
|
|
cdns3_dbg(priv_ep->cdns3_dev, "Clear stalled endpoint %s\n",
|
|
|
|
priv_ep->name);
|
|
|
|
|
|
|
|
writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
/* wait for EPRST cleared */
|
2019-06-19 02:06:40 -06:00
|
|
|
ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
|
|
|
|
EP_CMD_EPRST, 0, 100);
|
|
|
|
if (unlikely(ret)) {
|
|
|
|
dev_err(priv_dev->dev,
|
|
|
|
"Clearing halt condition failed for %s\n",
|
|
|
|
priv_ep->name);
|
|
|
|
goto finish;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
} else {
|
|
|
|
priv_ep->flags &= ~EP_STALL;
|
|
|
|
}
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->flags &= ~EP_PENDING_REQUEST;
|
|
|
|
finish:
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
return ret;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
extern const struct usb_ep_ops cdns3_gadget_ep0_ops;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static const struct usb_ep_ops cdns3_gadget_ep_ops = {
|
|
|
|
.enable = cdns3_gadget_ep_enable,
|
|
|
|
.disable = cdns3_gadget_ep_disable,
|
|
|
|
.alloc_request = cdns3_gadget_ep_alloc_request,
|
|
|
|
.free_request = cdns3_gadget_ep_free_request,
|
|
|
|
.queue = cdns3_gadget_ep_queue,
|
|
|
|
.dequeue = cdns3_gadget_ep_dequeue,
|
|
|
|
.set_halt = cdns3_gadget_ep_set_halt,
|
|
|
|
.set_wedge = cdns3_gadget_ep_set_wedge,
|
2017-07-24 19:39:40 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_gadget_get_frame Returns number of actual ITP frame
|
2017-07-24 19:39:40 -06:00
|
|
|
* @gadget: gadget object
|
|
|
|
*
|
|
|
|
* Returns number of actual ITP frame
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
return readl(&priv_dev->regs->usb_itpn);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2020-05-23 19:41:01 -06:00
|
|
|
int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2020-05-23 19:41:01 -06:00
|
|
|
enum usb_device_speed speed;
|
|
|
|
|
|
|
|
speed = cdns3_get_speed(priv_dev);
|
|
|
|
|
|
|
|
if (speed >= USB_SPEED_SUPER)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Start driving resume signaling to indicate remote wakeup. */
|
|
|
|
writel(USB_CONF_LGO_L0, &priv_dev->regs->usb_conf);
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-23 19:41:01 -06:00
|
|
|
static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
|
|
|
|
{
|
|
|
|
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
|
|
|
|
unsigned long flags;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
|
|
|
ret = __cdns3_gadget_wakeup(priv_dev);
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
|
|
|
|
int is_selfpowered)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
|
2018-04-13 02:12:52 -06:00
|
|
|
unsigned long flags;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
|
|
|
priv_dev->is_selfpowered = !!is_selfpowered;
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
2017-07-24 19:39:40 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (!priv_dev->start_gadget)
|
2017-08-21 01:04:58 -06:00
|
|
|
return 0;
|
|
|
|
|
2020-04-29 03:31:19 -06:00
|
|
|
if (is_on) {
|
2019-06-19 02:06:40 -06:00
|
|
|
writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
|
2020-04-29 03:31:19 -06:00
|
|
|
} else {
|
|
|
|
writel(~0, &priv_dev->regs->ep_ists);
|
|
|
|
writel(~0, &priv_dev->regs->usb_ists);
|
2019-06-19 02:06:40 -06:00
|
|
|
writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
|
2020-04-29 03:31:19 -06:00
|
|
|
}
|
2017-08-21 01:04:58 -06:00
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static void cdns3_gadget_config(struct cdns3_device *priv_dev)
|
|
|
|
{
|
|
|
|
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
|
|
|
|
|
|
|
|
cdns3_ep0_config(priv_dev);
|
|
|
|
|
|
|
|
/* enable interrupts for endpoint 0 (in and out) */
|
|
|
|
writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, ®s->ep_ien);
|
2019-09-26 03:11:18 -06:00
|
|
|
priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
|
|
|
|
priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver);
|
2019-06-19 02:06:40 -06:00
|
|
|
|
|
|
|
/*
|
2019-09-26 03:11:18 -06:00
|
|
|
* Driver needs to modify LFPS minimal U1 Exit time for DEV_VER_TI_V1
|
|
|
|
* revision of controller.
|
2019-06-19 02:06:40 -06:00
|
|
|
*/
|
2019-09-26 03:11:18 -06:00
|
|
|
if (priv_dev->dev_ver == DEV_VER_TI_V1) {
|
2019-06-19 02:06:40 -06:00
|
|
|
u32 reg = readl(®s->dbg_link1);
|
|
|
|
|
|
|
|
reg &= ~DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_MASK;
|
|
|
|
reg |= DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(0x55) |
|
|
|
|
DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET;
|
|
|
|
writel(reg, ®s->dbg_link1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable generic interrupt*/
|
|
|
|
writel(USB_IEN_INIT, ®s->usb_ien);
|
|
|
|
writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, ®s->usb_conf);
|
|
|
|
writel(USB_CONF_DMULT, ®s->usb_conf);
|
2019-09-26 03:11:18 -06:00
|
|
|
if (priv_dev->dev_ver == DEV_VER_V2)
|
|
|
|
writel(USB_CONF2_EN_TDL_TRB, ®s->usb_conf2);
|
2020-03-16 21:03:37 -06:00
|
|
|
else
|
|
|
|
priv_dev->gadget.sg_supported = 0;
|
2019-09-26 03:11:18 -06:00
|
|
|
|
2020-04-29 03:08:15 -06:00
|
|
|
/* keep Fast Access bit */
|
|
|
|
writel(PUSB_PWR_FST_REG_ACCESS, &priv_dev->regs->usb_pwr);
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_gadget_pullup(&priv_dev->gadget, 1);
|
|
|
|
}
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_gadget_udc_start Gadget start
|
2017-07-24 19:39:40 -06:00
|
|
|
* @gadget: gadget object
|
|
|
|
* @driver: driver which operates on this gadget
|
|
|
|
*
|
|
|
|
* Returns 0 on success, error code elsewhere
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
|
|
|
|
struct usb_gadget_driver *driver)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
|
2017-07-24 19:39:40 -06:00
|
|
|
unsigned long flags;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
dev_dbg(priv_dev->dev, "%s begins\n", __func__);
|
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
|
|
|
priv_dev->gadget_driver = driver;
|
|
|
|
if (!priv_dev->start_gadget) {
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
2017-07-24 19:39:40 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_gadget_config(priv_dev);
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
2017-07-24 19:39:40 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_gadget_udc_stop Stops gadget
|
2017-07-24 19:39:40 -06:00
|
|
|
* @gadget: gadget object
|
|
|
|
*
|
|
|
|
* Returns 0
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
|
|
|
|
struct cdns3_endpoint *priv_ep;
|
|
|
|
struct usb_ep *ep;
|
2019-07-29 00:34:18 -06:00
|
|
|
unsigned long flags;
|
2018-05-07 02:38:43 -06:00
|
|
|
int ret = 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-07-29 00:34:18 -06:00
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev->gadget_driver = NULL;
|
MA-13475 usb: cdns3: gadget: fix NULL pointer issue when switch functions
Below oops is reproduced when switch between mtp and ptp function
at Android, the interrupt occurs when the usb_ss->gadget_driver
is already NULL. In this commit, we add NULL pointer check for
it as well as protecting for clear usb_ss->gadget_driver.
init: Sending signal 9 to service 'adbd' (pid 4644) process group...
Mem abort info:
Exception class = DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
Data abort info:
ISV = 0, ISS = 0x00000004
CM = 0, WnR = 0
user pgtable: 4k pages, 48-bit VAs, pgd = ffff800830e49000
[0000000000000020] *pgd=0000000000000000
Internal error: Oops: 96000004 [#1] PREEMPT SMP
Modules linked in: wlan
CPU: 0 PID: 3226 Comm: surfaceflinger Not tainted 4.14.98 #1
init: Successfully killed process cgroup uid 0 pid 4644 in 11ms
Hardware name: Freescale i.MX8QXP MEK (DT)
task: ffff80083bf55400 task.stack: ffff00000ffe8000
PC is at cdns_get_setup_ret+0x38/0x64
LR is at cdns_get_setup_ret+0x24/0x64
pc : [<ffff0000089b0e88>] lr : [<ffff0000089b0e74>] pstate: 000001c5
sp : ffff000008003d10
x29: ffff000008003d10 x28: ffff80083bf55400
x27: ffff000009873018 x26: ffff000008004000
x25: ffff000009873018 x24: ffff000009c6a288
x23: ffff000008003f24 x22: ffff00000a85e000
x21: 0000000000000001 x20: ffff8008361c97b8
x19: ffff8008361c9000 x18: 0000000000000001
x17: 0000fe6e544aee34 x16: ffff0000082b54f0
x15: 0000ffffccae15f0 x14: 0000000000000000
x13: 0000000000000000 x12: 0000000000010000
x11: 0000000000000040 x10: 0000000000000040
x9 : ffff8008380ae238 x8 : ffff800838000248
x7 : 0000000000000000 x6 : ffff800838000248
init: Service 'adbd' (pid 4644) killed by signal 9
x5 : ffff80083ae40600 x4 : 0000000000000000
x3 : 00000000000001c0 x2 : 0000000000000000
x1 : ffff00000a85e000 x0 : ffff8008361c9300
X0: 0xffff8008361c9280:
9280 00000000 00000000 00000000 00000000 000f000f
init: processing action (init.svc.adbd=stopped) from (/init.usb.configfs.rc:15)
00000000 3bf6ce00 ffff8008
92a0 3acd9a00 ffff8008 00000000 00000000 00000000 00000000 00000000 00000000
92c0 00000000 00000000 00000000 00000000 00000000 00000000 089b0e3c ffff0000
92e0 00000000
init: processing action (sys.usb.config=mtp,adb && sys.usb.configfs=1) from (/init.usb.configfs.rc:33)
00000000 00000000 00000000
read descriptors
read strings
init: starting service 'adbd'...
00000000
init: property_set("ro.boottime.adbd", "97804848500") failed: property already set
init: Created socket '/dev/socket/adbd', mode 660, user 1000, group 1000
00000000 0e0f0000 ffff0000
9300 3ff21105 ffff8008 3ff1d120 ffff8008 2e15d4e0 ffff8008 089f9558 ffff0000
9320 3618ac00 ffff8008 091b7860 ffff0000 3bf6ce18 ffff8008 3bf6cd30
read descriptors
read strings
ffff8008
9340 3acd9a30 ffff8008 00000000 00000005 00000000 00000000 095c4e58 ffff0000
9360 361c9000 ffff8008 3acd9700 ffff8008 095be4c8 ffff0000 3618ac28 ffff8008
X5: 0xffff80083ae40580:
0580 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
05a0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
05c0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
05e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0600 03440204 ffffffff 00000000 00000000 00000000 00000000 0000000f 00000000
0620 00000001 00000000 00000000 00000030 0000010f 00000000 3ae40600 ffff8008
0640 09d7aae0 ffff0000 3a088000 ffff8008 3aa22c80 ffff8008 00000000 00000000
0660 09886e24 ffff0000 081496ec ffff0000 3b444080 ffff8008 00000000 00000000
X6: 0xffff8008380001c8:
01c8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
01e8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0208 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0228 fffffc00 ffffffff 00000000 00000000 00000000 00000000 38000248 ffff8008
0248 003f0000 00000000 380af6c0 ffff8008 09c937f0 ffff0000 38000260 ffff8008
0268 38000260 ffff8008 00000000 00000000 3a007400 ffff8008 3a007600 ffff8008
0288 3a007800 ffff8008 3a007a00 ffff8008 3a007c00 ffff8008 3a6be600 ffff8008
02a8 3ab1da00 ffff8008 3ab1dc00 ffff8008 3ab1de00 ffff8008 3ac52000 ffff8008
X8: 0xffff8008380001c8:
01c8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
01e8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0208 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0228 fffffc00 ffffffff 00000000 00000000 00000000 00000000 38000248 ffff8008
0248 003f0000 00000000 380af6c0 ffff8008 09c937f0 ffff0000 38000260 ffff8008
0268 38000260 ffff8008 00000000 00000000 3a007400 ffff8008 3a007600 ffff8008
0288 3a007800 ffff8008 3a007a00 ffff8008 3a007c00 ffff8008 3a6be600 ffff8008
02a8 3ab1da00 ffff8008 3ab1dc00 ffff8008 3ab1de00 ffff8008 3ac52000 ffff8008
X9: 0xffff8008380ae1b8:
e1b8 3ae1ddd0 ffff8008 3ae1de58 ffff8008 3ae1dee0 ffff8008 3ae1df68 ffff8008
e1d8 3aad9000 ffff8008 3aad9088 ffff8008 3aad9110 ffff8008 3aad9198 ffff8008
e1f8 3aad9220 ffff8008 3aad92a8 ffff8008 3aad9330 ffff8008 3aad93b8 ffff8008
e218 00000000 00000000 00000000 00000000 00000000 00000000 380ae238 ffff8008
e238 000b0400 00000000 3802c490 ffff8008 3a081060 ffff8008 380ae250 ffff8008
e258 380ae250 ffff8008 00000000 00000000 00000000 00000000 00000000 00000000
e278 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
e298 00000000 00000000 3aea2c28 ffff8008 3aea2e28 ffff8008 00000000 00000000
X19: 0xffff8008361c8f80:
8f80 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
8fa0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
8fc0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
8fe0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
9000 3ab9f410 ffff8008 3bf6b000 ffff8008 095c4e48 ffff0000 361c9378 ffff8008
9020 360ed558 ffff8008 3ab9f420 ffff8008 3a7d5800 ffff8008 09d9fa00 ffff0000
9040 361b94c8 ffff8008 00000006 00000003 00000000 00000000 00000000 00000000
9060 00000000 00000000 00000000 00000000 361c9070 ffff8008 361c9070 ffff8008
X20: 0xffff8008361c9738:
9738 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
9758 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
9778 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
9798 0000000e 00000000 71a71600 ffff8000 00000000 00000000 00000000 00000000
97b8 044c044c 00000002 00000000 00000000 00000001 00000000 3ab9f410 ffff8008
97d8 00000001 00000000 3bf6cc70 ffff8008 3bf6c170 ffff8008 00000000 00000000
97f8 00000000 00000000 361c9800 ffff8008 361c9800 ffff8008 089b1dc8 ffff0000
9818 71a71600 ffff8000 00000000 00000000 00000000 00000000 00000000 00000000
X28: 0xffff80083bf55380:
5380 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
53a0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
53c0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
53e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
5400 00000022 00000000 ffffffff ffffffff 00010001 00000000 00000000 00000000
5420 0ffe8000 ffff0000 00000006 00404100 00000000 00000000 00000000 00000000
5440 00000001 00000000 000000e6 00000000 ffff3a2d 00000000 30c10000 ffff8008
5460 00000000 00000001 00000062 00000070 00000062 00000001 09023e38 ffff0000
Process surfaceflinger (pid: 3226, stack limit = 0xffff00000ffe8000)
Call trace:
Exception stack(0xffff000008003bd0 to 0xffff000008003d10)
3bc0: ffff8008361c9300 ffff00000a85e000
3be0: 0000000000000000 00000000000001c0 0000000000000000 ffff80083ae40600
3c00: ffff800838000248 0000000000000000 ffff800838000248 ffff8008380ae238
3c20: 0000000000000040 0000000000000040 0000000000010000 0000000000000000
3c40: 0000000000000000 0000ffffccae15f0 ffff0000082b54f0 0000fe6e544aee34
3c60: 0000000000000001 ffff8008361c9000 ffff8008361c97b8 0000000000000001
3c80: ffff00000a85e000 ffff000008003f24 ffff000009c6a288 ffff000009873018
3ca0: ffff000008004000 ffff000009873018 ffff80083bf55400 ffff000008003d10
3cc0: ffff0000089b0e74 ffff000008003d10 ffff0000089b0e88 00000000000001c5
3ce0: 00000000000001c0 1a8824534cfb2000 0000ffffffffffff ffff0000080f87c8
3d00: ffff000008003d10 ffff0000089b0e88
[<ffff0000089b0e88>] cdns_get_setup_ret+0x38/0x64
[<ffff0000089b2a48>] cdns_check_ep0_interrupt_proceed+0xc0/0x8dc
[<ffff0000089b3508>] cdns_irq_handler_thread+0x2a4/0x4e8
[<ffff0000089afd98>] cdns3_irq+0x44/0x94
[<ffff000008145994>] __handle_irq_event_percpu+0x60/0x24c
[<ffff000008145c54>] handle_irq_event+0x58/0xc0
[<ffff000008149784>] handle_fasteoi_irq+0x98/0x180
[<ffff000008144a58>] generic_handle_irq+0x24/0x38
[<ffff0000081451b8>] __handle_domain_irq+0x60/0xac
[<ffff0000080819c4>] gic_handle_irq+0xd4/0x17c
Signed-off-by: Peter Chen <peter.chen@nxp.com>
(cherry picked from commit 6ee6e32b0c3fcbea70117eaea3c315f0e220144c)
2019-03-06 02:12:29 -07:00
|
|
|
|
2019-07-31 20:36:54 -06:00
|
|
|
priv_dev->status_completion_no_call = 0;
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev->onchip_mem_allocated_size = 0;
|
|
|
|
priv_dev->out_mem_is_allocated = 0;
|
|
|
|
priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
|
2019-07-29 00:34:18 -06:00
|
|
|
list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
|
|
|
|
priv_ep = ep_to_cdns3_ep(ep);
|
|
|
|
priv_ep->flags &= ~EP_CLAIMED;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
|
|
|
if (!priv_dev->start_gadget)
|
|
|
|
return ret;
|
2017-12-27 02:00:22 -07:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
|
|
|
|
priv_ep = ep_to_cdns3_ep(ep);
|
2019-07-29 00:34:18 -06:00
|
|
|
usb_ep_disable(ep);
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_free_trb_pool(priv_ep);
|
2017-12-27 02:00:22 -07:00
|
|
|
}
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
/* disable interrupt for device */
|
2019-06-19 02:06:40 -06:00
|
|
|
writel(0, &priv_dev->regs->usb_ien);
|
2020-04-29 03:08:15 -06:00
|
|
|
writel(0, &priv_dev->regs->usb_pwr);
|
2019-06-19 02:06:40 -06:00
|
|
|
writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
|
2017-08-21 01:00:05 -06:00
|
|
|
|
2018-05-07 02:38:43 -06:00
|
|
|
return ret;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static const struct usb_gadget_ops cdns3_gadget_ops = {
|
|
|
|
.get_frame = cdns3_gadget_get_frame,
|
|
|
|
.wakeup = cdns3_gadget_wakeup,
|
|
|
|
.set_selfpowered = cdns3_gadget_set_selfpowered,
|
|
|
|
.pullup = cdns3_gadget_pullup,
|
|
|
|
.udc_start = cdns3_gadget_udc_start,
|
|
|
|
.udc_stop = cdns3_gadget_udc_stop,
|
|
|
|
.match_ep = cdns3_gadget_match_ep,
|
2017-07-24 19:39:40 -06:00
|
|
|
};
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*ep0 OUT point to ep0 IN*/
|
|
|
|
priv_dev->eps[16] = NULL;
|
|
|
|
|
|
|
|
cdns3_free_trb_pool(priv_dev->eps[0]);
|
|
|
|
|
|
|
|
for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
|
|
|
|
if (priv_dev->eps[i])
|
|
|
|
devm_kfree(priv_dev->dev, priv_dev->eps[i]);
|
|
|
|
}
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
/**
|
2019-06-19 02:06:40 -06:00
|
|
|
* cdns3_init_eps Initializes software endpoints of gadget
|
|
|
|
* @cdns3: extended gadget object
|
2017-07-24 19:39:40 -06:00
|
|
|
*
|
|
|
|
* Returns 0 on success, error code elsewhere
|
|
|
|
*/
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_init_eps(struct cdns3_device *priv_dev)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
u32 ep_enabled_reg, iso_ep_reg;
|
|
|
|
struct cdns3_endpoint *priv_ep;
|
|
|
|
int ep_dir, ep_number;
|
|
|
|
u32 ep_mask;
|
|
|
|
int ret = 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
int i;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* Read it from USB_CAP3 and USB_CAP4 */
|
2017-07-24 19:39:40 -06:00
|
|
|
ep_enabled_reg = 0x00ff00ff;
|
|
|
|
iso_ep_reg = 0x00fe00fe;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
|
|
|
|
ep_dir = i >> 4; /* i div 16 */
|
|
|
|
ep_number = i & 0xF; /* i % 16 */
|
|
|
|
ep_mask = BIT(i);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (!(ep_enabled_reg & ep_mask))
|
2017-07-24 19:39:40 -06:00
|
|
|
continue;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (ep_dir && !ep_number) {
|
|
|
|
priv_dev->eps[i] = priv_dev->eps[0];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!priv_ep) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
|
|
|
/* set parent of endpoint object */
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->cdns3_dev = priv_dev;
|
|
|
|
priv_dev->eps[i] = priv_ep;
|
|
|
|
priv_ep->num = ep_number;
|
|
|
|
priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT;
|
|
|
|
|
|
|
|
if (!ep_number) {
|
|
|
|
ret = cdns3_init_ep0(priv_dev, priv_ep);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(priv_dev->dev, "Failed to init ep0\n");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s",
|
|
|
|
ep_number, !!ep_dir ? "in" : "out");
|
|
|
|
priv_ep->endpoint.name = priv_ep->name;
|
|
|
|
|
|
|
|
usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
|
|
|
|
CDNS3_EP_MAX_PACKET_LIMIT);
|
|
|
|
priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS;
|
|
|
|
priv_ep->endpoint.ops = &cdns3_gadget_ep_ops;
|
|
|
|
if (ep_dir)
|
|
|
|
priv_ep->endpoint.caps.dir_in = 1;
|
|
|
|
else
|
|
|
|
priv_ep->endpoint.caps.dir_out = 1;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (iso_ep_reg & ep_mask)
|
|
|
|
priv_ep->endpoint.caps.type_iso = 1;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->endpoint.caps.type_bulk = 1;
|
|
|
|
priv_ep->endpoint.caps.type_int = 1;
|
|
|
|
|
|
|
|
list_add_tail(&priv_ep->endpoint.ep_list,
|
|
|
|
&priv_dev->gadget.ep_list);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_ep->flags = 0;
|
|
|
|
|
|
|
|
dev_dbg(priv_dev->dev, "Initialized %s support: %s %s\n",
|
|
|
|
priv_ep->name,
|
|
|
|
priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
|
|
|
|
priv_ep->endpoint.caps.type_iso ? "ISO" : "");
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&priv_ep->pending_req_list);
|
|
|
|
INIT_LIST_HEAD(&priv_ep->deferred_req_list);
|
|
|
|
INIT_LIST_HEAD(&priv_ep->descmiss_req_list);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
2018-05-16 00:52:29 -06:00
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
return 0;
|
2019-06-19 02:06:40 -06:00
|
|
|
err:
|
|
|
|
cdns3_free_all_eps(priv_dev);
|
|
|
|
return -ENOMEM;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
void cdns3_gadget_exit(struct cdns3 *cdns)
|
|
|
|
{
|
|
|
|
struct cdns3_device *priv_dev;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev = cdns->gadget_dev;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
pm_runtime_mark_last_busy(cdns->dev);
|
|
|
|
pm_runtime_put_autosuspend(cdns->dev);
|
|
|
|
|
|
|
|
usb_del_gadget_udc(&priv_dev->gadget);
|
|
|
|
|
|
|
|
cdns3_free_all_eps(priv_dev);
|
|
|
|
|
|
|
|
while (!list_empty(&priv_dev->aligned_buf_list)) {
|
|
|
|
struct cdns3_aligned_buf *buf;
|
|
|
|
|
|
|
|
buf = cdns3_next_align_buf(&priv_dev->aligned_buf_list);
|
|
|
|
|
|
|
|
dma_free_coherent(priv_dev->sysdev, buf->size,
|
|
|
|
buf->buf,
|
|
|
|
buf->dma);
|
|
|
|
|
|
|
|
list_del(&buf->list);
|
|
|
|
kfree(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
|
|
|
|
priv_dev->setup_dma);
|
|
|
|
|
|
|
|
kfree(priv_dev->zlp_buf);
|
|
|
|
kfree(priv_dev);
|
|
|
|
cdns->gadget_dev = NULL;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
static int cdns3_gadget_start(struct cdns3 *cdns)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = cdns->gadget_dev;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
pm_runtime_get_sync(cdns->dev);
|
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
|
|
|
priv_dev->start_gadget = 1;
|
|
|
|
if (!priv_dev->gadget_driver) {
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cdns3_gadget_config(priv_dev);
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
return 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static int __cdns3_gadget_init(struct cdns3 *cdns)
|
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev;
|
|
|
|
u32 max_speed;
|
|
|
|
int ret = 0;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
|
|
|
|
if (!priv_dev)
|
2017-07-24 19:39:40 -06:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns->gadget_dev = priv_dev;
|
|
|
|
priv_dev->sysdev = cdns->dev;
|
|
|
|
priv_dev->dev = cdns->dev;
|
|
|
|
priv_dev->regs = cdns->dev_regs;
|
|
|
|
|
|
|
|
max_speed = usb_get_maximum_speed(cdns->dev);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* Check the maximum_speed parameter */
|
|
|
|
switch (max_speed) {
|
|
|
|
case USB_SPEED_FULL:
|
|
|
|
case USB_SPEED_HIGH:
|
|
|
|
case USB_SPEED_SUPER:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
|
|
|
|
max_speed);
|
|
|
|
/* fall through */
|
|
|
|
case USB_SPEED_UNKNOWN:
|
|
|
|
/* default to superspeed */
|
|
|
|
max_speed = USB_SPEED_SUPER;
|
|
|
|
break;
|
|
|
|
}
|
2017-07-24 19:39:40 -06:00
|
|
|
|
|
|
|
/* fill gadget fields */
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev->gadget.max_speed = max_speed;
|
|
|
|
priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
|
|
|
|
priv_dev->gadget.ops = &cdns3_gadget_ops;
|
|
|
|
priv_dev->gadget.name = "usb-ss-gadget";
|
|
|
|
priv_dev->gadget.sg_supported = 1;
|
|
|
|
priv_dev->gadget.quirk_avoids_skb_reserve = 1;
|
2020-05-06 04:14:51 -06:00
|
|
|
priv_dev->gadget.irq = cdns->irq;
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
spin_lock_init(&priv_dev->lock);
|
|
|
|
INIT_WORK(&priv_dev->pending_status_wq,
|
|
|
|
cdns3_pending_setup_status_handler);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
|
|
|
/* initialize endpoint container */
|
2019-06-19 02:06:40 -06:00
|
|
|
INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
|
|
|
|
INIT_LIST_HEAD(&priv_dev->aligned_buf_list);
|
2017-07-24 19:39:40 -06:00
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
pm_runtime_get_sync(cdns->dev);
|
|
|
|
ret = cdns3_init_eps(priv_dev);
|
2017-07-24 19:39:40 -06:00
|
|
|
if (ret) {
|
2019-06-19 02:06:40 -06:00
|
|
|
dev_err(priv_dev->dev, "Failed to create endpoints\n");
|
|
|
|
goto err1;
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
/* allocate memory for setup packet buffer */
|
|
|
|
priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
|
|
|
|
&priv_dev->setup_dma, GFP_DMA);
|
|
|
|
if (!priv_dev->setup_buf) {
|
|
|
|
dev_err(priv_dev->dev, "Failed to allocate memory for SETUP buffer\n");
|
2017-07-24 19:39:40 -06:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err2;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
|
|
|
|
if (!priv_dev->zlp_buf) {
|
2017-07-24 19:39:40 -06:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add USB gadget device */
|
2019-06-19 02:06:40 -06:00
|
|
|
ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
|
2017-07-24 19:39:40 -06:00
|
|
|
if (ret < 0) {
|
2019-06-19 02:06:40 -06:00
|
|
|
dev_err(priv_dev->dev,
|
|
|
|
"Failed to register USB device controller\n");
|
2017-07-24 19:39:40 -06:00
|
|
|
goto err4;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
if (ret)
|
2018-06-21 04:35:46 -06:00
|
|
|
goto err4;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
|
|
|
|
pm_runtime_put_sync(cdns->dev);
|
2017-07-24 19:39:40 -06:00
|
|
|
return 0;
|
|
|
|
err4:
|
2019-06-19 02:06:40 -06:00
|
|
|
kfree(priv_dev->zlp_buf);
|
2017-07-24 19:39:40 -06:00
|
|
|
err3:
|
2019-06-19 02:06:40 -06:00
|
|
|
dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
|
|
|
|
priv_dev->setup_dma);
|
2017-07-24 19:39:40 -06:00
|
|
|
err2:
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_free_all_eps(priv_dev);
|
2017-07-24 19:39:40 -06:00
|
|
|
err1:
|
|
|
|
cdns->gadget_dev = NULL;
|
2019-06-19 02:06:40 -06:00
|
|
|
pm_runtime_put_sync(cdns->dev);
|
2017-07-24 19:39:40 -06:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-11-30 23:13:06 -07:00
|
|
|
static void __cdns3_gadget_stop(struct cdns3 *cdns)
|
2017-07-24 19:39:40 -06:00
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = cdns->gadget_dev;
|
2017-07-24 19:39:40 -06:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/* disable interrupt for device */
|
2019-06-19 02:06:40 -06:00
|
|
|
writel(0, &priv_dev->regs->usb_ien);
|
2020-04-29 19:37:59 -06:00
|
|
|
if (priv_dev->gadget_driver)
|
|
|
|
usb_gadget_disconnect(&priv_dev->gadget);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
|
|
|
usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
|
2019-06-19 02:06:40 -06:00
|
|
|
priv_dev->start_gadget = 0;
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
2017-07-24 19:39:40 -06:00
|
|
|
}
|
|
|
|
|
2017-11-30 23:13:06 -07:00
|
|
|
static void cdns3_gadget_stop(struct cdns3 *cdns)
|
|
|
|
{
|
|
|
|
if (cdns->role == CDNS3_ROLE_GADGET)
|
|
|
|
__cdns3_gadget_stop(cdns);
|
|
|
|
pm_runtime_mark_last_busy(cdns->dev);
|
|
|
|
pm_runtime_put_autosuspend(cdns->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
|
|
|
|
{
|
|
|
|
__cdns3_gadget_stop(cdns);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
|
|
|
|
{
|
2019-06-19 02:06:40 -06:00
|
|
|
struct cdns3_device *priv_dev = cdns->gadget_dev;
|
2017-11-30 23:13:06 -07:00
|
|
|
unsigned long flags;
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
spin_lock_irqsave(&priv_dev->lock, flags);
|
|
|
|
priv_dev->start_gadget = 1;
|
|
|
|
|
|
|
|
if (!priv_dev->gadget_driver) {
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
2017-11-30 23:13:06 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-19 02:06:40 -06:00
|
|
|
cdns3_gadget_config(priv_dev);
|
|
|
|
spin_unlock_irqrestore(&priv_dev->lock, flags);
|
2017-11-30 23:13:06 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
/**
|
|
|
|
* cdns3_gadget_init - initialize device structure
|
|
|
|
*
|
|
|
|
* cdns: cdns3 instance
|
|
|
|
*
|
|
|
|
* This function initializes the gadget.
|
|
|
|
*/
|
|
|
|
int cdns3_gadget_init(struct cdns3 *cdns)
|
|
|
|
{
|
|
|
|
struct cdns3_role_driver *rdrv;
|
|
|
|
|
|
|
|
rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
|
|
|
|
if (!rdrv)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rdrv->start = cdns3_gadget_start;
|
|
|
|
rdrv->stop = cdns3_gadget_stop;
|
2017-11-30 23:13:06 -07:00
|
|
|
rdrv->suspend = cdns3_gadget_suspend;
|
|
|
|
rdrv->resume = cdns3_gadget_resume;
|
2019-06-19 02:06:40 -06:00
|
|
|
rdrv->irq = cdns3_device_irq_handler;
|
|
|
|
rdrv->thread_irq = cdns3_device_thread_irq_handler;
|
2017-07-24 19:39:40 -06:00
|
|
|
rdrv->name = "gadget";
|
|
|
|
cdns->roles[CDNS3_ROLE_GADGET] = rdrv;
|
2019-06-19 02:06:40 -06:00
|
|
|
|
2017-07-24 19:39:40 -06:00
|
|
|
return __cdns3_gadget_init(cdns);
|
|
|
|
}
|