Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/usb/dwc3/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 127 kB image not shown  

Quelle  gadget.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link
 *
 * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
 *
 * Authors: Felipe Balbi <balbi@ti.com>,
 *     Sebastian Andrzej Siewior <bigeasy@linutronix.de>
 */


#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>

#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>

#include "debug.h"
#include "core.h"
#include "gadget.h"
#include "io.h"

#define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \
     & ~((d)->interval - 1))

/**
 * dwc3_gadget_set_test_mode - enables usb2 test modes
 * @dwc: pointer to our context structure
 * @mode: the mode to set (J, K SE0 NAK, Force Enable)
 *
 * Caller should take care of locking. This function will return 0 on
 * success or -EINVAL if wrong Test Selector is passed.
 */

int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
{
 u32  reg;

 reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 reg &= ~DWC3_DCTL_TSTCTRL_MASK;

 switch (mode) {
 case USB_TEST_J:
 case USB_TEST_K:
 case USB_TEST_SE0_NAK:
 case USB_TEST_PACKET:
 case USB_TEST_FORCE_ENABLE:
  reg |= mode << 1;
  break;
 default:
  return -EINVAL;
 }

 dwc3_gadget_dctl_write_safe(dwc, reg);

 return 0;
}

/**
 * dwc3_gadget_get_link_state - gets current state of usb link
 * @dwc: pointer to our context structure
 *
 * Caller should take care of locking. This function will
 * return the link state on success (>= 0) or -ETIMEDOUT.
 */

int dwc3_gadget_get_link_state(struct dwc3 *dwc)
{
 u32  reg;

 reg = dwc3_readl(dwc->regs, DWC3_DSTS);

 return DWC3_DSTS_USBLNKST(reg);
}

/**
 * dwc3_gadget_set_link_state - sets usb link to a particular state
 * @dwc: pointer to our context structure
 * @state: the state to put link into
 *
 * Caller should take care of locking. This function will
 * return 0 on success or -ETIMEDOUT.
 */

int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
{
 int  retries = 10000;
 u32  reg;

 /*
 * Wait until device controller is ready. Only applies to 1.94a and
 * later RTL.
 */

 if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) {
  while (--retries) {
   reg = dwc3_readl(dwc->regs, DWC3_DSTS);
   if (reg & DWC3_DSTS_DCNRD)
    udelay(5);
   else
    break;
  }

  if (retries <= 0)
   return -ETIMEDOUT;
 }

 reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;

 /* set no action before sending new link state change */
 dwc3_writel(dwc->regs, DWC3_DCTL, reg);

 /* set requested state */
 reg |= DWC3_DCTL_ULSTCHNGREQ(state);
 dwc3_writel(dwc->regs, DWC3_DCTL, reg);

 /*
 * The following code is racy when called from dwc3_gadget_wakeup,
 * and is not needed, at least on newer versions
 */

 if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
  return 0;

 /* wait for a change in DSTS */
 retries = 10000;
 while (--retries) {
  reg = dwc3_readl(dwc->regs, DWC3_DSTS);

  if (DWC3_DSTS_USBLNKST(reg) == state)
   return 0;

  udelay(5);
 }

 return -ETIMEDOUT;
}

static void dwc3_ep0_reset_state(struct dwc3 *dwc)
{
 unsigned int dir;

 if (dwc->ep0state != EP0_SETUP_PHASE) {
  dir = !!dwc->ep0_expect_in;
  if (dwc->ep0state == EP0_DATA_PHASE)
   dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
  else
   dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);

  dwc->eps[0]->trb_enqueue = 0;
  dwc->eps[1]->trb_enqueue = 0;

  dwc3_ep0_stall_and_restart(dwc);
 }
}

/**
 * dwc3_ep_inc_trb - increment a trb index.
 * @index: Pointer to the TRB index to increment.
 *
 * The index should never point to the link TRB. After incrementing,
 * if it is point to the link TRB, wrap around to the beginning. The
 * link TRB is always at the last TRB entry.
 */

static void dwc3_ep_inc_trb(u8 *index)
{
 (*index)++;
 if (*index == (DWC3_TRB_NUM - 1))
  *index = 0;
}

/**
 * dwc3_ep_inc_enq - increment endpoint's enqueue pointer
 * @dep: The endpoint whose enqueue pointer we're incrementing
 */

static void dwc3_ep_inc_enq(struct dwc3_ep *dep)
{
 dwc3_ep_inc_trb(&dep->trb_enqueue);
}

/**
 * dwc3_ep_inc_deq - increment endpoint's dequeue pointer
 * @dep: The endpoint whose enqueue pointer we're incrementing
 */

static void dwc3_ep_inc_deq(struct dwc3_ep *dep)
{
 dwc3_ep_inc_trb(&dep->trb_dequeue);
}

static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
  struct dwc3_request *req, int status)
{
 struct dwc3   *dwc = dep->dwc;

 list_del(&req->list);
 req->remaining = 0;
 req->num_trbs = 0;

 if (req->request.status == -EINPROGRESS)
  req->request.status = status;

 if (req->trb)
  usb_gadget_unmap_request_by_dev(dwc->sysdev,
    &req->request, req->direction);

 req->trb = NULL;
 trace_dwc3_gadget_giveback(req);

 if (dep->number > 1)
  pm_runtime_put(dwc->dev);
}

/**
 * dwc3_gadget_giveback - call struct usb_request's ->complete callback
 * @dep: The endpoint to whom the request belongs to
 * @req: The request we're giving back
 * @status: completion code for the request
 *
 * 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.
 */

void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
  int status)
{
 struct dwc3   *dwc = dep->dwc;

 dwc3_gadget_del_and_unmap_request(dep, req, status);
 req->status = DWC3_REQUEST_STATUS_COMPLETED;

 spin_unlock(&dwc->lock);
 usb_gadget_giveback_request(&dep->endpoint, &req->request);
 spin_lock(&dwc->lock);
}

/**
 * dwc3_send_gadget_generic_command - issue a generic command for the controller
 * @dwc: pointer to the controller context
 * @cmd: the command to be issued
 * @param: command parameter
 *
 * Caller should take care of locking. Issue @cmd with a given @param to @dwc
 * and wait for its completion.
 */

int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
  u32 param)
{
 u32  timeout = 500;
 int  status = 0;
 int  ret = 0;
 u32  reg;

 dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
 dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);

 do {
  reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
  if (!(reg & DWC3_DGCMD_CMDACT)) {
   status = DWC3_DGCMD_STATUS(reg);
   if (status)
    ret = -EINVAL;
   break;
  }
 } while (--timeout);

 if (!timeout) {
  ret = -ETIMEDOUT;
  status = -ETIMEDOUT;
 }

 trace_dwc3_gadget_generic_cmd(cmd, param, status);

 return ret;
}

/**
 * dwc3_send_gadget_ep_cmd - issue an endpoint command
 * @dep: the endpoint to which the command is going to be issued
 * @cmd: the command to be issued
 * @params: parameters to the command
 *
 * Caller should handle locking. This function will issue @cmd with given
 * @params to @dep and wait for its completion.
 *
 * According to the programming guide, if the link state is in L1/L2/U3,
 * then sending the Start Transfer command may not complete. The
 * programming guide suggested to bring the link state back to ON/U0 by
 * performing remote wakeup prior to sending the command. However, don't
 * initiate remote wakeup when the user/function does not send wakeup
 * request via wakeup ops. Send the command when it's allowed.
 *
 * Notes:
 * For L1 link state, issuing a command requires the clearing of
 * GUSB2PHYCFG.SUSPENDUSB2, which turns on the signal required to complete
 * the given command (usually within 50us). This should happen within the
 * command timeout set by driver. No additional step is needed.
 *
 * For L2 or U3 link state, the gadget is in USB suspend. Care should be
 * taken when sending Start Transfer command to ensure that it's done after
 * USB resume.
 */

int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
  struct dwc3_gadget_ep_cmd_params *params)
{
 const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
 struct dwc3  *dwc = dep->dwc;
 u32   timeout = 5000;
 u32   saved_config = 0;
 u32   reg;

 int   cmd_status = 0;
 int   ret = -EINVAL;

 /*
 * When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or
 * GUSB2PHYCFG.SUSPHY is set, it must be cleared before issuing an
 * endpoint command.
 *
 * Save and clear both GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY
 * settings. Restore them after the command is completed.
 *
 * DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
 */

 if (dwc->gadget->speed <= USB_SPEED_HIGH ||
     DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) {
  reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
  if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
   saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
   reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
  }

  if (reg & DWC3_GUSB2PHYCFG_ENBLSLPM) {
   saved_config |= DWC3_GUSB2PHYCFG_ENBLSLPM;
   reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
  }

  if (saved_config)
   dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 }

 /*
 * For some commands such as Update Transfer command, DEPCMDPARn
 * registers are reserved. Since the driver often sends Update Transfer
 * command, don't write to DEPCMDPARn to avoid register write delays and
 * improve performance.
 */

 if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_UPDATETRANSFER) {
  dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
  dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
  dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
 }

 /*
 * Synopsys Databook 2.60a states in section 6.3.2.5.6 of that if we're
 * not relying on XferNotReady, we can make use of a special "No
 * Response Update Transfer" command where we should clear both CmdAct
 * and CmdIOC bits.
 *
 * With this, we don't need to wait for command completion and can
 * straight away issue further commands to the endpoint.
 *
 * NOTICE: We're making an assumption that control endpoints will never
 * make use of Update Transfer command. This is a safe assumption
 * because we can never have more than one request at a time with
 * Control Endpoints. If anybody changes that assumption, this chunk
 * needs to be updated accordingly.
 */

 if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_UPDATETRANSFER &&
   !usb_endpoint_xfer_isoc(desc))
  cmd &= ~(DWC3_DEPCMD_CMDIOC | DWC3_DEPCMD_CMDACT);
 else
  cmd |= DWC3_DEPCMD_CMDACT;

 dwc3_writel(dep->regs, DWC3_DEPCMD, cmd);

 if (!(cmd & DWC3_DEPCMD_CMDACT) ||
  (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER &&
  !(cmd & DWC3_DEPCMD_CMDIOC))) {
  ret = 0;
  goto skip_status;
 }

 do {
  reg = dwc3_readl(dep->regs, DWC3_DEPCMD);
  if (!(reg & DWC3_DEPCMD_CMDACT)) {
   cmd_status = DWC3_DEPCMD_STATUS(reg);

   switch (cmd_status) {
   case 0:
    ret = 0;
    break;
   case DEPEVT_TRANSFER_NO_RESOURCE:
    dev_WARN(dwc->dev, "No resource for %s\n",
      dep->name);
    ret = -EINVAL;
    break;
   case DEPEVT_TRANSFER_BUS_EXPIRY:
    /*
 * SW issues START TRANSFER command to
 * isochronous ep with future frame interval. If
 * future interval time has already passed when
 * core receives the command, it will respond
 * with an error status of 'Bus Expiry'.
 *
 * Instead of always returning -EINVAL, let's
 * give a hint to the gadget driver that this is
 * the case by returning -EAGAIN.
 */

    ret = -EAGAIN;
    break;
   default:
    dev_WARN(dwc->dev, "UNKNOWN cmd status\n");
   }

   break;
  }
 } while (--timeout);

 if (timeout == 0) {
  ret = -ETIMEDOUT;
  cmd_status = -ETIMEDOUT;
 }

skip_status:
 trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);

 if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
  if (ret == 0)
   dep->flags |= DWC3_EP_TRANSFER_STARTED;

  if (ret != -ETIMEDOUT)
   dwc3_gadget_ep_get_transfer_index(dep);
 }

 if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER &&
     !(cmd & DWC3_DEPCMD_CMDIOC))
  mdelay(1);

 if (saved_config) {
  reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
  reg |= saved_config;
  dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 }

 return ret;
}

static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
{
 struct dwc3 *dwc = dep->dwc;
 struct dwc3_gadget_ep_cmd_params params;
 u32 cmd = DWC3_DEPCMD_CLEARSTALL;

 /*
 * As of core revision 2.60a the recommended programming model
 * is to set the ClearPendIN bit when issuing a Clear Stall EP
 * command for IN endpoints. This is to prevent an issue where
 * some (non-compliant) hosts may not send ACK TPs for pending
 * IN transfers due to a mishandled error condition. Synopsys
 * STAR 9000614252.
 */

 if (dep->direction &&
     !DWC3_VER_IS_PRIOR(DWC3, 260A) &&
     (dwc->gadget->speed >= USB_SPEED_SUPER))
  cmd |= DWC3_DEPCMD_CLEARPENDIN;

 memset(¶ms, 0, sizeof(params));

 return dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
}

static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
  struct dwc3_trb *trb)
{
 u32  offset = (char *) trb - (char *) dep->trb_pool;

 return dep->trb_pool_dma + offset;
}

static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
{
 struct dwc3  *dwc = dep->dwc;

 if (dep->trb_pool)
  return 0;

 dep->trb_pool = dma_alloc_coherent(dwc->sysdev,
   sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
   &dep->trb_pool_dma, GFP_KERNEL);
 if (!dep->trb_pool) {
  dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n",
    dep->name);
  return -ENOMEM;
 }

 return 0;
}

static void dwc3_free_trb_pool(struct dwc3_ep *dep)
{
 struct dwc3  *dwc = dep->dwc;

 dma_free_coherent(dwc->sysdev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
   dep->trb_pool, dep->trb_pool_dma);

 dep->trb_pool = NULL;
 dep->trb_pool_dma = 0;
}

static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
{
 struct dwc3_gadget_ep_cmd_params params;
 int ret;

 if (dep->flags & DWC3_EP_RESOURCE_ALLOCATED)
  return 0;

 memset(¶ms, 0x00, sizeof(params));

 params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);

 ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
   ¶ms);
 if (ret)
  return ret;

 dep->flags |= DWC3_EP_RESOURCE_ALLOCATED;
 return 0;
}

/**
 * dwc3_gadget_start_config - reset endpoint resources
 * @dwc: pointer to the DWC3 context
 * @resource_index: DEPSTARTCFG.XferRscIdx value (must be 0 or 2)
 *
 * Set resource_index=0 to reset all endpoints' resources allocation. Do this as
 * part of the power-on/soft-reset initialization.
 *
 * Set resource_index=2 to reset only non-control endpoints' resources. Do this
 * on receiving the SET_CONFIGURATION request or hibernation resume.
 */

int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index)
{
 struct dwc3_gadget_ep_cmd_params params;
 struct dwc3_ep  *dep;
 u32   cmd;
 int   i;
 int   ret;

 if (resource_index != 0 && resource_index != 2)
  return -EINVAL;

 memset(¶ms, 0x00, sizeof(params));
 cmd = DWC3_DEPCMD_DEPSTARTCFG;
 cmd |= DWC3_DEPCMD_PARAM(resource_index);

 ret = dwc3_send_gadget_ep_cmd(dwc->eps[0], cmd, ¶ms);
 if (ret)
  return ret;

 /* Reset resource allocation flags */
 for (i = resource_index; i < dwc->num_eps; i++) {
  dep = dwc->eps[i];
  if (!dep)
   continue;

  dep->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
 }

 return 0;
}

static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
{
 const struct usb_ss_ep_comp_descriptor *comp_desc;
 const struct usb_endpoint_descriptor *desc;
 struct dwc3_gadget_ep_cmd_params params;
 struct dwc3 *dwc = dep->dwc;

 comp_desc = dep->endpoint.comp_desc;
 desc = dep->endpoint.desc;

 memset(¶ms, 0x00, sizeof(params));

 params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
  | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));

 /* Burst size is only needed in SuperSpeed mode */
 if (dwc->gadget->speed >= USB_SPEED_SUPER) {
  u32 burst = dep->endpoint.maxburst;

  params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
 }

 params.param0 |= action;
 if (action == DWC3_DEPCFG_ACTION_RESTORE)
  params.param2 |= dep->saved_state;

 if (usb_endpoint_xfer_control(desc))
  params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;

 if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc))
  params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN;

 if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
  params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
   | DWC3_DEPCFG_XFER_COMPLETE_EN
   | DWC3_DEPCFG_STREAM_EVENT_EN;
  dep->stream_capable = true;
 }

 if (!usb_endpoint_xfer_control(desc))
  params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN;

 /*
 * We are doing 1:1 mapping for endpoints, meaning
 * Physical Endpoints 2 maps to Logical Endpoint 2 and
 * so on. We consider the direction bit as part of the physical
 * endpoint number. So USB endpoint 0x81 is 0x03.
 */

 params.param1 |= DWC3_DEPCFG_EP_NUMBER(dep->number);

 /*
 * We must use the lower 16 TX FIFOs even though
 * HW might have more
 */

 if (dep->direction)
  params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1);

 if (desc->bInterval) {
  u8 bInterval_m1;

  /*
 * Valid range for DEPCFG.bInterval_m1 is from 0 to 13.
 *
 * NOTE: The programming guide incorrectly stated bInterval_m1
 * must be set to 0 when operating in fullspeed. Internally the
 * controller does not have this limitation. See DWC_usb3x
 * programming guide section 3.2.2.1.
 */

  bInterval_m1 = min_t(u8, desc->bInterval - 1, 13);

  if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
      dwc->gadget->speed == USB_SPEED_FULL)
   dep->interval = desc->bInterval;
  else
   dep->interval = 1 << (desc->bInterval - 1);

  params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(bInterval_m1);
 }

 return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms);
}

/**
 * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
 * @dwc: pointer to the DWC3 context
 * @mult: multiplier to be used when calculating the fifo_size
 *
 * Calculates the size value based on the equation below:
 *
 * DWC3 revision 280A and prior:
 * fifo_size = mult * (max_packet / mdwidth) + 1;
 *
 * DWC3 revision 290A and onwards:
 * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
 *
 * The max packet size is set to 1024, as the txfifo requirements mainly apply
 * to super speed USB use cases.  However, it is safe to overestimate the fifo
 * allocations for other scenarios, i.e. high speed USB.
 */

static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult)
{
 int max_packet = 1024;
 int fifo_size;
 int mdwidth;

 mdwidth = dwc3_mdwidth(dwc);

 /* MDWIDTH is represented in bits, we need it in bytes */
 mdwidth >>= 3;

 if (DWC3_VER_IS_PRIOR(DWC3, 290A))
  fifo_size = mult * (max_packet / mdwidth) + 1;
 else
  fifo_size = mult * ((max_packet + mdwidth) / mdwidth) + 1;
 return fifo_size;
}

/**
 * dwc3_gadget_calc_ram_depth - calculates the ram depth for txfifo
 * @dwc: pointer to the DWC3 context
 */

static int dwc3_gadget_calc_ram_depth(struct dwc3 *dwc)
{
 int ram_depth;
 int fifo_0_start;
 bool is_single_port_ram;

 /* Check supporting RAM type by HW */
 is_single_port_ram = DWC3_SPRAM_TYPE(dwc->hwparams.hwparams1);

 /*
 * If a single port RAM is utilized, then allocate TxFIFOs from
 * RAM0. otherwise, allocate them from RAM1.
 */

 ram_depth = is_single_port_ram ? DWC3_RAM0_DEPTH(dwc->hwparams.hwparams6) :
   DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);

 /*
 * In a single port RAM configuration, the available RAM is shared
 * between the RX and TX FIFOs. This means that the txfifo can begin
 * at a non-zero address.
 */

 if (is_single_port_ram) {
  u32 reg;

  /* Check if TXFIFOs start at non-zero addr */
  reg = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
  fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(reg);

  ram_depth -= (fifo_0_start >> 16);
 }

 return ram_depth;
}

/**
 * dwc3_gadget_clear_tx_fifos - Clears txfifo allocation
 * @dwc: pointer to the DWC3 context
 *
 * Iterates through all the endpoint registers and clears the previous txfifo
 * allocations.
 */

void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
{
 struct dwc3_ep *dep;
 int fifo_depth;
 int size;
 int num;

 if (!dwc->do_fifo_resize)
  return;

 /* Read ep0IN related TXFIFO size */
 dep = dwc->eps[1];
 size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
 if (DWC3_IP_IS(DWC3))
  fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size);
 else
  fifo_depth = DWC31_GTXFIFOSIZ_TXFDEP(size);

 dwc->last_fifo_depth = fifo_depth;
 /* Clear existing TXFIFO for all IN eps except ep0 */
 for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM); num += 2) {
  dep = dwc->eps[num];
  if (!dep)
   continue;

  /* Don't change TXFRAMNUM on usb31 version */
  size = DWC3_IP_IS(DWC3) ? 0 :
   dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
       DWC31_GTXFIFOSIZ_TXFRAMNUM;

  dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
  dep->flags &= ~DWC3_EP_TXFIFO_RESIZED;
 }
 dwc->num_ep_resized = 0;
}

/*
 * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
 * @dwc: pointer to our context structure
 *
 * This function will a best effort FIFO allocation in order
 * to improve FIFO usage and throughput, while still allowing
 * us to enable as many endpoints as possible.
 *
 * Keep in mind that this operation will be highly dependent
 * on the configured size for RAM1 - which contains TxFifo -,
 * the amount of endpoints enabled on coreConsultant tool, and
 * the width of the Master Bus.
 *
 * In general, FIFO depths are represented with the following equation:
 *
 * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
 *
 * In conjunction with dwc3_gadget_check_config(), this resizing logic will
 * ensure that all endpoints will have enough internal memory for one max
 * packet per endpoint.
 */

static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
{
 struct dwc3 *dwc = dep->dwc;
 int fifo_0_start;
 int ram_depth;
 int fifo_size;
 int min_depth;
 int num_in_ep;
 int remaining;
 int num_fifos = 1;
 int fifo;
 int tmp;

 if (!dwc->do_fifo_resize)
  return 0;

 /* resize IN endpoints except ep0 */
 if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1)
  return 0;

 /* bail if already resized */
 if (dep->flags & DWC3_EP_TXFIFO_RESIZED)
  return 0;

 ram_depth = dwc3_gadget_calc_ram_depth(dwc);

 switch (dwc->gadget->speed) {
 case USB_SPEED_SUPER_PLUS:
 case USB_SPEED_SUPER:
  if (usb_endpoint_xfer_bulk(dep->endpoint.desc) ||
      usb_endpoint_xfer_isoc(dep->endpoint.desc))
   num_fifos = min_t(unsigned int,
       dep->endpoint.maxburst,
       dwc->tx_fifo_resize_max_num);
  break;
 case USB_SPEED_HIGH:
  if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
   num_fifos = min_t(unsigned int,
       usb_endpoint_maxp_mult(dep->endpoint.desc) + 1,
       dwc->tx_fifo_resize_max_num);
   break;
  }
  fallthrough;
 case USB_SPEED_FULL:
  if (usb_endpoint_xfer_bulk(dep->endpoint.desc))
   num_fifos = 2;
  break;
 default:
  break;
 }

 /* FIFO size for a single buffer */
 fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1);

 /* Calculate the number of remaining EPs w/o any FIFO */
 num_in_ep = dwc->max_cfg_eps;
 num_in_ep -= dwc->num_ep_resized;

 /* Reserve at least one FIFO for the number of IN EPs */
 min_depth = num_in_ep * (fifo + 1);
 remaining = ram_depth - min_depth - dwc->last_fifo_depth;
 remaining = max_t(int, 0, remaining);
 /*
 * We've already reserved 1 FIFO per EP, so check what we can fit in
 * addition to it.  If there is not enough remaining space, allocate
 * all the remaining space to the EP.
 */

 fifo_size = (num_fifos - 1) * fifo;
 if (remaining < fifo_size)
  fifo_size = remaining;

 fifo_size += fifo;
 /* Last increment according to the TX FIFO size equation */
 fifo_size++;

 /* Check if TXFIFOs start at non-zero addr */
 tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
 fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp);

 fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16));
 if (DWC3_IP_IS(DWC3))
  dwc->last_fifo_depth += DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
 else
  dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);

 /* Check fifo size allocation doesn't exceed available RAM size. */
 if (dwc->last_fifo_depth >= ram_depth) {
  dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n",
   dwc->last_fifo_depth, ram_depth,
   dep->endpoint.name, fifo_size);
  if (DWC3_IP_IS(DWC3))
   fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
  else
   fifo_size = DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);

  dwc->last_fifo_depth -= fifo_size;
  return -ENOMEM;
 }

 dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
 dep->flags |= DWC3_EP_TXFIFO_RESIZED;
 dwc->num_ep_resized++;

 return 0;
}

/**
 * __dwc3_gadget_ep_enable - initializes a hw endpoint
 * @dep: endpoint to be initialized
 * @action: one of INIT, MODIFY or RESTORE
 *
 * Caller should take care of locking. Execute all necessary commands to
 * initialize a HW endpoint so it can be used by a gadget driver.
 */

static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
{
 const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
 struct dwc3  *dwc = dep->dwc;

 u32   reg;
 int   ret;

 if (!(dep->flags & DWC3_EP_ENABLED)) {
  ret = dwc3_gadget_resize_tx_fifos(dep);
  if (ret)
   return ret;
 }

 ret = dwc3_gadget_set_ep_config(dep, action);
 if (ret)
  return ret;

 ret = dwc3_gadget_set_xfer_resource(dep);
 if (ret)
  return ret;

 if (!(dep->flags & DWC3_EP_ENABLED)) {
  struct dwc3_trb *trb_st_hw;
  struct dwc3_trb *trb_link;

  dep->type = usb_endpoint_type(desc);
  dep->flags |= DWC3_EP_ENABLED;

  reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
  reg |= DWC3_DALEPENA_EP(dep->number);
  dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);

  dep->trb_dequeue = 0;
  dep->trb_enqueue = 0;

  if (usb_endpoint_xfer_control(desc))
   goto out;

  /* Initialize the TRB ring */
  memset(dep->trb_pool, 0,
         sizeof(struct dwc3_trb) * DWC3_TRB_NUM);

  /* Link TRB. The HWO bit is never reset */
  trb_st_hw = &dep->trb_pool[0];

  trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1];
  trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
  trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
  trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB;
  trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
 }

 /*
 * Issue StartTransfer here with no-op TRB so we can always rely on No
 * Response Update Transfer command.
 */

 if (usb_endpoint_xfer_bulk(desc) ||
   usb_endpoint_xfer_int(desc)) {
  struct dwc3_gadget_ep_cmd_params params;
  struct dwc3_trb *trb;
  dma_addr_t trb_dma;
  u32 cmd;

  memset(¶ms, 0, sizeof(params));
  trb = &dep->trb_pool[0];
  trb_dma = dwc3_trb_dma_offset(dep, trb);

  params.param0 = upper_32_bits(trb_dma);
  params.param1 = lower_32_bits(trb_dma);

  cmd = DWC3_DEPCMD_STARTTRANSFER;

  ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
  if (ret < 0)
   return ret;

  if (dep->stream_capable) {
   /*
 * For streams, at start, there maybe a race where the
 * host primes the endpoint before the function driver
 * queues a request to initiate a stream. In that case,
 * the controller will not see the prime to generate the
 * ERDY and start stream. To workaround this, issue a
 * no-op TRB as normal, but end it immediately. As a
 * result, when the function driver queues the request,
 * the next START_TRANSFER command will cause the
 * controller to generate an ERDY to initiate the
 * stream.
 */

   dwc3_stop_active_transfer(dep, truetrue);

   /*
 * All stream eps will reinitiate stream on NoStream
 * rejection.
 *
 * However, if the controller is capable of
 * TXF_FLUSH_BYPASS, then IN direction endpoints will
 * automatically restart the stream without the driver
 * initiation.
 */

   if (!dep->direction ||
       !(dwc->hwparams.hwparams9 &
         DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS))
    dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
  }
 }

out:
 trace_dwc3_gadget_ep_enable(dep);

 return 0;
}

void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status)
{
 struct dwc3_request  *req;

 dwc3_stop_active_transfer(dep, truefalse);

 /* If endxfer is delayed, avoid unmapping requests */
 if (dep->flags & DWC3_EP_DELAY_STOP)
  return;

 /* - giveback all requests to gadget driver */
 while (!list_empty(&dep->started_list)) {
  req = next_request(&dep->started_list);

  dwc3_gadget_giveback(dep, req, status);
 }

 while (!list_empty(&dep->pending_list)) {
  req = next_request(&dep->pending_list);

  dwc3_gadget_giveback(dep, req, status);
 }

 while (!list_empty(&dep->cancelled_list)) {
  req = next_request(&dep->cancelled_list);

  dwc3_gadget_giveback(dep, req, status);
 }
}

/**
 * __dwc3_gadget_ep_disable - disables a hw endpoint
 * @dep: the endpoint to disable
 *
 * This function undoes what __dwc3_gadget_ep_enable did and also removes
 * requests which are currently being processed by the hardware and those which
 * are not yet scheduled.
 *
 * Caller should take care of locking.
 */

static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
{
 struct dwc3  *dwc = dep->dwc;
 u32   reg;
 u32   mask;

 trace_dwc3_gadget_ep_disable(dep);

 /* make sure HW endpoint isn't stalled */
 if (dep->flags & DWC3_EP_STALL)
  __dwc3_gadget_ep_set_halt(dep, 0, false);

 reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
 reg &= ~DWC3_DALEPENA_EP(dep->number);
 dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);

 dwc3_remove_requests(dwc, dep, -ESHUTDOWN);

 dep->stream_capable = false;
 dep->type = 0;
 mask = DWC3_EP_TXFIFO_RESIZED | DWC3_EP_RESOURCE_ALLOCATED;
 /*
 * dwc3_remove_requests() can exit early if DWC3 EP delayed stop is
 * set.  Do not clear DEP flags, so that the end transfer command will
 * be reattempted during the next SETUP stage.
 */

 if (dep->flags & DWC3_EP_DELAY_STOP)
  mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
 dep->flags &= mask;

 /* Clear out the ep descriptors for non-ep0 */
 if (dep->number > 1) {
  dep->endpoint.comp_desc = NULL;
  dep->endpoint.desc = NULL;
 }

 return 0;
}

/* -------------------------------------------------------------------------- */

static int dwc3_gadget_ep0_enable(struct usb_ep *ep,
  const struct usb_endpoint_descriptor *desc)
{
 return -EINVAL;
}

static int dwc3_gadget_ep0_disable(struct usb_ep *ep)
{
 return -EINVAL;
}

/* -------------------------------------------------------------------------- */

static int dwc3_gadget_ep_enable(struct usb_ep *ep,
  const struct usb_endpoint_descriptor *desc)
{
 struct dwc3_ep   *dep;
 struct dwc3   *dwc;
 unsigned long   flags;
 int    ret;

 if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
  pr_debug("dwc3: invalid parameters\n");
  return -EINVAL;
 }

 if (!desc->wMaxPacketSize) {
  pr_debug("dwc3: missing wMaxPacketSize\n");
  return -EINVAL;
 }

 dep = to_dwc3_ep(ep);
 dwc = dep->dwc;

 if (dev_WARN_ONCE(dwc->dev, dep->flags & DWC3_EP_ENABLED,
     "%s is already enabled\n",
     dep->name))
  return 0;

 spin_lock_irqsave(&dwc->lock, flags);
 ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
 spin_unlock_irqrestore(&dwc->lock, flags);

 return ret;
}

static int dwc3_gadget_ep_disable(struct usb_ep *ep)
{
 struct dwc3_ep   *dep;
 struct dwc3   *dwc;
 unsigned long   flags;
 int    ret;

 if (!ep) {
  pr_debug("dwc3: invalid parameters\n");
  return -EINVAL;
 }

 dep = to_dwc3_ep(ep);
 dwc = dep->dwc;

 if (dev_WARN_ONCE(dwc->dev, !(dep->flags & DWC3_EP_ENABLED),
     "%s is already disabled\n",
     dep->name))
  return 0;

 spin_lock_irqsave(&dwc->lock, flags);
 ret = __dwc3_gadget_ep_disable(dep);
 spin_unlock_irqrestore(&dwc->lock, flags);

 return ret;
}

static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
  gfp_t gfp_flags)
{
 struct dwc3_request  *req;
 struct dwc3_ep   *dep = to_dwc3_ep(ep);

 req = kzalloc(sizeof(*req), gfp_flags);
 if (!req)
  return NULL;

 req->direction = dep->direction;
 req->epnum = dep->number;
 req->dep = dep;
 req->status = DWC3_REQUEST_STATUS_UNKNOWN;

 trace_dwc3_alloc_request(req);

 return &req->request;
}

static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
  struct usb_request *request)
{
 struct dwc3_request  *req = to_dwc3_request(request);

 trace_dwc3_free_request(req);
 kfree(req);
}

/**
 * dwc3_ep_prev_trb - returns the previous TRB in the ring
 * @dep: The endpoint with the TRB ring
 * @index: The index of the current TRB in the ring
 *
 * Returns the TRB prior to the one pointed to by the index. If the
 * index is 0, we will wrap backwards, skip the link TRB, and return
 * the one just before that.
 */

static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
{
 u8 tmp = index;

 if (!tmp)
  tmp = DWC3_TRB_NUM - 1;

 return &dep->trb_pool[tmp - 1];
}

static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
{
 u8   trbs_left;

 /*
 * If the enqueue & dequeue are equal then the TRB ring is either full
 * or empty. It's considered full when there are DWC3_TRB_NUM-1 of TRBs
 * pending to be processed by the driver.
 */

 if (dep->trb_enqueue == dep->trb_dequeue) {
  struct dwc3_request *req;

  /*
 * If there is any request remained in the started_list with
 * active TRBs at this point, then there is no TRB available.
 */

  req = next_request(&dep->started_list);
  if (req && req->num_trbs)
   return 0;

  return DWC3_TRB_NUM - 1;
 }

 trbs_left = dep->trb_dequeue - dep->trb_enqueue;
 trbs_left &= (DWC3_TRB_NUM - 1);

 if (dep->trb_dequeue < dep->trb_enqueue)
  trbs_left--;

 return trbs_left;
}

/**
 * dwc3_prepare_one_trb - setup one TRB from one request
 * @dep: endpoint for which this request is prepared
 * @req: dwc3_request pointer
 * @trb_length: buffer size of the TRB
 * @chain: should this TRB be chained to the next?
 * @node: only for isochronous endpoints. First TRB needs different type.
 * @use_bounce_buffer: set to use bounce buffer
 * @must_interrupt: set to interrupt on TRB completion
 */

static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
  struct dwc3_request *req, unsigned int trb_length,
  unsigned int chain, unsigned int node, bool use_bounce_buffer,
  bool must_interrupt)
{
 struct dwc3_trb  *trb;
 dma_addr_t  dma;
 unsigned int  stream_id = req->request.stream_id;
 unsigned int  short_not_ok = req->request.short_not_ok;
 unsigned int  no_interrupt = req->request.no_interrupt;
 unsigned int  is_last = req->request.is_last;
 struct dwc3  *dwc = dep->dwc;
 struct usb_gadget *gadget = dwc->gadget;
 enum usb_device_speed speed = gadget->speed;

 if (use_bounce_buffer)
  dma = dep->dwc->bounce_addr;
 else if (req->request.num_sgs > 0)
  dma = sg_dma_address(req->start_sg);
 else
  dma = req->request.dma;

 trb = &dep->trb_pool[dep->trb_enqueue];

 if (!req->trb) {
  dwc3_gadget_move_started_request(req);
  req->trb = trb;
  req->trb_dma = dwc3_trb_dma_offset(dep, trb);
 }

 req->num_trbs++;

 trb->size = DWC3_TRB_SIZE_LENGTH(trb_length);
 trb->bpl = lower_32_bits(dma);
 trb->bph = upper_32_bits(dma);

 switch (usb_endpoint_type(dep->endpoint.desc)) {
 case USB_ENDPOINT_XFER_CONTROL:
  trb->ctrl = DWC3_TRBCTL_CONTROL_SETUP;
  break;

 case USB_ENDPOINT_XFER_ISOC:
  if (!node) {
   trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;

   /*
 * USB Specification 2.0 Section 5.9.2 states that: "If
 * there is only a single transaction in the microframe,
 * only a DATA0 data packet PID is used.  If there are
 * two transactions per microframe, DATA1 is used for
 * the first transaction data packet and DATA0 is used
 * for the second transaction data packet.  If there are
 * three transactions per microframe, DATA2 is used for
 * the first transaction data packet, DATA1 is used for
 * the second, and DATA0 is used for the third."
 *
 * IOW, we should satisfy the following cases:
 *
 * 1) length <= maxpacket
 * - DATA0
 *
 * 2) maxpacket < length <= (2 * maxpacket)
 * - DATA1, DATA0
 *
 * 3) (2 * maxpacket) < length <= (3 * maxpacket)
 * - DATA2, DATA1, DATA0
 */

   if (speed == USB_SPEED_HIGH) {
    struct usb_ep *ep = &dep->endpoint;
    unsigned int mult = 2;
    unsigned int maxp = usb_endpoint_maxp(ep->desc);

    if (req->request.length <= (2 * maxp))
     mult--;

    if (req->request.length <= maxp)
     mult--;

    trb->size |= DWC3_TRB_SIZE_PCM1(mult);
   }
  } else {
   trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
  }

  if (!no_interrupt && !chain)
   trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
  break;

 case USB_ENDPOINT_XFER_BULK:
 case USB_ENDPOINT_XFER_INT:
  trb->ctrl = DWC3_TRBCTL_NORMAL;
  break;
 default:
  /*
 * This is only possible with faulty memory because we
 * checked it already :)
 */

  dev_WARN(dwc->dev, "Unknown endpoint type %d\n",
    usb_endpoint_type(dep->endpoint.desc));
 }

 /*
 * Enable Continue on Short Packet
 * when endpoint is not a stream capable
 */

 if (usb_endpoint_dir_out(dep->endpoint.desc)) {
  if (!dep->stream_capable)
   trb->ctrl |= DWC3_TRB_CTRL_CSP;

  if (short_not_ok)
   trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
 }

 /* All TRBs setup for MST must set CSP=1 when LST=0 */
 if (dep->stream_capable && DWC3_MST_CAPABLE(&dwc->hwparams))
  trb->ctrl |= DWC3_TRB_CTRL_CSP;

 if ((!no_interrupt && !chain) || must_interrupt)
  trb->ctrl |= DWC3_TRB_CTRL_IOC;

 if (chain)
  trb->ctrl |= DWC3_TRB_CTRL_CHN;
 else if (dep->stream_capable && is_last &&
   !DWC3_MST_CAPABLE(&dwc->hwparams))
  trb->ctrl |= DWC3_TRB_CTRL_LST;

 if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
  trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);

 /*
 * As per data book 4.2.3.2TRB Control Bit Rules section
 *
 * The controller autonomously checks the HWO field of a TRB to determine if the
 * entire TRB is valid. Therefore, software must ensure that the rest of the TRB
 * is valid before setting the HWO field to '1'. In most systems, this means that
 * software must update the fourth DWORD of a TRB last.
 *
 * However there is a possibility of CPU re-ordering here which can cause
 * controller to observe the HWO bit set prematurely.
 * Add a write memory barrier to prevent CPU re-ordering.
 */

 wmb();
 trb->ctrl |= DWC3_TRB_CTRL_HWO;

 dwc3_ep_inc_enq(dep);

 trace_dwc3_prepare_trb(dep, trb);
}

static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req)
{
 unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
 unsigned int rem = req->request.length % maxp;

 if ((req->request.length && req->request.zero && !rem &&
   !usb_endpoint_xfer_isoc(dep->endpoint.desc)) ||
   (!req->direction && rem))
  return true;

 return false;
}

/**
 * dwc3_prepare_last_sg - prepare TRBs for the last SG entry
 * @dep: The endpoint that the request belongs to
 * @req: The request to prepare
 * @entry_length: The last SG entry size
 * @node: Indicates whether this is not the first entry (for isoc only)
 *
 * Return the number of TRBs prepared.
 */

static int dwc3_prepare_last_sg(struct dwc3_ep *dep,
  struct dwc3_request *req, unsigned int entry_length,
  unsigned int node)
{
 unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
 unsigned int rem = req->request.length % maxp;
 unsigned int num_trbs = 1;
 bool needs_extra_trb;

 if (dwc3_needs_extra_trb(dep, req))
  num_trbs++;

 if (dwc3_calc_trbs_left(dep) < num_trbs)
  return 0;

 needs_extra_trb = num_trbs > 1;

 /* Prepare a normal TRB */
 if (req->direction || req->request.length)
  dwc3_prepare_one_trb(dep, req, entry_length,
    needs_extra_trb, node, falsefalse);

 /* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */
 if ((!req->direction && !req->request.length) || needs_extra_trb)
  dwc3_prepare_one_trb(dep, req,
    req->direction ? 0 : maxp - rem,
    false, 1, truefalse);

 return num_trbs;
}

static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
  struct dwc3_request *req)
{
 struct scatterlist *sg = req->start_sg;
 struct scatterlist *s;
 int  i;
 unsigned int length = req->request.length;
 unsigned int remaining = req->num_pending_sgs;
 unsigned int num_queued_sgs = req->request.num_mapped_sgs - remaining;
 unsigned int num_trbs = req->num_trbs;
 bool needs_extra_trb = dwc3_needs_extra_trb(dep, req);

 /*
 * If we resume preparing the request, then get the remaining length of
 * the request and resume where we left off.
 */

 for_each_sg(req->request.sg, s, num_queued_sgs, i)
  length -= sg_dma_len(s);

 for_each_sg(sg, s, remaining, i) {
  unsigned int num_trbs_left = dwc3_calc_trbs_left(dep);
  unsigned int trb_length;
  bool must_interrupt = false;
  bool last_sg = false;

  trb_length = min_t(unsigned int, length, sg_dma_len(s));

  length -= trb_length;

  /*
 * IOMMU driver is coalescing the list of sgs which shares a
 * page boundary into one and giving it to USB driver. With
 * this the number of sgs mapped is not equal to the number of
 * sgs passed. So mark the chain bit to false if it isthe last
 * mapped sg.
 */

  if ((i == remaining - 1) || !length)
   last_sg = true;

  if (!num_trbs_left)
   break;

  if (last_sg) {
   if (!dwc3_prepare_last_sg(dep, req, trb_length, i))
    break;
  } else {
   /*
 * Look ahead to check if we have enough TRBs for the
 * next SG entry. If not, set interrupt on this TRB to
 * resume preparing the next SG entry when more TRBs are
 * free.
 */

   if (num_trbs_left == 1 || (needs_extra_trb &&
     num_trbs_left <= 2 &&
     sg_dma_len(sg_next(s)) >= length)) {
    struct dwc3_request *r;

    /* Check if previous requests already set IOC */
    list_for_each_entry(r, &dep->started_list, list) {
     if (r != req && !r->request.no_interrupt)
      break;

     if (r == req)
      must_interrupt = true;
    }
   }

   dwc3_prepare_one_trb(dep, req, trb_length, 1, i, false,
     must_interrupt);
  }

  /*
 * There can be a situation where all sgs in sglist are not
 * queued because of insufficient trb number. To handle this
 * case, update start_sg to next sg to be queued, so that
 * we have free trbs we can continue queuing from where we
 * previously stopped
 */

  if (!last_sg)
   req->start_sg = sg_next(s);

  req->num_pending_sgs--;

  /*
 * The number of pending SG entries may not correspond to the
 * number of mapped SG entries. If all the data are queued, then
 * don't include unused SG entries.
 */

  if (length == 0) {
   req->num_pending_sgs = 0;
   break;
  }

  if (must_interrupt)
   break;
 }

 return req->num_trbs - num_trbs;
}

static int dwc3_prepare_trbs_linear(struct dwc3_ep *dep,
  struct dwc3_request *req)
{
 return dwc3_prepare_last_sg(dep, req, req->request.length, 0);
}

/*
 * dwc3_prepare_trbs - setup TRBs from requests
 * @dep: endpoint for which requests are being prepared
 *
 * The function goes through the requests list and sets up TRBs for the
 * transfers. The function returns once there are no more TRBs available or
 * it runs out of requests.
 *
 * Returns the number of TRBs prepared or negative errno.
 */

static int dwc3_prepare_trbs(struct dwc3_ep *dep)
{
 struct dwc3_request *req, *n;
 int   ret = 0;

 BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);

 /*
 * We can get in a situation where there's a request in the started list
 * but there weren't enough TRBs to fully kick it in the first time
 * around, so it has been waiting for more TRBs to be freed up.
 *
 * In that case, we should check if we have a request with pending_sgs
 * in the started list and prepare TRBs for that request first,
 * otherwise we will prepare TRBs completely out of order and that will
 * break things.
 */

 list_for_each_entry(req, &dep->started_list, list) {
  if (req->num_pending_sgs > 0) {
   ret = dwc3_prepare_trbs_sg(dep, req);
   if (!ret || req->num_pending_sgs)
    return ret;
  }

  if (!dwc3_calc_trbs_left(dep))
   return ret;

  /*
 * Don't prepare beyond a transfer. In DWC_usb32, its transfer
 * burst capability may try to read and use TRBs beyond the
 * active transfer instead of stopping.
 */

  if (dep->stream_capable && req->request.is_last &&
      !DWC3_MST_CAPABLE(&dep->dwc->hwparams))
   return ret;
 }

 list_for_each_entry_safe(req, n, &dep->pending_list, list) {
  struct dwc3 *dwc = dep->dwc;

  ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request,
          dep->direction);
  if (ret)
   return ret;

  req->start_sg  = req->request.sg;
  req->num_pending_sgs = req->request.num_mapped_sgs;

  if (req->num_pending_sgs > 0) {
   ret = dwc3_prepare_trbs_sg(dep, req);
   if (req->num_pending_sgs)
    return ret;
  } else {
   ret = dwc3_prepare_trbs_linear(dep, req);
  }

  if (!ret || !dwc3_calc_trbs_left(dep))
   return ret;

  /*
 * Don't prepare beyond a transfer. In DWC_usb32, its transfer
 * burst capability may try to read and use TRBs beyond the
 * active transfer instead of stopping.
 */

  if (dep->stream_capable && req->request.is_last &&
      !DWC3_MST_CAPABLE(&dwc->hwparams))
   return ret;
 }

 return ret;
}

static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep);

static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
{
 struct dwc3_gadget_ep_cmd_params params;
 struct dwc3_request  *req;
 int    starting;
 int    ret;
 u32    cmd;

 /*
 * Note that it's normal to have no new TRBs prepared (i.e. ret == 0).
 * This happens when we need to stop and restart a transfer such as in
 * the case of reinitiating a stream or retrying an isoc transfer.
 */

 ret = dwc3_prepare_trbs(dep);
 if (ret < 0)
  return ret;

 starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED);

 /*
 * If there's no new TRB prepared and we don't need to restart a
 * transfer, there's no need to update the transfer.
 */

 if (!ret && !starting)
  return ret;

 req = next_request(&dep->started_list);
 if (!req) {
  dep->flags |= DWC3_EP_PENDING_REQUEST;
  return 0;
 }

 memset(¶ms, 0, sizeof(params));

 if (starting) {
  params.param0 = upper_32_bits(req->trb_dma);
  params.param1 = lower_32_bits(req->trb_dma);
  cmd = DWC3_DEPCMD_STARTTRANSFER;

  if (dep->stream_capable)
   cmd |= DWC3_DEPCMD_PARAM(req->request.stream_id);

  if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
   cmd |= DWC3_DEPCMD_PARAM(dep->frame_number);
 } else {
  cmd = DWC3_DEPCMD_UPDATETRANSFER |
   DWC3_DEPCMD_PARAM(dep->resource_index);
 }

 ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
 if (ret < 0) {
  struct dwc3_request *tmp;

  if (ret == -EAGAIN)
   return ret;

  dwc3_stop_active_transfer(dep, truetrue);

  list_for_each_entry_safe(req, tmp, &dep->started_list, list)
   dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED);

  /* If ep isn't started, then there's no end transfer pending */
  if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
   dwc3_gadget_ep_cleanup_cancelled_requests(dep);

  return ret;
 }

 if (dep->stream_capable && req->request.is_last &&
     !DWC3_MST_CAPABLE(&dep->dwc->hwparams))
  dep->flags |= DWC3_EP_WAIT_TRANSFER_COMPLETE;

 return 0;
}

static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
{
 u32   reg;

 reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 return DWC3_DSTS_SOFFN(reg);
}

/**
 * __dwc3_stop_active_transfer - stop the current active transfer
 * @dep: isoc endpoint
 * @force: set forcerm bit in the command
 * @interrupt: command complete interrupt after End Transfer command
 *
 * When setting force, the ForceRM bit will be set. In that case
 * the controller won't update the TRB progress on command
 * completion. It also won't clear the HWO bit in the TRB.
 * The command will also not complete immediately in that case.
 */

static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
{
 struct dwc3_gadget_ep_cmd_params params;
 u32 cmd;
 int ret;

 cmd = DWC3_DEPCMD_ENDTRANSFER;
 cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
 cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
 cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
 memset(¶ms, 0, sizeof(params));
 ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
 /*
 * If the End Transfer command was timed out while the device is
 * not in SETUP phase, it's possible that an incoming Setup packet
 * may prevent the command's completion. Let's retry when the
 * ep0state returns to EP0_SETUP_PHASE.
 */

 if (ret == -ETIMEDOUT && dep->dwc->ep0state != EP0_SETUP_PHASE) {
  dep->flags |= DWC3_EP_DELAY_STOP;
  return 0;
 }

 if (ret)
  dev_err_ratelimited(dep->dwc->dev,
    "end transfer failed: %d\n", ret);

 dep->resource_index = 0;

 if (!interrupt)
  dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
 else if (!ret)
  dep->flags |= DWC3_EP_END_TRANSFER_PENDING;

 dep->flags &= ~DWC3_EP_DELAY_STOP;
 return ret;
}

/**
 * dwc3_gadget_start_isoc_quirk - workaround invalid frame number
 * @dep: isoc endpoint
 *
 * This function tests for the correct combination of BIT[15:14] from the 16-bit
 * microframe number reported by the XferNotReady event for the future frame
 * number to start the isoc transfer.
 *
 * In DWC_usb31 version 1.70a-ea06 and prior, for highspeed and fullspeed
 * isochronous IN, BIT[15:14] of the 16-bit microframe number reported by the
 * XferNotReady event are invalid. The driver uses this number to schedule the
 * isochronous transfer and passes it to the START TRANSFER command. Because
 * this number is invalid, the command may fail. If BIT[15:14] matches the
 * internal 16-bit microframe, the START TRANSFER command will pass and the
 * transfer will start at the scheduled time, if it is off by 1, the command
 * will still pass, but the transfer will start 2 seconds in the future. For all
 * other conditions, the START TRANSFER command will fail with bus-expiry.
 *
 * In order to workaround this issue, we can test for the correct combination of
 * BIT[15:14] by sending START TRANSFER commands with different values of
 * BIT[15:14]: 'b00, 'b01, 'b10, and 'b11. Each combination is 2^14 uframe apart
 * (or 2 seconds). 4 seconds into the future will result in a bus-expiry status.
 * As the result, within the 4 possible combinations for BIT[15:14], there will
 * be 2 successful and 2 failure START COMMAND status. One of the 2 successful
 * command status will result in a 2-second delay start. The smaller BIT[15:14]
 * value is the correct combination.
 *
 * Since there are only 4 outcomes and the results are ordered, we can simply
 * test 2 START TRANSFER commands with BIT[15:14] combinations 'b00 and 'b01 to
 * deduce the smaller successful combination.
 *
 * Let test0 = test status for combination 'b00 and test1 = test status for 'b01
 * of BIT[15:14]. The correct combination is as follow:
 *
 * if test0 fails and test1 passes, BIT[15:14] is 'b01
 * if test0 fails and test1 fails, BIT[15:14] is 'b10
 * if test0 passes and test1 fails, BIT[15:14] is 'b11
 * if test0 passes and test1 passes, BIT[15:14] is 'b00
 *
 * Synopsys STAR 9001202023: Wrong microframe number for isochronous IN
 * endpoints.
 */

static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
{
 int cmd_status = 0;
 bool test0;
 bool test1;

 while (dep->combo_num < 2) {
  struct dwc3_gadget_ep_cmd_params params;
  u32 test_frame_number;
  u32 cmd;

  /*
 * Check if we can start isoc transfer on the next interval or
 * 4 uframes in the future with BIT[15:14] as dep->combo_num
 */

  test_frame_number = dep->frame_number & DWC3_FRNUMBER_MASK;
  test_frame_number |= dep->combo_num << 14;
  test_frame_number += max_t(u32, 4, dep->interval);

  params.param0 = upper_32_bits(dep->dwc->bounce_addr);
  params.param1 = lower_32_bits(dep->dwc->bounce_addr);

  cmd = DWC3_DEPCMD_STARTTRANSFER;
  cmd |= DWC3_DEPCMD_PARAM(test_frame_number);
  cmd_status = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);

  /* Redo if some other failure beside bus-expiry is received */
  if (cmd_status && cmd_status != -EAGAIN) {
   dep->start_cmd_status = 0;
   dep->combo_num = 0;
   return 0;
  }

  /* Store the first test status */
  if (dep->combo_num == 0)
   dep->start_cmd_status = cmd_status;

  dep->combo_num++;

  /*
 * End the transfer if the START_TRANSFER command is successful
 * to wait for the next XferNotReady to test the command again
 */

  if (cmd_status == 0) {
   dwc3_stop_active_transfer(dep, truetrue);
   return 0;
  }
 }

 /* test0 and test1 are both completed at this point */
 test0 = (dep->start_cmd_status == 0);
 test1 = (cmd_status == 0);

 if (!test0 && test1)
  dep->combo_num = 1;
 else if (!test0 && !test1)
  dep->combo_num = 2;
 else if (test0 && !test1)
  dep->combo_num = 3;
 else if (test0 && test1)
  dep->combo_num = 0;

 dep->frame_number &= DWC3_FRNUMBER_MASK;
 dep->frame_number |= dep->combo_num << 14;
 dep->frame_number += max_t(u32, 4, dep->interval);

 /* Reinitialize test variables */
 dep->start_cmd_status = 0;
 dep->combo_num = 0;

 return __dwc3_gadget_kick_transfer(dep);
}

static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
{
 const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
 struct dwc3 *dwc = dep->dwc;
 int ret;
 int i;

 if (list_empty(&dep->pending_list) &&
     list_empty(&dep->started_list)) {
  dep->flags |= DWC3_EP_PENDING_REQUEST;
  return -EAGAIN;
 }

 if (!dwc->dis_start_transfer_quirk &&
     (DWC3_VER_IS_PRIOR(DWC31, 170A) ||
      DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) {
  if (dwc->gadget->speed <= USB_SPEED_HIGH && dep->direction)
   return dwc3_gadget_start_isoc_quirk(dep);
 }

 if (desc->bInterval <= 14 &&
     dwc->gadget->speed >= USB_SPEED_HIGH) {
  u32 frame = __dwc3_gadget_get_frame(dwc);
  bool rollover = frame <
    (dep->frame_number & DWC3_FRNUMBER_MASK);

  /*
 * frame_number is set from XferNotReady and may be already
 * out of date. DSTS only provides the lower 14 bit of the
 * current frame number. So add the upper two bits of
 * frame_number and handle a possible rollover.
 * This will provide the correct frame_number unless more than
 * rollover has happened since XferNotReady.
 */


  dep->frame_number = (dep->frame_number & ~DWC3_FRNUMBER_MASK) |
         frame;
  if (rollover)
   dep->frame_number += BIT(14);
 }

 for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
  int future_interval = i + 1;

  /* Give the controller at least 500us to schedule transfers */
  if (desc->bInterval < 3)
   future_interval += 3 - desc->bInterval;

  dep->frame_number = DWC3_ALIGN_FRAME(dep, future_interval);

  ret = __dwc3_gadget_kick_transfer(dep);
  if (ret != -EAGAIN)
   break;
 }

 /*
 * After a number of unsuccessful start attempts due to bus-expiry
 * status, issue END_TRANSFER command and retry on the next XferNotReady
 * event.
 */

 if (ret == -EAGAIN)
  ret = __dwc3_stop_active_transfer(dep, falsetrue);

 return ret;
}

static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
 struct dwc3  *dwc = dep->dwc;

 if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
  dev_dbg(dwc->dev, "%s: can't queue to disabled endpoint\n",
    dep->name);
  return -ESHUTDOWN;
 }

 if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
    &req->request, req->dep->name))
  return -EINVAL;

 if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED,
    "%s: request %p already in flight\n",
    dep->name, &req->request))
  return -EINVAL;

 pm_runtime_get(dwc->dev);

 req->request.actual = 0;
 req->request.status = -EINPROGRESS;

 trace_dwc3_ep_queue(req);

 list_add_tail(&req->list, &dep->pending_list);
 req->status = DWC3_REQUEST_STATUS_QUEUED;

 if (dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)
  return 0;

 /*
 * Start the transfer only after the END_TRANSFER is completed
 * and endpoint STALL is cleared.
 */

 if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
     (dep->flags & DWC3_EP_WEDGE) ||
     (dep->flags & DWC3_EP_DELAY_STOP) ||
     (dep->flags & DWC3_EP_STALL)) {
  dep->flags |= DWC3_EP_DELAY_START;
  return 0;
 }

 /*
 * NOTICE: Isochronous endpoints should NEVER be prestarted. We must
 * wait for a XferNotReady event so we will know what's the current
 * (micro-)frame number.
 *
 * Without this trick, we are very, very likely gonna get Bus Expiry
 * errors which will force us issue EndTransfer command.
 */

 if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
  if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
   if ((dep->flags & DWC3_EP_PENDING_REQUEST))
    return __dwc3_gadget_start_isoc(dep);

   return 0;
  }
 }

 __dwc3_gadget_kick_transfer(dep);

 return 0;
}

static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
 gfp_t gfp_flags)
{
 struct dwc3_request  *req = to_dwc3_request(request);
 struct dwc3_ep   *dep = to_dwc3_ep(ep);
 struct dwc3   *dwc = dep->dwc;

 unsigned long   flags;

 int    ret;

 spin_lock_irqsave(&dwc->lock, flags);
 ret = __dwc3_gadget_ep_queue(dep, req);
 spin_unlock_irqrestore(&dwc->lock, flags);

 return ret;
}

static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req)
{
 int i;

 /* If req->trb is not set, then the request has not started */
 if (!req->trb)
  return;

 /*
 * If request was already started, this means we had to
 * stop the transfer. With that we also need to ignore
 * all TRBs used by the request, however TRBs can only
 * be modified after completion of END_TRANSFER
 * command. So what we do here is that we wait for
 * END_TRANSFER completion and only after that, we jump
 * over TRBs by clearing HWO and incrementing dequeue
 * pointer.
 */

 for (i = 0; i < req->num_trbs; i++) {
  struct dwc3_trb *trb;

  trb = &dep->trb_pool[dep->trb_dequeue];
  trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
  dwc3_ep_inc_deq(dep);
 }

 req->num_trbs = 0;
}

static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
{
 struct dwc3_request  *req;
 struct dwc3   *dwc = dep->dwc;

 while (!list_empty(&dep->cancelled_list)) {
  req = next_request(&dep->cancelled_list);
  dwc3_gadget_ep_skip_trbs(dep, req);
  switch (req->status) {
  case DWC3_REQUEST_STATUS_DISCONNECTED:
   dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
   break;
  case DWC3_REQUEST_STATUS_DEQUEUED:
   dwc3_gadget_giveback(dep, req, -ECONNRESET);
   break;
  case DWC3_REQUEST_STATUS_STALLED:
   dwc3_gadget_giveback(dep, req, -EPIPE);
   break;
  default:
   dev_err(dwc->dev, "request cancelled with wrong reason:%d\n", req->status);
   dwc3_gadget_giveback(dep, req, -ECONNRESET);
   break;
  }
  /*
 * The endpoint is disabled, let the dwc3_remove_requests()
 * handle the cleanup.
 */

  if (!dep->endpoint.desc)
   break;
 }
}

static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
  struct usb_request *request)
{
 struct dwc3_request  *req = to_dwc3_request(request);
 struct dwc3_request  *r = NULL;

 struct dwc3_ep   *dep = to_dwc3_ep(ep);
 struct dwc3   *dwc = dep->dwc;

 unsigned long   flags;
 int    ret = 0;

 trace_dwc3_ep_dequeue(req);

 spin_lock_irqsave(&dwc->lock, flags);

 list_for_each_entry(r, &dep->cancelled_list, list) {
  if (r == req)
   goto out;
 }

 list_for_each_entry(r, &dep->pending_list, list) {
  if (r == req) {
   /*
 * Explicitly check for EP0/1 as dequeue for those
 * EPs need to be handled differently.  Control EP
 * only deals with one USB req, and giveback will
 * occur during dwc3_ep0_stall_and_restart().  EP0
 * requests are never added to started_list.
 */

   if (dep->number > 1)
    dwc3_gadget_giveback(dep, req, -ECONNRESET);
   else
    dwc3_ep0_reset_state(dwc);
   goto out;
  }
 }

 list_for_each_entry(r, &dep->started_list, list) {
  if (r == req) {
   struct dwc3_request *t;

   /* wait until it is processed */
   dwc3_stop_active_transfer(dep, truetrue);

   /*
 * Remove any started request if the transfer is
 * cancelled.
 */

   list_for_each_entry_safe(r, t, &dep->started_list, list)
    dwc3_gadget_move_cancelled_request(r,
      DWC3_REQUEST_STATUS_DEQUEUED);

   dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;

   goto out;
  }
 }

 dev_err(dwc->dev, "request %p was not queued to %s\n",
  request, ep->name);
 ret = -EINVAL;
out:
 spin_unlock_irqrestore(&dwc->lock, flags);

 return ret;
}

int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
{
 struct dwc3_gadget_ep_cmd_params params;
 struct dwc3    *dwc = dep->dwc;
 struct dwc3_request   *req;
 struct dwc3_request   *tmp;
 int     ret;

 if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
  dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
  return -EINVAL;
 }

 memset(¶ms, 0x00, sizeof(params));

 if (value) {
  struct dwc3_trb *trb;

  unsigned int transfer_in_flight;
  unsigned int started;

  if (dep->number > 1)
   trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
  else
   trb = &dwc->ep0_trb[dep->trb_enqueue];

  transfer_in_flight = trb->ctrl & DWC3_TRB_CTRL_HWO;
  started = !list_empty(&dep->started_list);

  if (!protocol && ((dep->direction && transfer_in_flight) ||
    (!dep->direction && started))) {
   return -EAGAIN;
  }

  ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETSTALL,
    ¶ms);
  if (ret)
   dev_err(dwc->dev, "failed to set STALL on %s\n",
     dep->name);
  else
   dep->flags |= DWC3_EP_STALL;
 } else {
  /*
 * Don't issue CLEAR_STALL command to control endpoints. The
 * controller automatically clears the STALL when it receives
 * the SETUP token.
 */

  if (dep->number <= 1) {
   dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
   return 0;
  }

  dwc3_stop_active_transfer(dep, truetrue);

  list_for_each_entry_safe(req, tmp, &dep->started_list, list)
   dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED);

  if (dep->flags & DWC3_EP_END_TRANSFER_PENDING ||
      (dep->flags & DWC3_EP_DELAY_STOP)) {
   dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
   if (protocol)
    dwc->clear_stall_protocol = dep->number;

   return 0;
  }

  dwc3_gadget_ep_cleanup_cancelled_requests(dep);

  ret = dwc3_send_clear_stall_ep_cmd(dep);
  if (ret) {
   dev_err(dwc->dev, "failed to clear STALL on %s\n",
     dep->name);
   return ret;
  }

  dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);

  if ((dep->flags & DWC3_EP_DELAY_START) &&
      !usb_endpoint_xfer_isoc(dep->endpoint.desc))
   __dwc3_gadget_kick_transfer(dep);

  dep->flags &= ~DWC3_EP_DELAY_START;
 }

 return ret;
}

static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
{
 struct dwc3_ep   *dep = to_dwc3_ep(ep);
 struct dwc3   *dwc = dep->dwc;

 unsigned long   flags;

 int    ret;

 spin_lock_irqsave(&dwc->lock, flags);
 ret = __dwc3_gadget_ep_set_halt(dep, value, false);
 spin_unlock_irqrestore(&dwc->lock, flags);

 return ret;
}

static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
{
 struct dwc3_ep   *dep = to_dwc3_ep(ep);
 struct dwc3   *dwc = dep->dwc;
 unsigned long   flags;
 int    ret;

 spin_lock_irqsave(&dwc->lock, flags);
 dep->flags |= DWC3_EP_WEDGE;

 if (dep->number == 0 || dep->number == 1)
  ret = __dwc3_gadget_ep0_set_halt(ep, 1);
 else
  ret = __dwc3_gadget_ep_set_halt(dep, 1, false);
 spin_unlock_irqrestore(&dwc->lock, flags);

 return ret;
}

/* -------------------------------------------------------------------------- */

static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = {
 .bLength = USB_DT_ENDPOINT_SIZE,
 .bDescriptorType = USB_DT_ENDPOINT,
 .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
};

static const struct usb_ep_ops dwc3_gadget_ep0_ops = {
 .enable  = dwc3_gadget_ep0_enable,
 .disable = dwc3_gadget_ep0_disable,
 .alloc_request = dwc3_gadget_ep_alloc_request,
 .free_request = dwc3_gadget_ep_free_request,
 .queue  = dwc3_gadget_ep0_queue,
 .dequeue = dwc3_gadget_ep_dequeue,
 .set_halt = dwc3_gadget_ep0_set_halt,
 .set_wedge = dwc3_gadget_ep_set_wedge,
};

static const struct usb_ep_ops dwc3_gadget_ep_ops = {
 .enable  = dwc3_gadget_ep_enable,
 .disable = dwc3_gadget_ep_disable,
 .alloc_request = dwc3_gadget_ep_alloc_request,
 .free_request = dwc3_gadget_ep_free_request,
 .queue  = dwc3_gadget_ep_queue,
 .dequeue = dwc3_gadget_ep_dequeue,
 .set_halt = dwc3_gadget_ep_set_halt,
 .set_wedge = dwc3_gadget_ep_set_wedge,
};

/* -------------------------------------------------------------------------- */

static void dwc3_gadget_enable_linksts_evts(struct dwc3 *dwc, bool set)
{
 u32 reg;

 if (DWC3_VER_IS_PRIOR(DWC3, 250A))
  return;

 reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
 if (set)
  reg |= DWC3_DEVTEN_ULSTCNGEN;
 else
  reg &= ~DWC3_DEVTEN_ULSTCNGEN;

 dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
}

static int dwc3_gadget_get_frame(struct usb_gadget *g)
{
 struct dwc3  *dwc = gadget_to_dwc(g);

 return __dwc3_gadget_get_frame(dwc);
}

static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
{
 int   ret;
 u32   reg;

 u8   link_state;

 /*
 * According to the Databook Remote wakeup request should
 * be issued only when the device is in early suspend state.
 *
 * We can check that via USB Link State bits in DSTS register.
 */

 reg = dwc3_readl(dwc->regs, DWC3_DSTS);

 link_state = DWC3_DSTS_USBLNKST(reg);

 switch (link_state) {
 case DWC3_LINK_STATE_RESET:
 case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */
 case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
 case DWC3_LINK_STATE_U2: /* in HS, means Sleep (L1) */
 case DWC3_LINK_STATE_U1:
 case DWC3_LINK_STATE_RESUME:
  break;
 default:
  return -EINVAL;
 }

 dwc3_gadget_enable_linksts_evts(dwc, true);

 ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
 if (ret < 0) {
  dev_err(dwc->dev, "failed to put link in Recovery\n");
  dwc3_gadget_enable_linksts_evts(dwc, false);
  return ret;
 }

 /* Recent versions do this automatically */
 if (DWC3_VER_IS_PRIOR(DWC3, 194A)) {
  /* write zeroes to Link Change Request */
  reg = dwc3_readl(dwc->regs, DWC3_DCTL);
  reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
  dwc3_writel(dwc->regs, DWC3_DCTL, reg);
 }

 /*
 * Since link status change events are enabled we will receive
 * an U0 event when wakeup is successful.
 */

 return 0;
}

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.58 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.