Add Corigine USB IP Driver for Axiado AX3000 SoC's
USB peripheral (USB 2.0/3.0).
The driver is based on the Corigine USB IP core with
Axiado-specific enhancements including VBUS detection and USB link
stability fixes.
The driver supports both USB 2.0 High-Speed and USB 3.0 SuperSpeed
modes with control, bulk, interrupt, and isochronous transfer types.
Co-developed-by: Krutik Shah <krutikshah@axiado.com>
Signed-off-by: Krutik Shah <krutikshah@axiado.com>
Co-developed-by: Prasad Bolisetty <pbolisetty@axiado.com>
Signed-off-by: Prasad Bolisetty <pbolisetty@axiado.com>
Signed-off-by: Vladimir Moravcevic <vmoravcevic@axiado.com>
---
drivers/usb/gadget/udc/Kconfig | 15 +
drivers/usb/gadget/udc/Makefile | 1 +
drivers/usb/gadget/udc/crg_udc.c | 4522 ++++++++++++++++++++++++++++++++++++++
drivers/usb/gadget/udc/crg_udc.h | 364 +++
4 files changed, 4902 insertions(+)
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 26460340fbc9..b94d113aad99 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -417,6 +417,21 @@ config USB_ASPEED_UDC
dynamically linked module called "aspeed_udc" and force all
gadget drivers to also be dynamically linked.
+config USB_CRG_UDC
+ tristate "AXIADO CORIGINE-based AX3000 Device Controller"
+ depends on ARCH_AXIADO || COMPILE_TEST
+ depends on USB_GADGET
+ help
+ Enables AX3000 USB device controller driver for Axiado
+ SoCs and evaluation boards.
+
+ Based on the Corigine USB IP core driver with Axiado specific
+ enhancements. Supports USB 2.0 (High-Speed) and USB 3.0
+ (SuperSpeed), including control, bulk, interrupt, and
+ isochronous transfers.
+
+ Say "y" to build statically, or "m" to build as a module.
+
source "drivers/usb/gadget/udc/aspeed-vhub/Kconfig"
source "drivers/usb/gadget/udc/cdns2/Kconfig"
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 1b9b1a4f9c57..b17b9c4665a1 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -38,3 +38,4 @@ obj-$(CONFIG_USB_ASPEED_UDC) += aspeed_udc.o
obj-$(CONFIG_USB_BDC_UDC) += bdc/
obj-$(CONFIG_USB_MAX3420_UDC) += max3420_udc.o
obj-$(CONFIG_USB_CDNS2_UDC) += cdns2/
+obj-$(CONFIG_USB_CRG_UDC) += crg_udc.o
diff --git a/drivers/usb/gadget/udc/crg_udc.c b/drivers/usb/gadget/udc/crg_udc.c
new file mode 100644
index 000000000000..701123dc35bc
--- /dev/null
+++ b/drivers/usb/gadget/udc/crg_udc.c
@@ -0,0 +1,4522 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+//
+// Copyright (c) 2019 Corigine Inc.
+// Copyright (c) 2022-2026 Axiado Corporation.
+//
+
+#include <linux/net.h>
+#include <asm/byteorder.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/unaligned.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/irqreturn.h>
+#include <linux/interrupt.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/err.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/scatterlist.h>
+#include "crg_udc.h"
+
+#define INIT_ZERO -1
+#define UDC_FALSE false
+
+#define MAX_PACKET_SIZE 1024
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+#define CRG_ERST_SIZE 1
+#define CRG_EVENT_RING_SIZE 256
+#define CRG_NUM_EP_CX 32
+
+#define TRB_MAX_BUFFER_SIZE 65536
+#define CRGUDC_CONTROL_EP_TD_RING_SIZE 16
+#define CRGUDC_BULK_EP_TD_RING_SIZE 1024
+#define CRGUDC_ISOC_EP_TD_RING_SIZE 32
+#define CRGUDC_INT_EP_TD_RING_SIZE 8
+#define CRGUDC_ROLE_DEVICE 0x1
+
+#define U1_TIMEOUT_VAL 0x70
+#define U2_TIMEOUT_VAL 0x70
+
+#define STATE_USB_LINK_STABLE 4
+
+/*********Feature switches********************/
+#define U12_FORBIDDEN 1
+#define U12_INITIATE_FORBIDDEN 1
+#define CRG_UDC_INT_EN
+#define REINIT_EP0_ON_BUS_RESET
+/*********************************************/
+
+enum EP_STATE_E {
+ EP_STATE_DISABLED = 0,
+ EP_STATE_RUNNING = 1,
+ EP_STATE_HALTED = 2,
+ EP_STATE_STOPPED = 3
+};
+
+enum EP_TYPE_E {
+ EP_TYPE_INVALID = 0,
+ EP_TYPE_ISOCH_OUTBOUND,
+ EP_TYPE_BULK_OUTBOUND,
+ EP_TYPE_INTR_OUTBOUND,
+ EP_TYPE_INVALID2,
+ EP_TYPE_ISOCH_INBOUND,
+ EP_TYPE_BULK_INBOUND,
+ EP_TYPE_INTR_INBOUND
+};
+
+enum TRB_TYPE_E {
+ TRB_TYPE_RSVD = 0,
+ TRB_TYPE_XFER_NORMAL,
+ TRB_TYPE_RSVD2,
+ TRB_TYPE_XFER_DATA_STAGE,
+ TRB_TYPE_XFER_STATUS_STAGE,
+ TRB_TYPE_XFER_DATA_ISOCH, /* 5*/
+ TRB_TYPE_LINK,
+ TRB_TYPE_RSVD7,
+ TRB_TYPE_NO_OP,
+
+ TRB_TYPE_EVT_TRANSFER = 32,
+ TRB_TYPE_EVT_CMD_COMPLETION = 33,
+ TRB_TYPE_EVT_PORT_STATUS_CHANGE = 34,
+ TRB_TYPE_EVT_MFINDEX_WRAP = 39,
+ TRB_TYPE_EVT_SETUP_PKT = 40,
+};
+
+/*Table 127*/
+enum TRB_CMPL_CODES_E {
+ CMPL_CODE_INVALID = 0,
+ CMPL_CODE_SUCCESS,
+ CMPL_CODE_DATA_BUFFER_ERR,
+ CMPL_CODE_BABBLE_DETECTED_ERR,
+ CMPL_CODE_USB_TRANS_ERR,
+ CMPL_CODE_TRB_ERR, /*5*/
+ CMPL_CODE_TRB_STALL,
+ CMPL_CODE_INVALID_STREAM_TYPE_ERR = 10,
+ CMPL_CODE_SHORT_PKT = 13,
+ CMPL_CODE_RING_UNDERRUN,
+ CMPL_CODE_RING_OVERRUN, /*15*/
+ CMPL_CODE_EVENT_RING_FULL_ERR = 21,
+ CMPL_CODE_STOPPED = 26,
+ CMPL_CODE_STOPPED_LENGTH_INVALID = 27,
+ CMPL_CODE_ISOCH_BUFFER_OVERRUN = 31,
+ /*192-224 vendor defined error*/
+ CMPL_CODE_PROTOCOL_STALL = 192,
+ CMPL_CODE_SETUP_TAG_MISMATCH = 193,
+ CMPL_CODE_HALTED = 194,
+ CMPL_CODE_HALTED_LENGTH_INVALID = 195,
+ CMPL_CODE_DISABLED = 196,
+ CMPL_CODE_DISABLED_LENGTH_INVALID = 197,
+};
+
+static const char driver_name[] = "crg_udc";
+
+struct buffer_info {
+ void *vaddr;
+ dma_addr_t dma;
+ u32 len;
+};
+
+struct transfer_trb_s {
+ __le32 dw0;
+ __le32 dw1;
+
+#define TRB_TRANSFER_LEN_MASK 0x0001FFFF
+#define TRB_TRANSFER_LEN_SHIFT 0
+#define TRB_TD_SIZE_MASK 0x003E0000
+#define TRB_TD_SIZE_SHIFT 17
+#define TRB_INTR_TARGET_MASK 0xFFC00000
+#define TRB_INTR_TARGET_SHIFT 22
+ __le32 dw2;
+
+#define TRB_CYCLE_BIT_MASK 0x00000001
+#define TRB_CYCLE_BIT_SHIFT 0
+#define TRB_LINK_TOGGLE_CYCLE_MASK 0x00000002
+#define TRB_LINK_TOGGLE_CYCLE_SHIFT 1
+#define TRB_INTR_ON_SHORT_PKT_MASK 0x00000004
+#define TRB_INTR_ON_SHORT_PKT_SHIFT 2
+#define TRB_NO_SNOOP_MASK 0x00000008
+#define TRB_NO_SNOOP_SHIFT 3
+#define TRB_CHAIN_BIT_MASK 0x00000010
+#define TRB_CHAIN_BIT_SHIFT 4
+#define TRB_INTR_ON_COMPLETION_MASK 0x00000020
+#define TRB_INTR_ON_COMPLETION_SHIFT 5
+
+#define TRB_APPEND_ZLP_MASK 0x00000080
+#define TRB_APPEND_ZLP_SHIFT 7
+
+#define TRB_BLOCK_EVENT_INT_MASK 0x00000200
+#define TRB_BLOCK_EVENT_INT_SHIFT 9
+#define TRB_TYPE_MASK 0x0000FC00
+#define TRB_TYPE_SHIFT 10
+#define DATA_STAGE_TRB_DIR_MASK 0x00010000
+#define DATA_STAGE_TRB_DIR_SHIFT 16
+#define TRB_SETUP_TAG_MASK 0x00060000
+#define TRB_SETUP_TAG_SHIFT 17
+#define STATUS_STAGE_TRB_STALL_MASK 0x00080000
+#define STATUS_STAGE_TRB_STALL_SHIFT 19
+#define STATUS_STAGE_TRB_SET_ADDR_MASK 0x00100000
+#define STATUS_STAGE_TRB_SET_ADDR_SHIFT 20
+
+#define ISOC_TRB_FRAME_ID_MASK 0x7FF00000
+#define ISOC_TRB_FRAME_ID_SHIFT 20
+#define ISOC_TRB_SIA_MASK 0x80000000
+#define ISOC_TRB_SIA_SHIFT 31
+ __le32 dw3;
+};
+
+struct event_trb_s {
+ __le32 dw0;
+ __le32 dw1;
+
+#define EVE_TRB_TRAN_LEN_MASK 0x0001FFFF
+#define EVE_TRB_TRAN_LEN_SHIFT 0
+#define EVE_TRB_COMPL_CODE_MASK 0xFF000000
+#define EVE_TRB_COMPL_CODE_SHIFT 24
+ __le32 dw2;
+
+#define EVE_TRB_CYCLE_BIT_MASK 0x00000001
+#define EVE_TRB_CYCLE_BIT_SHIFT 0
+#define EVE_TRB_TYPE_MASK 0x0000FC00
+#define EVE_TRB_TYPE_SHIFT 10
+#define EVE_TRB_ENDPOINT_ID_MASK 0x001F0000
+#define EVE_TRB_ENDPOINT_ID_SHIFT 16
+#define EVE_TRB_SETUP_TAG_MASK 0x00600000
+#define EVE_TRB_SETUP_TAG_SHIFT 21
+ __le32 dw3;
+};
+
+struct ep_cx_s {
+
+#define EP_CX_LOGICAL_EP_NUM_MASK 0x00000078
+#define EP_CX_LOGICAL_EP_NUM_SHIFT 3
+
+
+#define EP_CX_INTERVAL_MASK 0x00FF0000
+#define EP_CX_INTERVAL_SHIFT 16
+ __le32 dw0;
+
+#define EP_CX_EP_TYPE_MASK 0x00000038
+#define EP_CX_EP_TYPE_SHIFT 3
+#define EP_CX_MAX_BURST_SIZE_MASK 0x0000FF00
+#define EP_CX_MAX_BURST_SIZE_SHIFT 8
+#define EP_CX_MAX_PACKET_SIZE_MASK 0xFFFF0000
+#define EP_CX_MAX_PACKET_SIZE_SHIFT 16
+ __le32 dw1;
+
+#define EP_CX_DEQ_CYC_STATE_MASK 0x00000001
+#define EP_CX_DEQ_CYC_STATE_SHIFT 0
+#define EP_CX_TR_DQPT_LO_MASK 0xFFFFFFF0
+#define EP_CX_TR_DQPT_LO_SHIFT 4
+ __le32 dw2;
+ __le32 dw3;
+};
+
+struct erst_s {
+ /* 64-bit event ring segment address */
+ __le32 seg_addr_lo;
+ __le32 seg_addr_hi;
+ __le32 seg_size;
+ /* Set to zero */
+ __le32 rsvd;
+};
+
+struct sel_value_s {
+ u16 u2_pel_value;
+ u16 u2_sel_value;
+ u8 u1_pel_value;
+ u8 u1_sel_value;
+};
+
+struct crg_udc_request {
+ struct usb_request usb_req;
+ struct list_head queue;
+ bool mapped;
+ u64 buff_len_left;
+ u32 trbs_needed;
+ struct transfer_trb_s *first_trb;
+ struct transfer_trb_s *last_trb;
+ bool all_trbs_queued;
+ bool short_pkt;
+};
+
+struct crg_udc_ep {
+ struct usb_ep usb_ep;
+
+ struct buffer_info tran_ring_info;
+ struct transfer_trb_s *first_trb;
+ struct transfer_trb_s *last_trb;
+
+ struct transfer_trb_s *enq_pt;
+ struct transfer_trb_s *deq_pt;
+ u8 pcs;
+
+ char name[10];
+ u8 DCI;
+ struct list_head queue;
+ const struct usb_endpoint_descriptor *desc;
+ const struct usb_ss_ep_comp_descriptor *comp_desc;
+ bool tran_ring_full;
+ struct crg_gadget_dev *crg_udc;
+
+ int ep_state;
+
+ unsigned wedge:1;
+};
+
+#define CRG_RING_NUM 1
+
+struct crg_udc_event {
+ struct buffer_info erst;
+ struct erst_s *p_erst;
+ struct buffer_info event_ring;
+ struct event_trb_s *evt_dq_pt;
+ u8 CCS;
+ struct event_trb_s *evt_seg0_last_trb;
+};
+
+struct crg_setup_packet {
+ struct usb_ctrlrequest usbctrlreq;
+ u16 setup_tag;
+};
+
+struct crg_udc_priv {
+ bool plat_setup_gen3;
+};
+
+struct crg_gadget_dev {
+ void __iomem *mmio_virt_base;
+ struct resource *udc_res;
+ resource_size_t udc_res_len;
+ struct crg_uccr *uccr;
+ struct crg_uicr *uicr[CRG_RING_NUM];
+
+ const struct crg_udc_priv *priv;
+
+ /* udc_lock device lock */
+ spinlock_t udc_lock;
+
+ struct device *dev;
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *gadget_driver;
+
+ int irq;
+ struct task_struct *vbus_task;
+
+ struct crg_udc_ep udc_ep[32];
+ struct buffer_info ep_cx;
+ struct ep_cx_s *p_epcx;
+
+ struct crg_udc_event udc_event[CRG_RING_NUM];
+
+ struct crg_udc_request *status_req;
+ u16 *statusbuf;
+ struct sel_value_s sel_value;
+ void (*setup_fn_call_back)(struct crg_gadget_dev *a);
+
+#define WAIT_FOR_SETUP 0
+#define SETUP_PKT_PROCESS_IN_PROGRESS 1
+#define DATA_STAGE_XFER 2
+#define DATA_STAGE_RECV 3
+#define STATUS_STAGE_XFER 4
+#define STATUS_STAGE_RECV 5
+ u8 setup_status;
+#define CTRL_REQ_QUEUE_DEPTH 5
+ struct crg_setup_packet ctrl_req_queue[CTRL_REQ_QUEUE_DEPTH];
+ u8 ctrl_req_enq_idx;
+
+ u8 device_state;
+ u8 resume_state;
+ u16 dev_addr;
+ u8 setup_tag;
+ u8 set_tm;
+
+ u32 num_enabled_eps;
+
+ int connected;
+
+ unsigned u2_RWE:1;
+ unsigned feature_u1_enable:1;
+ unsigned feature_u2_enable:1;
+
+ int setup_tag_mismatch_found;
+ int portsc_on_reconnecting;
+};
+
+/*An array should be implemented if we want to support multi
+ * usb device controller
+ */
+static DECLARE_WAIT_QUEUE_HEAD(vbus_wait);
+
+static struct usb_endpoint_descriptor crg_udc_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
+static int get_ep_state(struct crg_gadget_dev *crg_udc, int DCI)
+{
+ struct crg_udc_ep *udc_ep_ptr;
+
+ if (DCI < 0 || DCI == 1)
+ return -EINVAL;
+
+ udc_ep_ptr = &crg_udc->udc_ep[DCI];
+
+ return udc_ep_ptr->ep_state;
+}
+
+/************command related ops**************************/
+static int crg_issue_command(struct crg_gadget_dev *crg_udc,
+ enum crg_cmd_type type, u32 param0, u32 param1)
+{
+ struct crg_uccr *uccr = crg_udc->uccr;
+ u32 status;
+ bool check_complete = false;
+ u32 tmp;
+
+ tmp = readl(&uccr->control);
+ if (tmp & CRG_U3DC_CTRL_RUN)
+ check_complete = true;
+
+ if (check_complete) {
+ tmp = readl(&uccr->cmd_control);
+ if (tmp & CRG_U3DC_CMD_CTRL_ACTIVE) {
+ dev_err(crg_udc->dev, "%s prev command is not complete!\n", __func__);
+ return -1;
+ }
+ }
+ /* Ensure that everything is written before issuing new command */
+ wmb();
+
+ writel(param0, &uccr->cmd_param0);
+ writel(param1, &uccr->cmd_param1);
+
+ /*ignore CMD IOC, in uboot no irq is*/
+ tmp = CRG_U3DC_CMD_CTRL_ACTIVE |
+ CRG_U3DC_CMD_CTRL_TYPE(type);
+ writel(tmp, &uccr->cmd_control);
+
+ dev_dbg(crg_udc->dev, "%s start, type=%d, par0=0x%x, par1=0x%x\n",
+ __func__, type, param0, param1);
+
+ if (check_complete) {
+ do {
+ tmp = readl(&uccr->cmd_control);
+ } while (tmp & CRG_U3DC_CMD_CTRL_ACTIVE);
+
+ dev_dbg(crg_udc->dev, "%s successful\n", __func__);
+
+ status = CRG_U3DC_CMD_CTRL_STATUS_GET(tmp);
+ if (status != 0) {
+ dev_dbg(crg_udc->dev, "%s fail\n", __func__);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static void setup_link_trb(struct transfer_trb_s *link_trb,
+ bool toggle, ulong next_trb)
+{
+ u32 dw = 0;
+
+ link_trb->dw0 = cpu_to_le32(lower_32_bits(next_trb));
+ link_trb->dw1 = cpu_to_le32(upper_32_bits(next_trb));
+ link_trb->dw2 = 0;
+
+ dw = SETF_VAR(TRB_TYPE, dw, TRB_TYPE_LINK);
+ if (toggle)
+ dw = SETF_VAR(TRB_LINK_TOGGLE_CYCLE, dw, 1);
+ else
+ dw = SETF_VAR(TRB_LINK_TOGGLE_CYCLE, dw, 0);
+
+ link_trb->dw3 = cpu_to_le32(dw);
+
+ /* Ensure that lint trb is updated */
+ wmb();
+}
+
+static dma_addr_t tran_trb_virt_to_dma(struct crg_udc_ep *udc_ep,
+ struct transfer_trb_s *trb)
+{
+ unsigned long offset;
+ int trb_idx;
+ dma_addr_t dma_addr = 0;
+
+ trb_idx = trb - udc_ep->first_trb;
+ if (unlikely(trb_idx < 0))
+ return 0;
+
+ offset = trb_idx * sizeof(*trb);
+ if (unlikely(offset > udc_ep->tran_ring_info.len))
+ return 0;
+ dma_addr = udc_ep->tran_ring_info.dma + offset;
+ return dma_addr;
+}
+
+static struct transfer_trb_s *tran_trb_dma_to_virt
+ (struct crg_udc_ep *udc_ep, dma_addr_t address)
+{
+ unsigned long offset;
+ struct transfer_trb_s *trb_virt;
+
+ if (lower_32_bits(address) & 0xf)
+ return NULL;
+
+ offset = address - udc_ep->tran_ring_info.dma;
+ if (unlikely(offset > udc_ep->tran_ring_info.len))
+ return NULL;
+ offset = offset / sizeof(struct transfer_trb_s);
+ trb_virt = udc_ep->first_trb + offset;
+ return trb_virt;
+}
+
+static dma_addr_t event_trb_virt_to_dma
+ (struct crg_udc_event *udc_event, struct event_trb_s *event)
+{
+ dma_addr_t dma_addr = 0;
+ unsigned long seg_offset;
+
+ if (!udc_event || !event)
+ return 0;
+
+ /* update dequeue pointer */
+ seg_offset = (void *)event - udc_event->event_ring.vaddr;
+ dma_addr = udc_event->event_ring.dma + seg_offset;
+
+ return dma_addr;
+}
+
+/* Completes request. Calls gadget completion handler
+ * caller must have acquired spin lock.
+ */
+static void req_done(struct crg_udc_ep *udc_ep,
+ struct crg_udc_request *udc_req, int status)
+{
+ unsigned long flags = 0;
+ struct crg_gadget_dev *crg_udc = udc_ep->crg_udc;
+
+ if (likely(udc_req->usb_req.status == -EINPROGRESS))
+ udc_req->usb_req.status = status;
+
+ list_del_init(&udc_req->queue);
+
+
+ if (udc_req->usb_req.num_mapped_sgs) {
+ dma_unmap_sg(crg_udc->dev, udc_req->usb_req.sg,
+ udc_req->usb_req.num_sgs,
+ (usb_endpoint_dir_in(udc_ep->desc)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+
+ udc_req->usb_req.num_mapped_sgs = 0;
+ dev_dbg(crg_udc->dev, "dma_unmap_sg done\n");
+ }
+
+ if (udc_req->mapped) {
+ if (udc_req->usb_req.length) {
+ dma_unmap_single(crg_udc->dev, udc_req->usb_req.dma,
+ udc_req->usb_req.length, usb_endpoint_dir_in(udc_ep->desc)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ }
+ udc_req->usb_req.dma = DMA_ADDR_INVALID;
+ udc_req->mapped = 0;
+ }
+
+ if (udc_req->usb_req.complete) {
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ udc_req->usb_req.complete(&udc_ep->usb_ep, &udc_req->usb_req);
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+ }
+}
+
+static void nuke(struct crg_udc_ep *udc_ep, int status)
+{
+ struct crg_udc_request *req = NULL;
+
+ while (!list_empty(&udc_ep->queue)) {
+ req = list_entry(udc_ep->queue.next,
+ struct crg_udc_request,
+ queue);
+
+ req_done(udc_ep, req, status);
+ }
+}
+
+static void clear_req_container(struct crg_udc_request *udc_req_ptr)
+{
+ udc_req_ptr->buff_len_left = 0;
+ udc_req_ptr->trbs_needed = 0;
+ udc_req_ptr->all_trbs_queued = 0;
+ udc_req_ptr->first_trb = NULL;
+ udc_req_ptr->last_trb = NULL;
+ udc_req_ptr->short_pkt = 0;
+}
+
+static bool is_pointer_less_than(struct transfer_trb_s *a,
+ struct transfer_trb_s *b, struct crg_udc_ep *udc_ep)
+{
+ if (b > a && (udc_ep->enq_pt >= b || udc_ep->enq_pt < a))
+ return true;
+ if (b < a && (udc_ep->enq_pt >= b && udc_ep->enq_pt < a))
+ return true;
+ return false;
+}
+
+/* num_trbs here is the size of the ring. */
+static u32 room_on_ring(struct crg_gadget_dev *crg_udc, u32 num_trbs,
+ struct transfer_trb_s *p_ring, struct transfer_trb_s *enq_pt,
+ struct transfer_trb_s *dq_pt)
+{
+ u32 i = 0;
+
+ if (enq_pt == dq_pt) {
+ /* ring is empty */
+ return num_trbs - 1;
+ }
+
+ while (enq_pt != dq_pt) {
+ i++;
+
+ enq_pt++;
+
+ if (GETF(TRB_TYPE, enq_pt->dw3) == TRB_TYPE_LINK)
+ enq_pt = p_ring;
+
+ if (i > num_trbs)
+ break;
+ }
+
+ return i - 1;
+}
+
+static void crg_udc_epcx_setup(struct crg_udc_ep *udc_ep)
+{
+ struct crg_gadget_dev *crg_udc = udc_ep->crg_udc;
+ const struct usb_endpoint_descriptor *desc = udc_ep->desc;
+ const struct usb_ss_ep_comp_descriptor *comp_desc = udc_ep->comp_desc;
+ u8 DCI = udc_ep->DCI;
+ struct ep_cx_s *epcx = (struct ep_cx_s *)(crg_udc->p_epcx + DCI - 2);
+ enum EP_TYPE_E ep_type;
+ u16 maxburst = 0;
+ u8 maxstreams = 0;
+ u16 maxsize;
+ u32 dw;
+
+ dev_dbg(crg_udc->dev, "crgudc->p_epcx %p, epcx %p\n", crg_udc->p_epcx, epcx);
+ dev_dbg(crg_udc->dev, "DCI %d, sizeof ep_cx %ld\n", DCI, sizeof(struct ep_cx_s));
+ dev_dbg(crg_udc->dev, "desc epaddr = 0x%x\n", desc->bEndpointAddress);
+
+ /*corigine gadget dir should be opposite to host dir*/
+ if (usb_endpoint_dir_out(desc))
+ ep_type = usb_endpoint_type(desc) + EP_TYPE_INVALID2;
+ else
+ ep_type = usb_endpoint_type(desc);
+
+ maxsize = usb_endpoint_maxp(desc) & 0x07ff; /* D[0:10] */
+
+ if (crg_udc->gadget.speed >= USB_SPEED_SUPER) {
+ maxburst = comp_desc->bMaxBurst;
+
+ if (usb_endpoint_xfer_bulk(udc_ep->desc))
+ maxstreams = comp_desc->bmAttributes & 0x1f;
+
+ } else if ((crg_udc->gadget.speed == USB_SPEED_HIGH ||
+ crg_udc->gadget.speed == USB_SPEED_FULL) &&
+ (usb_endpoint_xfer_int(udc_ep->desc) ||
+ usb_endpoint_xfer_isoc(udc_ep->desc))) {
+ if (crg_udc->gadget.speed == USB_SPEED_HIGH)
+ maxburst = (usb_endpoint_maxp(desc) >> 11) & 0x3;
+ if (maxburst == 0x3) {
+ dev_err(crg_udc->dev, "invalid maxburst\n");
+ maxburst = 0x2;
+ }
+ }
+
+ /* fill ep_dw0 */
+ dw = 0;
+ dw = SETF_VAR(EP_CX_LOGICAL_EP_NUM, dw, udc_ep->DCI / 2);
+ dw = SETF_VAR(EP_CX_INTERVAL, dw, desc->bInterval);
+ if (maxstreams) {
+ dev_err(crg_udc->dev, "%s maxstream=%d is not expected\n",
+ __func__, maxstreams);
+ }
+ epcx->dw0 = cpu_to_le32(dw);
+
+ /* fill ep_dw1 */
+ dw = 0;
+ dw = SETF_VAR(EP_CX_EP_TYPE, dw, ep_type);
+ dw = SETF_VAR(EP_CX_MAX_PACKET_SIZE, dw, maxsize);
+ dw = SETF_VAR(EP_CX_MAX_BURST_SIZE, dw, maxburst);
+ epcx->dw1 = cpu_to_le32(dw);
+
+ /* fill ep_dw2 */
+ dw = lower_32_bits(udc_ep->tran_ring_info.dma) & EP_CX_TR_DQPT_LO_MASK;
+ dw = SETF_VAR(EP_CX_DEQ_CYC_STATE, dw, udc_ep->pcs);
+ epcx->dw2 = cpu_to_le32(dw);
+
+ /* fill ep_dw3 */
+ dw = upper_32_bits(udc_ep->tran_ring_info.dma);
+ epcx->dw3 = cpu_to_le32(dw);
+ /* Ensure that epcx is updated */
+ wmb();
+}
+
+static void crg_udc_epcx_update_dqptr(struct crg_udc_ep *udc_ep)
+{
+ struct crg_gadget_dev *crg_udc = udc_ep->crg_udc;
+ u8 DCI = udc_ep->DCI;
+ struct ep_cx_s *epcx = (struct ep_cx_s *)(crg_udc->p_epcx + DCI - 2);
+ u32 dw;
+ dma_addr_t dqptaddr;
+ u32 cmd_param0;
+
+ if (DCI == 0) {
+ dev_err(crg_udc->dev, "%s Cannot update dqptr for ep0\n", __func__);
+ return;
+ }
+
+ dqptaddr = tran_trb_virt_to_dma(udc_ep, udc_ep->deq_pt);
+
+ /* fill ep_dw2 */
+ dw = lower_32_bits(dqptaddr) & EP_CX_TR_DQPT_LO_MASK;
+ dw = SETF_VAR(EP_CX_DEQ_CYC_STATE, dw, udc_ep->pcs);
+ epcx->dw2 = cpu_to_le32(dw);
+
+ /* fill ep_dw3 */
+ dw = upper_32_bits(dqptaddr);
+ epcx->dw3 = cpu_to_le32(dw);
+
+ cmd_param0 = (0x1 << udc_ep->DCI);
+ /* Ensure that dqptr is updated */
+ wmb();
+
+ crg_issue_command(crg_udc, CRG_CMD_SET_TR_DQPTR, cmd_param0, 0);
+}
+
+static void setup_status_trb(struct crg_gadget_dev *crg_udc,
+ struct transfer_trb_s *p_trb,
+ struct usb_request *usb_req, u8 pcs, u8 set_addr, u8 stall)
+{
+ u32 tmp, dir = 0;
+
+ /* There are some cases where seutp_status_trb() is called with
+ * usb_req set to NULL.
+ */
+
+ p_trb->dw0 = 0;
+ p_trb->dw1 = 0;
+
+ dev_dbg(crg_udc->dev, "data_buf_ptr_lo = 0x%x, data_buf_ptr_hi = 0x%x\n",
+ p_trb->dw0, p_trb->dw1);
+
+ tmp = 0;
+ tmp = SETF_VAR(TRB_INTR_TARGET, tmp, 0);
+ p_trb->dw2 = tmp;
+
+ tmp = 0;
+ tmp = SETF_VAR(TRB_CYCLE_BIT, tmp, pcs);
+ tmp = SETF_VAR(TRB_INTR_ON_COMPLETION, tmp, 1);/*IOC:1*/
+ tmp = SETF_VAR(TRB_TYPE, tmp, TRB_TYPE_XFER_STATUS_STAGE);
+
+ dir = (crg_udc->setup_status == STATUS_STAGE_XFER) ? 0 : 1;
+ tmp = SETF_VAR(DATA_STAGE_TRB_DIR, tmp, dir);
+
+ tmp = SETF_VAR(TRB_SETUP_TAG, tmp, crg_udc->setup_tag);
+ tmp = SETF_VAR(STATUS_STAGE_TRB_STALL, tmp, stall);
+ tmp = SETF_VAR(STATUS_STAGE_TRB_SET_ADDR, tmp, set_addr);
+
+ p_trb->dw3 = tmp;
+ dev_dbg(crg_udc->dev, "trb_dword2 = 0x%x, trb_dword3 = 0x%x\n",
+ p_trb->dw2, p_trb->dw3);
+ /* Ensure that status trb is updated */
+ wmb();
+}
+
+static void knock_doorbell(struct crg_gadget_dev *crg_udc, int DCI)
+{
+ u32 tmp;
+ struct crg_uccr *uccr;
+
+ uccr = crg_udc->uccr;
+ /* Ensure evreything is written before notifying the HW */
+ wmb();
+
+ tmp = CRG_U3DC_DB_TARGET(DCI);
+ dev_dbg(crg_udc->dev, "DOORBELL = 0x%x\n", tmp);
+ writel(tmp, &uccr->doorbell);
+}
+
+static void setup_datastage_trb(struct crg_gadget_dev *crg_udc,
+ struct transfer_trb_s *p_trb, struct usb_request *usb_req,
+ u8 pcs, u32 num_trb, u32 transfer_length, u32 td_size,
+ u8 IOC, u8 AZP, u8 dir, u8 setup_tag)
+{
+ u32 tmp;
+
+ dev_dbg(crg_udc->dev, "dma = 0x%llx, ", usb_req->dma);
+ dev_dbg(crg_udc->dev, "buf = 0x%lx, ", (unsigned long)usb_req->buf);
+
+ p_trb->dw0 = lower_32_bits(usb_req->dma);
+ p_trb->dw1 = upper_32_bits(usb_req->dma);
+
+ dev_dbg(crg_udc->dev, "data_buf_ptr_lo = 0x%x, data_buf_ptr_hi = 0x%x\n",
+ p_trb->dw0, p_trb->dw1);
+
+
+ /* TRB_Transfer_Length
+ *For USB_DIR_OUT, this field is the number of data bytes expected from
+ *xhc. For USB_DIR_IN, this field is the number of data bytes the device
+ *will send.
+ */
+ tmp = 0;
+ tmp = SETF_VAR(TRB_TRANSFER_LEN, tmp, transfer_length);
+ tmp = SETF_VAR(TRB_TD_SIZE, tmp, td_size);
+ tmp = SETF_VAR(TRB_INTR_TARGET, tmp, 0);
+ p_trb->dw2 = tmp;
+
+ tmp = 0;
+ tmp = SETF_VAR(TRB_CYCLE_BIT, tmp, pcs);
+ tmp = SETF_VAR(TRB_INTR_ON_SHORT_PKT, tmp, 1);
+ tmp = SETF_VAR(TRB_INTR_ON_COMPLETION, tmp, IOC);
+ tmp = SETF_VAR(TRB_TYPE, tmp, TRB_TYPE_XFER_DATA_STAGE);
+ tmp = SETF_VAR(TRB_APPEND_ZLP, tmp, AZP);
+ tmp = SETF_VAR(DATA_STAGE_TRB_DIR, tmp, dir);
+ tmp = SETF_VAR(TRB_SETUP_TAG, tmp, setup_tag);
+
+ p_trb->dw3 = tmp;
+ /* Ensure that datastage trb is updated */
+ wmb();
+
+ dev_dbg(crg_udc->dev, "trb_dword0 = 0x%x, trb_dword1 = 0x%x trb_dword2 = 0x%x, trb_dword3 = 0x%x\n",
+ p_trb->dw0, p_trb->dw1, p_trb->dw2, p_trb->dw3);
+}
+
+static void setup_trb(struct crg_gadget_dev *crg_udc,
+ struct transfer_trb_s *p_trb,
+ struct usb_request *usb_req, u32 xfer_len,
+ dma_addr_t xfer_buf_addr, u8 td_size, u8 pcs,
+ u8 trb_type, u8 short_pkt, u8 chain_bit,
+ u8 intr_on_compl, bool b_setup_stage, u8 usb_dir,
+ bool b_isoc, u8 tlb_pc, u16 frame_i_d, u8 SIA, u8 AZP)
+{
+ u32 tmp;
+
+ p_trb->dw0 = lower_32_bits(xfer_buf_addr);
+ p_trb->dw1 = upper_32_bits(xfer_buf_addr);
+
+ dev_dbg(crg_udc->dev, "data_buf_ptr_lo = 0x%x, data_buf_ptr_hi = 0x%x\n",
+ p_trb->dw0, p_trb->dw1);
+
+ tmp = 0;
+ tmp = SETF_VAR(TRB_TRANSFER_LEN, tmp, xfer_len);
+ tmp = SETF_VAR(TRB_TD_SIZE, tmp, td_size);
+ tmp = SETF_VAR(TRB_INTR_TARGET, tmp, 0);
+
+ p_trb->dw2 = tmp;
+
+ tmp = 0;
+ tmp = SETF_VAR(TRB_CYCLE_BIT, tmp, pcs);
+ tmp = SETF_VAR(TRB_INTR_ON_SHORT_PKT, tmp, short_pkt);
+ tmp = SETF_VAR(TRB_CHAIN_BIT, tmp, chain_bit);
+ tmp = SETF_VAR(TRB_INTR_ON_COMPLETION, tmp, intr_on_compl);
+ tmp = SETF_VAR(TRB_APPEND_ZLP, tmp, AZP);
+ tmp = SETF_VAR(TRB_TYPE, tmp, trb_type);
+
+ if (b_setup_stage)
+ tmp = SETF_VAR(DATA_STAGE_TRB_DIR, tmp, usb_dir);
+
+ if (b_isoc) {
+ tmp = SETF_VAR(ISOC_TRB_FRAME_ID, tmp, frame_i_d);
+ tmp = SETF_VAR(ISOC_TRB_SIA, tmp, SIA);
+ }
+
+ p_trb->dw3 = tmp;
+ /* Ensure that trb is updated */
+ wmb();
+ dev_dbg(crg_udc->dev, "trb_dword2 = 0x%.8x, trb_dword3 = 0x%.8x\n",
+ p_trb->dw2, p_trb->dw3);
+
+}
+
+static int crg_udc_queue_trbs(struct crg_udc_ep *udc_ep_ptr,
+ struct crg_udc_request *udc_req_ptr, bool b_isoc,
+ u32 xfer_ring_size,
+ u32 num_trbs_needed, u64 buffer_length)
+{
+ struct crg_gadget_dev *crg_udc = udc_ep_ptr->crg_udc;
+ struct transfer_trb_s *p_xfer_ring = udc_ep_ptr->first_trb;
+ u32 num_trbs_ava = 0;
+ struct usb_request *usb_req = &udc_req_ptr->usb_req;
+ u64 buff_len_temp = 0;
+ u32 i, j = 1;
+ struct transfer_trb_s *enq_pt = udc_ep_ptr->enq_pt;
+ u8 td_size;
+ u8 chain_bit = 1;
+ u8 short_pkt = 0;
+ u8 intr_on_compl = 0;
+ u32 count;
+ bool full_td = true;
+ u32 intr_rate;
+ dma_addr_t trb_buf_addr;
+ bool need_zlp = false;
+ struct scatterlist *sg = NULL;
+ u32 num_sgs = 0;
+ u64 sg_addr = 0;
+
+ dev_dbg(crg_udc->dev, "%s %s\n", __func__, udc_ep_ptr->usb_ep.name);
+ if (udc_req_ptr->usb_req.num_sgs) {
+ num_sgs = udc_req_ptr->usb_req.num_sgs;
+ sg = udc_req_ptr->usb_req.sg;
+ sg_addr = (u64) sg_dma_address(sg);
+ buffer_length = sg_dma_len(sg);
+
+ dev_dbg(crg_udc->dev, "num_sgs = %d, num_mapped_sgs = %d\n",
+ udc_req_ptr->usb_req.num_sgs,
+ udc_req_ptr->usb_req.num_mapped_sgs);
+ dev_dbg(crg_udc->dev,
+ "sg_addr = %p, buffer_length = %llu, num_trbs = %d\n",
+ (void *)sg_addr, buffer_length, num_trbs_needed);
+ }
+
+ if (!b_isoc) {
+ if (udc_req_ptr->usb_req.zero == 1 &&
+ udc_req_ptr->usb_req.length != 0 &&
+ ((udc_req_ptr->usb_req.length %
+ udc_ep_ptr->usb_ep.maxpacket) == 0)) {
+ need_zlp = true;
+ }
+ }
+
+ td_size = num_trbs_needed;
+
+ num_trbs_ava = room_on_ring(crg_udc, xfer_ring_size,
+ p_xfer_ring, udc_ep_ptr->enq_pt, udc_ep_ptr->deq_pt);
+
+ /* trb_buf_addr points to the addr of the buffer that we write in
+ * each TRB. If this function is called to complete the pending TRB
+ * transfers of a previous request, point it to the buffer that is
+ * not transferred, or else point it to the starting address of the
+ * buffer received in usb_request
+ */
+ if (udc_req_ptr->trbs_needed) {
+ /* Here udc_req_ptr->trbs_needed is used to indicate if we
+ * are completing a previous req
+ */
+ trb_buf_addr = usb_req->dma +
+ (usb_req->length - udc_req_ptr->buff_len_left);
+ } else {
+ if (sg_addr)
+ trb_buf_addr = sg_addr;
+ else
+ trb_buf_addr = usb_req->dma;
+ }
+
+ if (num_trbs_ava >= num_trbs_needed) {
+ count = num_trbs_needed;
+ } else {
+ if (b_isoc) {
+ struct crg_udc_request *udc_req_ptr_temp;
+ u8 temp = 0;
+
+ list_for_each_entry(udc_req_ptr_temp,
+ &udc_ep_ptr->queue, queue) {
+ temp++;
+ }
+
+ if (temp >= 2) {
+ dev_err(crg_udc->dev, "%s don't do isoc discard\n", __func__);
+ /* we already scheduled two mfi in advance. */
+ return 0;
+ }
+ }
+
+ /* always keep one trb for zlp. */
+ count = num_trbs_ava;
+ full_td = false;
+ dev_dbg(crg_udc->dev, "TRB Ring Full. Avail: 0x%x Req: 0x%x\n",
+ num_trbs_ava, num_trbs_needed);
+ udc_ep_ptr->tran_ring_full = true;
+
+ /*if there is still some trb not queued,
+ *it means last queued
+ *trb is not the last trb of TD, so no need zlp
+ */
+ need_zlp = false;
+ }
+
+ for (i = 0; i < count; i++) {
+ if ((udc_req_ptr->usb_req.num_sgs) && (buffer_length == 0)) {
+ sg = sg_next(sg);
+ if (sg) {
+ trb_buf_addr = (u64) sg_dma_address(sg);
+ buffer_length = sg_dma_len(sg);
+ dev_dbg(crg_udc->dev,
+ "trb_buf_addr = %p, num_trbs = %d\n",
+ (void *)trb_buf_addr, num_trbs_needed);
+ dev_dbg(crg_udc->dev, "buffer_length = %llu\n",
+ buffer_length);
+ } else {
+ dev_err(crg_udc->dev,
+ "scatterlist ended unexpectedly (i=%d, count=%d)\n",
+ i, count);
+ return -EINVAL;
+ }
+ }
+
+ if (buffer_length > TRB_MAX_BUFFER_SIZE)
+ buff_len_temp = TRB_MAX_BUFFER_SIZE;
+ else
+ buff_len_temp = buffer_length;
+
+ buffer_length -= buff_len_temp;
+
+ if (usb_endpoint_dir_out(udc_ep_ptr->desc))
+ short_pkt = 1;
+
+ if ((buffer_length == 0) && (i == (count - 1))) {
+ chain_bit = 0;
+ intr_on_compl = 1;
+ udc_req_ptr->all_trbs_queued = 1;
+ }
+
+
+#define BULK_EP_INTERRUPT_RATE 5
+#define ISOC_EP_INTERRUPT_RATE 1
+ if (b_isoc)
+ intr_rate = ISOC_EP_INTERRUPT_RATE;
+ else
+ intr_rate = BULK_EP_INTERRUPT_RATE;
+
+ if (!full_td && j == intr_rate) {
+ intr_on_compl = 1;
+ j = 0;
+ }
+
+ if (b_isoc) {
+ setup_trb(crg_udc, enq_pt, usb_req, buff_len_temp,
+ trb_buf_addr, td_size - 1, udc_ep_ptr->pcs,
+ TRB_TYPE_XFER_DATA_ISOCH, short_pkt, chain_bit,
+ intr_on_compl, 0, 0, 1, 0, 0, 1, 0);
+ } else {
+ u8 pcs = udc_ep_ptr->pcs;
+
+ if (udc_ep_ptr->comp_desc &&
+ (usb_ss_max_streams(udc_ep_ptr->comp_desc))) {
+ dev_err(crg_udc->dev, "%s don't do bulk stream\n", __func__);
+ } else {
+ if (udc_req_ptr->all_trbs_queued) {
+ /*it is the last trb of TD,
+ * so consider zlp
+ */
+ u8 AZP = 0;
+
+ AZP = (need_zlp ? 1 : 0);
+
+ setup_trb(crg_udc, enq_pt, usb_req,
+ buff_len_temp, trb_buf_addr,
+ td_size - 1, pcs,
+ TRB_TYPE_XFER_NORMAL, short_pkt,
+ chain_bit, intr_on_compl,
+ 0, 0, 0, 0, 0, 0, AZP);
+
+ } else {
+ setup_trb(crg_udc, enq_pt, usb_req,
+ buff_len_temp, trb_buf_addr,
+ td_size - 1, pcs,
+ TRB_TYPE_XFER_NORMAL, short_pkt,
+ chain_bit, intr_on_compl,
+ 0, 0, 0, 0, 0, 0, 0);
+ }
+ }
+ }
+ trb_buf_addr += buff_len_temp;
+ td_size--;
+ enq_pt++;
+ j++;
+ if (GETF(TRB_TYPE, enq_pt->dw3) == TRB_TYPE_LINK) {
+ if (GETF(TRB_LINK_TOGGLE_CYCLE,
+ enq_pt->dw3)) {
+ enq_pt->dw3 = SETF_VAR(TRB_CYCLE_BIT,
+ enq_pt->dw3, udc_ep_ptr->pcs);
+ udc_ep_ptr->pcs ^= 0x1;
+ /* Ensure that trb cycle bit is updated */
+ wmb();
+ enq_pt = udc_ep_ptr->first_trb;
+ }
+ }
+ }
+
+ if (!udc_req_ptr->trbs_needed)
+ udc_req_ptr->first_trb = udc_ep_ptr->enq_pt;
+ udc_ep_ptr->enq_pt = enq_pt;
+ udc_req_ptr->buff_len_left = buffer_length;
+ udc_req_ptr->trbs_needed = td_size;
+
+ if (udc_req_ptr->buff_len_left == 0) {
+ /* It is actually last trb of a request plus 1 */
+ if (udc_ep_ptr->enq_pt == udc_ep_ptr->first_trb)
+ udc_req_ptr->last_trb = udc_ep_ptr->last_trb - 1;
+ else
+ udc_req_ptr->last_trb = udc_ep_ptr->enq_pt - 1;
+ }
+
+ return 0;
+}
+
+static int crg_udc_queue_ctrl(struct crg_udc_ep *udc_ep_ptr,
+ struct crg_udc_request *udc_req_ptr, u32 num_of_trbs_needed)
+{
+ struct crg_gadget_dev *crg_udc = udc_ep_ptr->crg_udc;
+ u8 ep_state;
+ struct transfer_trb_s *enq_pt = udc_ep_ptr->enq_pt;
+ struct transfer_trb_s *dq_pt = udc_ep_ptr->deq_pt;
+ struct usb_request *usb_req = &udc_req_ptr->usb_req;
+ struct transfer_trb_s *p_trb;
+ u32 transfer_length;
+ u32 td_size = 0;
+ u8 IOC;
+ u8 AZP;
+ u8 dir = 0;
+ u8 setup_tag = crg_udc->setup_tag;
+
+ ep_state = get_ep_state(crg_udc, 0);
+
+
+ /* Need to queue the request even ep is paused or halted */
+ if (ep_state != EP_STATE_RUNNING) {
+ dev_dbg(crg_udc->dev, "EP State = 0x%x\n", ep_state);
+ return -EINVAL;
+ }
+
+ if (list_empty(&udc_ep_ptr->queue)) {
+ /* For control endpoint, we can handle one setup request at a
+ * time. so if there are TD pending in the transfer ring.
+ * wait for the sequence number error event. Then put the new
+ * request to transfer ring
+ */
+ if (enq_pt == dq_pt) {
+ u32 tmp = 0, i;
+ bool need_zlp = false;
+
+ dev_dbg(crg_udc->dev, "Setup Data Stage TRBs\n");
+ /* Transfer ring is empty
+ * setup data stage TRBs
+ */
+ udc_req_ptr->first_trb = udc_ep_ptr->enq_pt;
+
+ if (crg_udc->setup_status == DATA_STAGE_XFER)
+ dir = 0;
+ else if (crg_udc->setup_status == DATA_STAGE_RECV)
+ dir = 1;
+ else
+ dev_dbg(crg_udc->dev, "unexpected setup_status!%d\n",
+ crg_udc->setup_status);
+
+ if (udc_req_ptr->usb_req.zero == 1 &&
+ udc_req_ptr->usb_req.length != 0 &&
+ ((udc_req_ptr->usb_req.length %
+ udc_ep_ptr->usb_ep.maxpacket) == 0))
+ need_zlp = true;/*zlp = zero length packet*/
+
+
+ for (i = 0; i < num_of_trbs_needed; i++) {
+ p_trb = enq_pt;
+ if (i < (num_of_trbs_needed - 1)) {
+ transfer_length = TRB_MAX_BUFFER_SIZE;
+ IOC = 0;
+ AZP = 0;
+ } else {
+ tmp = TRB_MAX_BUFFER_SIZE * i;
+ transfer_length = (u32)usb_req->length
+ - tmp;
+
+ IOC = 1;
+ AZP = (need_zlp ? 1 : 0);
+ }
+
+ dev_dbg(crg_udc->dev,
+ "tx_len = 0x%x, tmp = 0x%x\n",
+ transfer_length, tmp);
+
+ setup_datastage_trb(crg_udc, p_trb, usb_req,
+ udc_ep_ptr->pcs, i, transfer_length,
+ td_size, IOC, AZP, dir, setup_tag);
+ udc_req_ptr->all_trbs_queued = 1;
+ enq_pt++;
+
+ if (GETF(TRB_TYPE, enq_pt->dw3) ==
+ TRB_TYPE_LINK) {
+ if (GETF(TRB_LINK_TOGGLE_CYCLE,
+ enq_pt->dw3)) {
+ enq_pt->dw3 = SETF_VAR(TRB_CYCLE_BIT,
+ enq_pt->dw3,
+ udc_ep_ptr->pcs);
+ udc_ep_ptr->pcs ^= 0x1;
+ }
+ /* Ensure that trb cycle bit is updated */
+ wmb();
+ enq_pt = udc_ep_ptr->first_trb;
+ }
+ }
+
+ udc_ep_ptr->enq_pt = enq_pt;
+
+ tmp = 0;
+
+ knock_doorbell(crg_udc, 0);
+
+ if (udc_ep_ptr->enq_pt == udc_ep_ptr->first_trb)
+ udc_req_ptr->last_trb =
+ udc_ep_ptr->last_trb - 1;
+ else
+ udc_req_ptr->last_trb = udc_ep_ptr->enq_pt - 1;
+ } else {
+ /* we process one setup request at a time, so ring
+ * should already be empty.
+ */
+ dev_err(crg_udc->dev, "Eq = 0x%p != Dq = 0x%p\n",
+ enq_pt, dq_pt);
+ }
+ } else {
+ dev_err(crg_udc->dev, "udc_ep_ptr->queue not empty\n");
+ /* New setup packet came
+ * Drop the this req..
+ */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void build_ep0_status(struct crg_udc_ep *udc_ep_ptr,
+ bool default_value, u32 status,
+ struct crg_udc_request *udc_req_ptr, u8 set_addr, u8 stall)
+{
+ struct crg_gadget_dev *crg_udc = udc_ep_ptr->crg_udc;
+ struct transfer_trb_s *enq_pt = udc_ep_ptr->enq_pt;
+
+ if (default_value) {
+ udc_req_ptr = crg_udc->status_req;
+ udc_req_ptr->usb_req.length = 0;
+ udc_req_ptr->usb_req.status = status;
+ udc_req_ptr->usb_req.actual = 0;
+ udc_req_ptr->usb_req.complete = NULL;
+ } else {
+ udc_req_ptr->usb_req.status = status;
+ udc_req_ptr->usb_req.actual = 0;
+ }
+
+ setup_status_trb(crg_udc, enq_pt, &udc_req_ptr->usb_req,
+ udc_ep_ptr->pcs, set_addr, stall);
+
+ enq_pt++;
+
+ /* check if we are at end of trb segment. If so, update
+ * pcs and enq for next segment
+ */
+ if (GETF(TRB_TYPE, enq_pt->dw3) == TRB_TYPE_LINK) {
+ if (GETF(TRB_LINK_TOGGLE_CYCLE, enq_pt->dw3)) {
+ enq_pt->dw3 = SETF_VAR
+ (TRB_CYCLE_BIT, enq_pt->dw3, udc_ep_ptr->pcs);
+ udc_ep_ptr->pcs ^= 0x1;
+ }
+ enq_pt = udc_ep_ptr->first_trb;
+ }
+ udc_ep_ptr->enq_pt = enq_pt;
+
+ knock_doorbell(crg_udc, 0);
+
+ list_add_tail(&udc_req_ptr->queue, &udc_ep_ptr->queue);
+}
+
+static void ep0_req_complete(struct crg_udc_ep *udc_ep_ptr)
+{
+ struct crg_gadget_dev *crg_udc = udc_ep_ptr->crg_udc;
+
+ switch (crg_udc->setup_status) {
+ case DATA_STAGE_XFER:
+ crg_udc->setup_status = STATUS_STAGE_RECV;
+ build_ep0_status(udc_ep_ptr, true, -EINPROGRESS, NULL, 0, 0);
+ break;
+ case DATA_STAGE_RECV:
+ crg_udc->setup_status = STATUS_STAGE_XFER;
+ build_ep0_status(udc_ep_ptr, true, -EINPROGRESS, NULL, 0, 0);
+ break;
+ default:
+ if (crg_udc->setup_fn_call_back)
+ crg_udc->setup_fn_call_back(crg_udc);
+
+ crg_udc->setup_status = WAIT_FOR_SETUP;
+ break;
+ }
+}
+
+static void handle_cmpl_code_success(struct crg_gadget_dev *crg_udc,
+ struct event_trb_s *event, struct crg_udc_ep *udc_ep_ptr)
+{
+ u64 trb_pt;
+ struct transfer_trb_s *p_trb;
+ struct crg_udc_request *udc_req_ptr;
+ u32 trb_transfer_length;
+
+ trb_pt = (u64)event->dw0 + ((u64)(event->dw1) << 32);
+ p_trb = tran_trb_dma_to_virt(udc_ep_ptr, trb_pt);
+
+ dev_dbg(crg_udc->dev, "trb_pt = 0x%lx, p_trb = 0x%p\n", (unsigned long)trb_pt, p_trb);
+ dev_dbg(crg_udc->dev, "trb dw0 = 0x%x\n", p_trb->dw0);
+ dev_dbg(crg_udc->dev, "trb dw1 = 0x%x\n", p_trb->dw1);
+ dev_dbg(crg_udc->dev, "trb dw2 = 0x%x\n", p_trb->dw2);
+ dev_dbg(crg_udc->dev, "trb dw3 = 0x%x\n", p_trb->dw3);
+
+ if (!GETF(TRB_CHAIN_BIT, p_trb->dw3)) {
+ /* chain bit is not set, which means it
+ * is the end of a TD
+ */
+ udc_req_ptr = list_entry(udc_ep_ptr->queue.next,
+ struct crg_udc_request, queue);
+
+ dev_dbg(crg_udc->dev, "udc_req_ptr = 0x%p\n", udc_req_ptr);
+
+ trb_transfer_length = GETF(EVE_TRB_TRAN_LEN,
+ event->dw2);
+ udc_req_ptr->usb_req.actual = udc_req_ptr->usb_req.length -
+ trb_transfer_length;
+ dev_dbg(crg_udc->dev, "Actual data xfer = 0x%x, tx_len = 0x%x\n",
+ udc_req_ptr->usb_req.actual, trb_transfer_length);
+
+ dev_dbg(crg_udc->dev, "udc_req_ptr->usb_req.buf = 0x%p\n",
+ udc_req_ptr->usb_req.buf);
+ /* Ensure that req_ptr is updated */
+ wmb();
+ req_done(udc_ep_ptr, udc_req_ptr, 0);
+ if (!udc_ep_ptr->desc) {
+ dev_dbg(crg_udc->dev, "udc_ep_ptr->desc is NULL\n");
+ } else {
+ if (usb_endpoint_xfer_control(udc_ep_ptr->desc))
+ ep0_req_complete(udc_ep_ptr);
+ }
+ }
+}
+
+static void update_dequeue_pt(struct event_trb_s *event,
+ struct crg_udc_ep *udc_ep)
+{
+ u32 deq_pt_lo = event->dw0;
+ u32 deq_pt_hi = event->dw1;
+ u64 dq_pt_addr = (u64)deq_pt_lo + ((u64)deq_pt_hi << 32);
+ struct transfer_trb_s *deq_pt;
+
+ deq_pt = tran_trb_dma_to_virt(udc_ep, dq_pt_addr);
+ deq_pt++;
+
+ if (GETF(TRB_TYPE, deq_pt->dw3) == TRB_TYPE_LINK)
+ deq_pt = udc_ep->first_trb;
+
+ udc_ep->deq_pt = deq_pt;
+}
+
+static void advance_dequeue_pt(struct crg_udc_ep *udc_ep)
+{
+ struct crg_udc_request *udc_req;
+
+ if (!list_empty(&udc_ep->queue)) {
+ udc_req = list_entry(udc_ep->queue.next,
+ struct crg_udc_request,
+ queue);
+
+ if (udc_req->first_trb)
+ udc_ep->deq_pt = udc_req->first_trb;
+ else
+ udc_ep->deq_pt = udc_ep->enq_pt;
+ } else
+ udc_ep->deq_pt = udc_ep->enq_pt;
+}
+
+static bool is_request_dequeued(struct crg_gadget_dev *crg_udc,
+ struct crg_udc_ep *udc_ep, struct event_trb_s *event)
+{
+ struct crg_udc_request *udc_req;
+ u32 trb_pt_lo = event->dw0;
+ u32 trb_pt_hi = event->dw1;
+ u64 trb_addr = (u64)trb_pt_lo + ((u64)trb_pt_hi << 32);
+ struct transfer_trb_s *trb_pt;
+ bool status = true;
+
+ if (udc_ep->DCI == 0)
+ return false;
+
+ trb_pt = tran_trb_dma_to_virt(udc_ep, trb_addr);
+ list_for_each_entry(udc_req, &udc_ep->queue, queue) {
+ if (trb_pt == udc_req->last_trb ||
+ trb_pt == udc_req->first_trb) {
+ status = false;
+ break;
+ }
+
+ if (is_pointer_less_than(trb_pt, udc_req->last_trb, udc_ep) &&
+ is_pointer_less_than(udc_req->first_trb, trb_pt,
+ udc_ep)) {
+ status = false;
+ break;
+ }
+ }
+
+ return status;
+}
+
+static unsigned int count_trbs(u64 addr, u64 len)
+{
+ unsigned int num_trbs;
+
+ num_trbs = DIV_ROUND_UP(len + (addr & (TRB_MAX_BUFFER_SIZE - 1)),
+ TRB_MAX_BUFFER_SIZE);
+ if (num_trbs == 0)
+ num_trbs++;
+
+ return num_trbs;
+}
+
+static unsigned int count_sg_trbs_needed(struct usb_request *usb_req)
+{
+ struct scatterlist *sg;
+ unsigned int i, len, full_len, num_trbs = 0;
+
+ full_len = usb_req->length;
+
+ for_each_sg(usb_req->sg, sg, usb_req->num_mapped_sgs, i) {
+ len = sg_dma_len(sg);
+ num_trbs += count_trbs(sg_dma_address(sg), len);
+ len = min_t(unsigned int, len, full_len);
+ full_len -= len;
+ if (full_len == 0)
+ break;
+ }
+
+ return num_trbs;
+}
+
+
+static int crg_udc_build_td(struct crg_udc_ep *udc_ep_ptr,
+ struct crg_udc_request *udc_req_ptr)
+{
+ int status = 0;
+ struct crg_gadget_dev *crg_udc = udc_ep_ptr->crg_udc;
+ u32 num_trbs_needed;
+ u64 buffer_length;
+ u32 tmp;
+
+ dev_dbg(crg_udc->dev, "udc_req buf = 0x%p\n", udc_req_ptr->usb_req.buf);
+
+ if (udc_req_ptr->trbs_needed) {
+ /* If this is called to complete pending TRB transfers
+ * of previous Request
+ */
+ buffer_length = udc_req_ptr->buff_len_left;
+ num_trbs_needed = udc_req_ptr->trbs_needed;
+ } else {
+ buffer_length = (u64)udc_req_ptr->usb_req.length;
+
+ if (udc_req_ptr->usb_req.num_sgs)
+ num_trbs_needed = count_sg_trbs_needed(&udc_req_ptr->usb_req);
+ else {
+ num_trbs_needed = (u32)(buffer_length / TRB_MAX_BUFFER_SIZE);
+
+ if (buffer_length == 0 ||
+ (buffer_length % TRB_MAX_BUFFER_SIZE))
+ num_trbs_needed += 1;
+ }
+ }
+
+ dev_dbg(crg_udc->dev, "buf_len = %ld, num_trb_needed = %d\n",
+ (unsigned long)buffer_length, num_trbs_needed);
+
+ if (usb_endpoint_xfer_control(udc_ep_ptr->desc)) {
+ dev_dbg(crg_udc->dev, "crg_udc_queue_ctrl control\n");
+ status = crg_udc_queue_ctrl(udc_ep_ptr,
+ udc_req_ptr, num_trbs_needed);
+ } else if (usb_endpoint_xfer_isoc(udc_ep_ptr->desc)) {
+ dev_dbg(crg_udc->dev, "crg_udc_queue_trbs isoc\n");
+ status = crg_udc_queue_trbs(udc_ep_ptr, udc_req_ptr, 1,
+ CRGUDC_ISOC_EP_TD_RING_SIZE,
+ num_trbs_needed, buffer_length);
+
+ tmp = udc_ep_ptr->DCI;
+ tmp = CRG_U3DC_DB_TARGET(tmp);
+ dev_dbg(crg_udc->dev, "DOORBELL = 0x%x\n", tmp);
+
+ knock_doorbell(crg_udc, udc_ep_ptr->DCI);
+ } else if (usb_endpoint_xfer_bulk(udc_ep_ptr->desc)) {
+ dev_dbg(crg_udc->dev, "crg_udc_queue_trbs bulk\n");
+ status = crg_udc_queue_trbs(udc_ep_ptr, udc_req_ptr, 0,
+ CRGUDC_BULK_EP_TD_RING_SIZE,
+ num_trbs_needed, buffer_length);
+ tmp = udc_ep_ptr->DCI;
+ tmp = CRG_U3DC_DB_TARGET(tmp);
+ if (udc_ep_ptr->comp_desc &&
+ usb_ss_max_streams(udc_ep_ptr->comp_desc)) {
+ /* hold the doorbell if stream_rejected is set */
+ dev_err(crg_udc->dev, "%s, WANT TO have bulk stream\n", __func__);
+ }
+
+ knock_doorbell(crg_udc, udc_ep_ptr->DCI);
+ } else {
+ /* interrupt endpoint */
+ status = crg_udc_queue_trbs(udc_ep_ptr, udc_req_ptr, 0,
+ CRGUDC_INT_EP_TD_RING_SIZE,
+ num_trbs_needed, buffer_length);
+ tmp = udc_ep_ptr->DCI;
+ tmp = CRG_U3DC_DB_TARGET(tmp);
+
+ knock_doorbell(crg_udc, udc_ep_ptr->DCI);
+ }
+
+ return status;
+}
+
+/* This function will go through the list of the USB requests for the
+ * given endpoint and schedule any unscheduled trb's to the xfer ring
+ */
+static void queue_pending_trbs(struct crg_udc_ep *udc_ep_ptr)
+{
+ struct crg_udc_request *udc_req_ptr;
+ /* schedule trbs till there arent any pending unscheduled ones
+ * or the ring is full again
+ */
+
+ list_for_each_entry(udc_req_ptr, &udc_ep_ptr->queue, queue) {
+ if (udc_req_ptr->all_trbs_queued == 0)
+ crg_udc_build_td(udc_ep_ptr, udc_req_ptr);
+
+ if (udc_ep_ptr->tran_ring_full)
+ break;
+ }
+}
+
+static void squeeze_xfer_ring(struct crg_udc_ep *udc_ep_ptr,
+ struct crg_udc_request *udc_req_ptr)
+{
+ struct transfer_trb_s *temp = udc_req_ptr->first_trb;
+ struct crg_udc_request *next_req;
+
+ /* All the incompleted Requests are recorded in crg_udc_ep.queue by SW*/
+ /* 1. Clear all the queued-in-ring trbs from the deleted point */
+ /* 2. Re-queue in ring the Requests that are after the deleted Request*/
+ while (temp != udc_ep_ptr->enq_pt) {
+ temp->dw0 = 0;
+ temp->dw1 = 0;
+ temp->dw2 = 0;
+ temp->dw3 = 0;
+
+ temp++;
+
+ if (GETF(TRB_TYPE, temp->dw3) == TRB_TYPE_LINK)
+ temp = udc_ep_ptr->first_trb;
+ }
+
+ /* Update the new enq_ptr starting from the deleted req */
+ udc_ep_ptr->enq_pt = udc_req_ptr->first_trb;
+
+ if (udc_ep_ptr->tran_ring_full)
+ udc_ep_ptr->tran_ring_full = false;
+
+ next_req = list_entry(udc_req_ptr->queue.next,
+ struct crg_udc_request, queue);
+
+ list_for_each_entry_from(next_req, &udc_ep_ptr->queue, queue) {
+ next_req->usb_req.status = -EINPROGRESS;
+ next_req->usb_req.actual = 0;
+
+ /* clear the values of the nv_udc_request container **/
+ clear_req_container(next_req);
+
+ if (udc_ep_ptr->tran_ring_full)
+ break;
+
+ /* push the request to the transfer ring */
+ crg_udc_build_td(udc_ep_ptr, next_req);
+ }
+}
+
+static int set_ep0_halt(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_udc_ep *udc_ep_ptr = &crg_udc->udc_ep[0];
+ int ep_state;
+
+ ep_state = get_ep_state(crg_udc, udc_ep_ptr->DCI);
+ if (ep_state == EP_STATE_HALTED ||
+ ep_state == EP_STATE_DISABLED) {
+ return 0;
+ }
+
+ build_ep0_status(udc_ep_ptr, true, -EINVAL, NULL, 0, 1);
+
+ udc_ep_ptr->ep_state = EP_STATE_HALTED;
+
+ return 0;
+}
+
+static int set_ep_halt(struct crg_gadget_dev *crg_udc, int DCI)
+{
+ struct crg_uccr *uccr = crg_udc->uccr;
+ struct crg_udc_ep *udc_ep_ptr = &crg_udc->udc_ep[DCI];
+ u32 param0;
+ u32 tmp;
+
+ dev_dbg(crg_udc->dev, "%s DCI=%d !!\n", __func__, DCI);
+
+ if (DCI == 0)
+ return 0;
+
+ if (udc_ep_ptr->ep_state == EP_STATE_DISABLED ||
+ udc_ep_ptr->ep_state == EP_STATE_HALTED)
+ return 0;
+
+ param0 = (0x1 << DCI);
+ crg_issue_command(crg_udc, CRG_CMD_SET_HALT, param0, 0);
+ do {
+ tmp = readl(&uccr->ep_running);
+ } while ((tmp & param0) != 0);
+
+ /* clean up the request queue */
+ nuke(udc_ep_ptr, -ECONNRESET);
+
+ udc_ep_ptr->deq_pt = udc_ep_ptr->enq_pt;
+ udc_ep_ptr->tran_ring_full = false;
+ udc_ep_ptr->ep_state = EP_STATE_HALTED;
+
+ return 0;
+}
+
+static int ep_halt(struct crg_udc_ep *udc_ep_ptr,
+ int halt, int ignore_wedge)
+{
+ struct crg_gadget_dev *crg_udc = udc_ep_ptr->crg_udc;
+ struct crg_uccr *uccr = crg_udc->uccr;
+ int ep_state;
+ bool reset_seq_only = false;
+ int do_halt;
+ u32 param0;
+ u32 tmp;
+ struct crg_udc_request *udc_req_ptr;
+
+ if (!udc_ep_ptr->desc) {
+ dev_err(crg_udc->dev, "NULL desc\n");
+ return -EINVAL;
+ }
+
+ if (udc_ep_ptr->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ dev_err(crg_udc->dev, "Isoc ep, halt not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (udc_ep_ptr->DCI == 0)
+ return 0;
+
+ ep_state = get_ep_state(crg_udc, udc_ep_ptr->DCI);
+
+ if (ep_state == EP_STATE_DISABLED)
+ return 0;
+
+ if (ep_state == EP_STATE_HALTED) {
+ if (halt != 0)
+ return 0;
+
+ /* want unhalt an halted ep */
+ if (udc_ep_ptr->wedge && !ignore_wedge) {
+ do_halt = -1;
+ reset_seq_only = true;
+ } else {
+ do_halt = 0;
+ }
+
+ } else {
+ /* ep state == running or stopped */
+ if (halt != 0) {
+ /* want halt a running ep */
+ do_halt = 1;
+ } else {
+ /* reset a running ep */
+ do_halt = 0;
+ reset_seq_only = true;
+ }
+ }
+
+ param0 = (0x1 << udc_ep_ptr->DCI);
+ if (do_halt == 1) {
+ /* setting ep to halt */
+ dev_dbg(crg_udc->dev, "HALT EP DCI = %d\n", udc_ep_ptr->DCI);
+ crg_issue_command(crg_udc, CRG_CMD_SET_HALT, param0, 0);
+ do {
+ tmp = readl(&uccr->ep_running);
+ } while ((tmp & param0) != 0);
+
+ if (crg_udc->gadget.speed >= USB_SPEED_SUPER) {
+ /* clean up the request queue */
+ nuke(udc_ep_ptr, -ECONNRESET);
+
+ udc_ep_ptr->deq_pt = udc_ep_ptr->enq_pt;
+ udc_ep_ptr->tran_ring_full = false;
+
+ dev_dbg(crg_udc->dev, "update deq_pt tp enq_pt 0x%p\n",
+ udc_ep_ptr->deq_pt);
+ }
+ /* clean up the request queue */
+ udc_ep_ptr->ep_state = EP_STATE_HALTED;
+ } else if (do_halt == 0) {
+ /* clearing ep halt state */
+ dev_dbg(crg_udc->dev, "Clear EP HALT DCI = %d\n", udc_ep_ptr->DCI);
+ /* reset sequence number */
+ crg_issue_command(crg_udc, CRG_CMD_RESET_SEQNUM, param0, 0);
+
+ if (!reset_seq_only) {
+ /* Clear halt for a halted EP.*/
+ /* NOTE: we must CLEAR_HALT first, then SET_TR_DQPTR*/
+ crg_issue_command(crg_udc,
+ CRG_CMD_CLEAR_HALT, param0, 0);
+ crg_udc_epcx_update_dqptr(udc_ep_ptr);
+
+ dev_dbg(crg_udc->dev,
+ "crg_udc_epcx_update_dqptr , PCS = %d\n",
+ udc_ep_ptr->pcs);
+ }
+
+ udc_ep_ptr->wedge = 0;
+ udc_ep_ptr->ep_state = EP_STATE_RUNNING;
+ /* set endpoint to running state */
+ /* clear pause for the endpoint */
+ if (!list_empty(&udc_ep_ptr->queue)) {
+ tmp = udc_ep_ptr->DCI;
+ tmp = CRG_U3DC_DB_TARGET(tmp);
+
+ list_for_each_entry(udc_req_ptr, &udc_ep_ptr->queue,
+ queue) {
+ struct transfer_trb_s *tmp_ptr =
+ udc_ep_ptr->deq_pt;
+
+ tmp_ptr--;
+ tmp_ptr = udc_ep_ptr->deq_pt + 1;
+ if (GETF(TRB_TYPE, tmp_ptr->dw3) == TRB_TYPE_LINK) {
+ udc_ep_ptr->pcs ^= 0x1;
+ crg_udc_epcx_update_dqptr(udc_ep_ptr);
+ udc_ep_ptr->pcs ^= 0x1;
+ }
+ }
+ knock_doorbell(crg_udc, udc_ep_ptr->DCI);
+ }
+ } else {
+ /* wedged EP deny CLEAR HALT */
+ dev_dbg(crg_udc->dev, "wedged EP deny CLEAR HALT DCI = %d\n", udc_ep_ptr->DCI);
+ /* reset sequence number */
+ if (reset_seq_only)
+ crg_issue_command(crg_udc,
+ CRG_CMD_RESET_SEQNUM, param0, 0);
+ }
+
+ return 0;
+}
+
+/************ep related ops*******************************/
+static int crg_udc_ep_disable(struct usb_ep *ep)
+{
+ struct crg_udc_ep *udc_ep;
+ struct crg_gadget_dev *crg_udc;
+ struct ep_cx_s *p_ep_cx;
+ int ep_state;
+ struct crg_uccr *uccr;
+ unsigned long flags = 0;
+
+ if (!ep)
+ return -EINVAL;
+
+ udc_ep = container_of(ep, struct crg_udc_ep, usb_ep);
+ crg_udc = udc_ep->crg_udc;
+
+ if (udc_ep->DCI == 0)
+ return 0;
+
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+
+ uccr = crg_udc->uccr;
+ p_ep_cx = (struct ep_cx_s *)crg_udc->p_epcx + udc_ep->DCI - 2;
+
+ ep_state = get_ep_state(crg_udc, udc_ep->DCI);
+ if (ep_state == EP_STATE_DISABLED) {
+ /* get here if ep is already disabled */
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return -EINVAL;
+ }
+
+ dev_dbg(crg_udc->dev, "EPDCI = 0x%x\n", udc_ep->DCI);
+
+ /*Maybe we need to halt ep before ep disable*/
+
+ writel(0x1 << udc_ep->DCI, &uccr->ep_enable);
+
+ /* clean up the request queue */
+ nuke(udc_ep, -ESHUTDOWN);
+
+ /* decrement ep counters */
+ crg_udc->num_enabled_eps--;
+
+ udc_ep->desc = NULL;
+
+ /* clean up the endpoint context */
+ memset(p_ep_cx, 0, sizeof(struct ep_cx_s));
+
+ dev_dbg(crg_udc->dev, "num_enabled_eps = %d\n", crg_udc->num_enabled_eps);
+
+ /* If device state was changed to default by port
+ * reset, should not overwrite it again
+ */
+ if (crg_udc->num_enabled_eps == 0 &&
+ crg_udc->device_state == USB_STATE_CONFIGURED) {
+ dev_dbg(crg_udc->dev, "Device State USB_STATE_CONFIGURED\n");
+ dev_dbg(crg_udc->dev, "Set Device State to addressed\n");
+ crg_udc->device_state = USB_STATE_ADDRESS;
+
+ }
+
+ udc_ep->ep_state = EP_STATE_DISABLED;
+
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+
+ return 0;
+}
+
+static int crg_udc_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct crg_udc_ep *udc_ep;
+ struct crg_gadget_dev *crg_udc;
+ u32 param0;
+ unsigned long flags = 0;
+ struct ep_cx_s *epcx;
+ struct crg_uccr *uccr;
+
+ if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT)
+ return -EINVAL;
+
+ udc_ep = container_of(ep, struct crg_udc_ep, usb_ep);
+
+ /*ep0 is always running*/
+ if (udc_ep->DCI == 0)
+ return 0;
+
+ crg_udc = udc_ep->crg_udc;
+ uccr = crg_udc->uccr;
+
+ if (!crg_udc->gadget_driver)
+ return -ESHUTDOWN;
+
+ dev_dbg(crg_udc->dev, "%s DCI = %d\n", __func__, udc_ep->DCI);
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+
+ /*crg ep context start from ep1*/
+ if (get_ep_state(crg_udc, udc_ep->DCI) != EP_STATE_DISABLED) {
+ dev_dbg(crg_udc->dev, "%s disable first\n", __func__);
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ crg_udc_ep_disable(ep);
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+ }
+
+ udc_ep->desc = desc;
+ udc_ep->comp_desc = ep->comp_desc;
+
+ /* setup endpoint context for regular endpoint
+ * the endpoint context for control endpoint has been
+ * setted up in probe function
+ */
+ if (udc_ep->DCI) {
+ dev_dbg(crg_udc->dev, "ep_enable udc_ep->DCI = %d\n", udc_ep->DCI);
+
+ /* setup transfer ring */
+ if (!udc_ep->tran_ring_info.vaddr) {
+ dma_addr_t dma;
+ u32 ring_size = 0;
+ void *vaddr;
+ size_t len;
+
+ if (usb_endpoint_xfer_bulk(desc))
+ ring_size = CRGUDC_BULK_EP_TD_RING_SIZE;
+ else if (usb_endpoint_xfer_isoc(desc))
+ ring_size = CRGUDC_ISOC_EP_TD_RING_SIZE;
+ else if (usb_endpoint_xfer_int(desc))
+ ring_size = CRGUDC_INT_EP_TD_RING_SIZE;
+ len = ring_size * sizeof(struct transfer_trb_s);
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ vaddr = dma_alloc_coherent(crg_udc->dev, len,
+ &dma, GFP_ATOMIC);
+ if (!vaddr)
+ return -ENOMEM;
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+
+ udc_ep->tran_ring_info.vaddr = vaddr;
+ udc_ep->tran_ring_info.dma = dma;
+ udc_ep->tran_ring_info.len = len;
+ udc_ep->first_trb = vaddr;
+ udc_ep->last_trb = udc_ep->first_trb + ring_size - 1;
+ }
+ memset(udc_ep->first_trb, 0, udc_ep->tran_ring_info.len);
+ /* Ensure that transfer ring is updated */
+ wmb();
+ setup_link_trb(udc_ep->last_trb, true,
+ udc_ep->tran_ring_info.dma);
+
+ udc_ep->enq_pt = udc_ep->first_trb;
+ udc_ep->deq_pt = udc_ep->first_trb;
+ udc_ep->pcs = 1;
+ udc_ep->tran_ring_full = false;
+ crg_udc->num_enabled_eps++;
+ crg_udc_epcx_setup(udc_ep);
+ }
+
+ dev_dbg(crg_udc->dev, "num_enabled_eps = %d\n", crg_udc->num_enabled_eps);
+
+ epcx = (struct ep_cx_s *)(crg_udc->p_epcx + udc_ep->DCI - 2);
+
+ param0 = (0x1 << udc_ep->DCI);
+ crg_issue_command(crg_udc, CRG_CMD_CONFIG_EP, param0, 0);
+
+ dev_dbg(crg_udc->dev, "config ep and start, DCI=%d\n", udc_ep->DCI);
+ if (crg_udc->device_state == USB_STATE_ADDRESS)
+ crg_udc->device_state = USB_STATE_CONFIGURED;
+
+ udc_ep->wedge = 0;
+ udc_ep->ep_state = EP_STATE_RUNNING;
+
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+
+ return 0;
+}
+
+static struct usb_request *
+crg_udc_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct crg_udc_request *udc_req_ptr = NULL;
+
+ udc_req_ptr = kzalloc(sizeof(*udc_req_ptr), gfp_flags);
+
+
+ if (!udc_req_ptr)
+ return NULL;
+
+ udc_req_ptr->usb_req.dma = DMA_ADDR_INVALID;
+ INIT_LIST_HEAD(&udc_req_ptr->queue);
+
+ return &udc_req_ptr->usb_req;
+}
+
+static void crg_udc_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct crg_udc_request *udc_req_ptr = NULL;
+
+ if (!_ep || !_req)
+ return;
+
+ udc_req_ptr = container_of(_req, struct crg_udc_request, usb_req);
+ kfree(udc_req_ptr);
+}
+
+static int
+crg_udc_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct crg_udc_request *udc_req_ptr;
+ struct crg_udc_ep *udc_ep_ptr;
+ struct crg_gadget_dev *crg_udc;
+ int status;
+ unsigned long flags = 0;
+ int dma_data_dir = 0;
+
+
+ if (!_req || !_ep)
+ return -EINVAL;
+
+ udc_req_ptr = container_of(_req, struct crg_udc_request, usb_req);
+ if (!udc_req_ptr)
+ return -EINVAL;
+
+ udc_ep_ptr = container_of(_ep, struct crg_udc_ep, usb_ep);
+ if (!udc_ep_ptr)
+ return -EINVAL;
+
+ crg_udc = udc_ep_ptr->crg_udc;
+ if (!crg_udc)
+ return -EINVAL;
+
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+
+ if (!udc_ep_ptr->first_trb ||
+ !udc_req_ptr->usb_req.complete ||
+ (!udc_req_ptr->usb_req.buf && !udc_req_ptr->usb_req.num_sgs) ||
+ !list_empty(&udc_req_ptr->queue)) {
+ dev_dbg(crg_udc->dev, "%s, invalid usbrequest\n", __func__);
+ if (!udc_ep_ptr->first_trb)
+ dev_err(crg_udc->dev, "%s, no first_trb\n", __func__);
+
+ if (!udc_req_ptr->usb_req.complete)
+ dev_err(crg_udc->dev, "%s, no complete\n", __func__);
+
+ if (!udc_req_ptr->usb_req.buf)
+ dev_err(crg_udc->dev, "%s, no req buf\n", __func__);
+
+ if (!list_empty(&udc_req_ptr->queue))
+ dev_err(crg_udc->dev, "%s, list not empty\n", __func__);
+
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return -EINVAL;
+ }
+ dev_dbg(crg_udc->dev, "enqueue EPDCI = 0x%x\n", udc_ep_ptr->DCI);
+ dev_dbg(crg_udc->dev, "udc_req buf = 0x%p\n", udc_req_ptr->usb_req.buf);
+
+ if (!udc_ep_ptr->desc) {
+ dev_dbg(crg_udc->dev, "udc_ep_ptr->Desc is null\n");
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return -EINVAL;
+ }
+ /* Ensure that req_ptr is updated */
+ wmb();
+ /* Clearing the Values of the UDC_REQUEST container */
+ clear_req_container(udc_req_ptr);
+ udc_req_ptr->mapped = 0;
+
+ if (usb_endpoint_xfer_control(udc_ep_ptr->desc) &&
+ _req->length == 0) {
+ crg_udc->setup_status = STATUS_STAGE_XFER;
+ status = -EINPROGRESS;
+ dev_dbg(crg_udc->dev, "udc_req_ptr = 0x%p\n", udc_req_ptr);
+
+ build_ep0_status(&crg_udc->udc_ep[0], false, status,
+ udc_req_ptr, 0, 0);
+ dev_dbg(crg_udc->dev, "act status request for control endpoint\n");
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return 0;
+ }
+
+ /* request length is possible to be 0. Like SCSI blank command */
+ dev_dbg(crg_udc->dev, "request length=%d\n", _req->length);
+
+ if (udc_req_ptr->usb_req.num_sgs) {
+ int n;
+
+ dev_dbg(crg_udc->dev, "udc_req_ptr->usb_req.num_sgs = %d\n",
+ udc_req_ptr->usb_req.num_sgs);
+ dev_dbg(crg_udc->dev, "udc_req_ptr->usb_req.sg->length = %d\n",
+ udc_req_ptr->usb_req.sg->length);
+ dma_data_dir = (usb_endpoint_dir_in(udc_ep_ptr->desc)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ n = dma_map_sg(crg_udc->dev, udc_req_ptr->usb_req.sg,
+ udc_req_ptr->usb_req.num_sgs, dma_data_dir);
+ if (n <= 0) {
+ dev_err(crg_udc->dev, "dma_map_sg fail, ret is %d\n", n);
+ dump_stack();
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return -EINVAL;
+ }
+ if (n != udc_req_ptr->usb_req.num_sgs) {
+ dev_err(crg_udc->dev, "URB_DMA_SG_COMBINED we not support\n");
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return -EAGAIN;
+ }
+ udc_req_ptr->usb_req.num_mapped_sgs = n;
+ dev_dbg(crg_udc->dev, "dma_map_sg done , usb_req.num_mapped_sgs = %d\n", n);
+
+
+ } else if (udc_req_ptr->usb_req.sg) {
+ dev_err(crg_udc->dev, "num_sgs = 0, but udc_req_ptr->usb_req.sg is not NULL\n");
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return -EINVAL;
+
+ } else {
+ if (udc_req_ptr->usb_req.dma == DMA_ADDR_INVALID && _req->length != 0) {
+ if (usb_endpoint_xfer_control(udc_ep_ptr->desc)) {
+ if (crg_udc->setup_status == DATA_STAGE_XFER ||
+ crg_udc->setup_status == STATUS_STAGE_XFER)
+ dma_data_dir = DMA_TO_DEVICE;
+
+ if (crg_udc->setup_status == DATA_STAGE_RECV ||
+ crg_udc->setup_status == STATUS_STAGE_RECV)
+ dma_data_dir = DMA_FROM_DEVICE;
+ } else {
+ dma_data_dir = (usb_endpoint_dir_in(udc_ep_ptr->desc)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ }
+ udc_req_ptr->usb_req.dma =
+ dma_map_single(crg_udc->dev, udc_req_ptr->usb_req.buf,
+ udc_req_ptr->usb_req.length,
+ dma_data_dir);
+
+
+ udc_req_ptr->mapped = 1;
+
+ dev_dbg(crg_udc->dev, "dma_map_single and mapped is 1\n");
+ }
+ }
+
+ udc_req_ptr->usb_req.status = -EINPROGRESS;
+ udc_req_ptr->usb_req.actual = 0;
+
+ /* If the transfer ring for this particular end point is full,
+ * then simply queue the request and return
+ */
+ if (udc_ep_ptr->tran_ring_full) {
+ status = 0;
+ } else {
+ /* push the request to the transfer ring if possible. */
+ status = crg_udc_build_td(udc_ep_ptr, udc_req_ptr);
+ }
+ if (!status)
+ list_add_tail(&udc_req_ptr->queue, &udc_ep_ptr->queue);
+
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+
+ return status;
+}
+
+static int
+crg_udc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct crg_udc_ep *udc_ep_ptr;
+ struct crg_gadget_dev *crg_udc;
+ struct crg_uccr *uccr;
+ u32 tmp = 0;
+ struct crg_udc_request *udc_req;
+ struct ep_cx_s *p_ep_cx;
+ int old_ep_state;
+ struct transfer_trb_s *pause_pt;
+ u32 deq_pt_lo, deq_pt_hi;
+ u64 dq_pt_addr;
+ u8 DCI;
+ unsigned long flags = 0;
+
+ if (!_ep || !_req)
+ return -EINVAL;
+
+ udc_ep_ptr = container_of(_ep, struct crg_udc_ep, usb_ep);
+ crg_udc = udc_ep_ptr->crg_udc;
+ uccr = crg_udc->uccr;
+ DCI = udc_ep_ptr->DCI;
+
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+
+ dev_dbg(crg_udc->dev, "%s EPDCI = 0x%x\n", __func__, DCI);
+ old_ep_state = get_ep_state(crg_udc, DCI);
+ if (DCI != 0 && old_ep_state == EP_STATE_RUNNING) {
+ u32 param0;
+
+ dev_dbg(crg_udc->dev, "%s, EP_STATE_RUNNING\n", __func__);
+ param0 = (0x1 << DCI);
+ /* stop the DMA from HW first */
+ crg_issue_command(crg_udc, CRG_CMD_STOP_EP, param0, 0);
+ do {
+ tmp = readl(&uccr->ep_running);
+ } while ((tmp & param0) != 0);
+ udc_ep_ptr->ep_state = EP_STATE_STOPPED;
+ }
+
+ list_for_each_entry(udc_req, &udc_ep_ptr->queue, queue) {
+ if (&udc_req->usb_req == _req)
+ break;
+ }
+
+ if (&udc_req->usb_req != _req) {
+ dev_dbg(crg_udc->dev, "did not find the request in request queue\n");
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return -EINVAL;
+ }
+ /* Request hasn't been queued to transfer ring yet
+ * dequeue it from sw queue only
+ */
+ if (!udc_req->first_trb) {
+ req_done(udc_ep_ptr, udc_req, -ECONNRESET);
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return 0;
+ }
+
+ /* ep0 cannot be stopped. So if request has already been queued to
+ * transfer ring, it cannot be dequeued
+ */
+ if (DCI == 0) {
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return -EINVAL;
+ }
+
+ p_ep_cx = crg_udc->p_epcx + DCI - 2;
+ deq_pt_lo = p_ep_cx->dw2 & EP_CX_TR_DQPT_LO_MASK;
+ deq_pt_hi = p_ep_cx->dw3;
+ dq_pt_addr = (u64)deq_pt_lo + ((u64)deq_pt_hi << 32);
+ pause_pt = tran_trb_dma_to_virt(udc_ep_ptr, dq_pt_addr);
+
+ dev_dbg(crg_udc->dev, "dequeue pause_pt = 0x%p, first_trb = 0x%p\n",
+ pause_pt, udc_req->first_trb);
+ dev_dbg(crg_udc->dev, "dequeue deq_pt = 0x%p, enq_pt = 0x%p\n",
+ udc_ep_ptr->deq_pt, udc_ep_ptr->enq_pt);
+
+ if (is_pointer_less_than(pause_pt, udc_req->first_trb, udc_ep_ptr)) {
+ dev_dbg(crg_udc->dev, "squeeze_xfer_ring\n");
+ /* HW hasn't process the request yet */
+ squeeze_xfer_ring(udc_ep_ptr, udc_req);
+ req_done(udc_ep_ptr, udc_req, -ECONNRESET);
+ } else if (udc_req->last_trb &&
+ is_pointer_less_than(udc_req->last_trb, pause_pt, udc_ep_ptr)) {
+ /* Request has been completed by HW
+ * There must be transfer events pending in event ring, and
+ * it will be processed later once interrupt context gets spin
+ * lock.
+ * Gadget driver free the request without checking the return
+ * value of usb_ep_dequeue, so we have to complete the request
+ * here and drop the transfer event later.
+ */
+ dev_dbg(crg_udc->dev, " Request has been complete by HW, reject request\n");
+ req_done(udc_ep_ptr, udc_req, -ECONNRESET);
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return -EINVAL;
+
+ } else {
+ /* Request has been partially completed by HW */
+
+ dev_dbg(crg_udc->dev, " Request has been partially completed by HW\n");
+ /*udc_req->usb_req.actual = actual_data_xfered(udc_ep, udc_req);*/
+
+ dev_dbg(crg_udc->dev, "%s, complete requests\n", __func__);
+ req_done(udc_ep_ptr, udc_req, -ECONNRESET);
+
+ advance_dequeue_pt(udc_ep_ptr);
+ crg_udc_epcx_update_dqptr(udc_ep_ptr);
+
+ /* For big TD, we generated completion event every 5 TRBS.
+ * So, we do not need to update sw dequeue pointer here.
+ * Wait for interrupt context to update it.
+ * Do not need to queue more trbs also.
+ */
+ }
+
+ dev_dbg(crg_udc->dev, "End dequeue deq_pt = 0x%p, enq_pt = 0x%p\n",
+ udc_ep_ptr->deq_pt, udc_ep_ptr->enq_pt);
+
+ /* knock doorbell and resume data transfer */
+ if (old_ep_state == EP_STATE_RUNNING) {
+ tmp = DCI;
+ tmp = CRG_U3DC_DB_TARGET(tmp);
+
+ knock_doorbell(crg_udc, udc_ep_ptr->DCI);
+
+ udc_ep_ptr->ep_state = EP_STATE_RUNNING;
+ }
+
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+
+ return 0;
+}
+
+static int crg_udc_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct crg_udc_ep *udc_ep_ptr;
+ int status;
+ unsigned long flags = 0;
+ struct crg_gadget_dev *crg_udc;
+
+ if (!_ep)
+ return -EINVAL;
+
+ udc_ep_ptr = container_of(_ep, struct crg_udc_ep, usb_ep);
+ crg_udc = udc_ep_ptr->crg_udc;
+
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+
+ if (value && usb_endpoint_dir_in(udc_ep_ptr->desc) &&
+ !list_empty(&udc_ep_ptr->queue)) {
+ dev_err(crg_udc->dev, "set_halt: list not empty\n");
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ return -EAGAIN;
+ }
+
+ status = ep_halt(udc_ep_ptr, value, 1);
+
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+
+ return status;
+}
+
+static int crg_udc_ep_set_wedge(struct usb_ep *_ep)
+{
+ struct crg_udc_ep *udc_ep_ptr;
+ int status;
+ unsigned long flags = 0;
+ struct crg_gadget_dev *crg_udc;
+
+ if (!_ep)
+ return -EINVAL;
+
+ udc_ep_ptr = container_of(_ep, struct crg_udc_ep, usb_ep);
+ crg_udc = udc_ep_ptr->crg_udc;
+
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+
+ udc_ep_ptr->wedge = 1;
+
+ status = ep_halt(udc_ep_ptr, 1, 1);
+
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+
+ return status;
+}
+
+static struct usb_ep_ops crg_udc_ep_ops = {
+ .enable = crg_udc_ep_enable,
+ .disable = crg_udc_ep_disable,
+ .alloc_request = crg_udc_alloc_request,
+ .free_request = crg_udc_free_request,
+ .queue = crg_udc_ep_queue,
+ .dequeue = crg_udc_ep_dequeue,
+ .set_halt = crg_udc_ep_set_halt,
+ .set_wedge = crg_udc_ep_set_wedge,
+};
+
+static void crg_ep_struct_setup(struct crg_gadget_dev *crg_udc,
+ u32 DCI, const char *name)
+{
+ struct crg_udc_ep *ep = &crg_udc->udc_ep[DCI];
+
+ ep->DCI = DCI;
+
+ if (ep->DCI > 1) {
+ strscpy_pad(ep->name, name, sizeof(ep->name) - 1);
+ ep->usb_ep.name = ep->name;
+ ep->usb_ep.maxpacket = 1024;
+ ep->usb_ep.max_streams = 16;
+
+ ep->usb_ep.caps.type_iso = 1;
+ ep->usb_ep.caps.type_bulk = 1;
+ ep->usb_ep.caps.type_int = 1;
+ ep->usb_ep.caps.dir_in = 1;
+ ep->usb_ep.caps.type_control = 1;
+
+ ep->usb_ep.caps.type_iso = 1;
+ ep->usb_ep.caps.type_bulk = 1;
+ ep->usb_ep.caps.type_int = 1;
+ ep->usb_ep.caps.type_control = 1;
+ if (ep->DCI % 2)
+ ep->usb_ep.caps.dir_out = 1;
+ else
+ ep->usb_ep.caps.dir_in = 1;
+ usb_ep_set_maxpacket_limit(&ep->usb_ep, MAX_PACKET_SIZE);
+ } else {
+ strscpy(ep->name, "ep0", sizeof(ep->name) - 1);
+ ep->usb_ep.name = ep->name;
+ ep->usb_ep.maxpacket = 512;
+ ep->usb_ep.caps.type_control = 1;
+ ep->usb_ep.caps.dir_in = 1;
+ ep->usb_ep.caps.dir_out = 1;
+ }
+
+ dev_dbg(crg_udc->dev, "ep = 0x%p, ep name = %s maxpacket = %d DCI=%d\n",
+ ep, ep->name, ep->usb_ep.maxpacket, ep->DCI);
+ ep->usb_ep.ops = &crg_udc_ep_ops;
+ ep->crg_udc = crg_udc;
+
+ INIT_LIST_HEAD(&ep->queue);
+ if (ep->DCI > 1)
+ list_add_tail(&ep->usb_ep.ep_list, &crg_udc->gadget.ep_list);
+}
+
+
+static void enable_setup_event(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_uccr *uccr = crg_udc->uccr;
+ u32 val;
+
+ dev_dbg(crg_udc->dev, "before setup en config1[0x%p]=0x%x\n",
+ &uccr->config1, readl(&uccr->config1));
+
+ val = readl(&uccr->config1);
+ val |= CRG_U3DC_CFG1_SETUP_EVENT_EN;
+ writel(val, &uccr->config1);
+ dev_dbg(crg_udc->dev, "update config1[0x%p]=0x%x\n",
+ &uccr->config1, readl(&uccr->config1));
+}
+
+static int is_event_ring_x_empty(struct crg_gadget_dev *crg_udc, int index)
+{
+ struct event_trb_s *event;
+ struct crg_udc_event *udc_event;
+
+ udc_event = &crg_udc->udc_event[index];
+ if (udc_event->evt_dq_pt) {
+ event = (struct event_trb_s *)udc_event->evt_dq_pt;
+
+ if (GETF(EVE_TRB_CYCLE_BIT, event->dw3) !=
+ udc_event->CCS)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int is_event_rings_empty(struct crg_gadget_dev *crg_udc)
+{
+ int i;
+
+ for (i = 0; i < CRG_RING_NUM; i++) {
+ if (!is_event_ring_x_empty(crg_udc, i)) {
+ dev_err(crg_udc->dev, "%s evt ring not empty\n", __func__);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int enable_setup(struct crg_gadget_dev *crg_udc)
+{
+ enable_setup_event(crg_udc);
+ crg_udc->device_state = USB_STATE_DEFAULT;
+ crg_udc->setup_status = WAIT_FOR_SETUP;
+ dev_dbg(crg_udc->dev, "%s ready to receive setup events\n", __func__);
+
+ return 0;
+}
+
+static int prepare_for_setup(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_udc_ep *udc_ep0_ptr;
+
+#ifdef REINIT_EP0_ON_BUS_RESET
+ dma_addr_t dqptaddr;
+ u32 cmd_param0;
+ u32 cmd_param1;
+#endif
+
+ if (!is_event_rings_empty(crg_udc) ||
+ crg_udc->portsc_on_reconnecting == 1)
+ return -EBUSY;
+
+ udc_ep0_ptr = &crg_udc->udc_ep[0];
+/* If we reinit ep0 on bus reset, we just make ep0 dequeue pointer align
+ * with enqueue pointer, all remaining xfer trbs became dumb ones which
+ * will not produce xfer event anymore.
+ *
+ * If we considering the opposite solution, we should wait all ep0 xfer
+ * trbs be completed(with some err complete code)
+ */
+#ifdef REINIT_EP0_ON_BUS_RESET
+ /* Complete any reqs on EP0 queue */
+ nuke(udc_ep0_ptr, -ESHUTDOWN);
+
+ crg_udc->ctrl_req_enq_idx = 0;
+ memset(crg_udc->ctrl_req_queue, 0,
+ sizeof(struct crg_setup_packet) * CTRL_REQ_QUEUE_DEPTH);
+
+ /*context related ops*/
+ dqptaddr = tran_trb_virt_to_dma(udc_ep0_ptr, udc_ep0_ptr->enq_pt);
+ cmd_param0 = (lower_32_bits(dqptaddr) &
+ CRG_CMD0_0_DQPTRLO_MASK) |
+ CRG_CMD0_0_DCS(udc_ep0_ptr->pcs);
+ cmd_param1 = upper_32_bits(dqptaddr);
+ crg_issue_command(crg_udc,
+ CRG_CMD_INIT_EP0, cmd_param0, cmd_param1);
+
+ /* TRY this solution*/
+ udc_ep0_ptr->deq_pt = udc_ep0_ptr->enq_pt;
+ udc_ep0_ptr->tran_ring_full = false;
+#else
+ if (!list_empty(&udc_ep0_ptr->queue)) {
+ dev_err(crg_udc->dev, "%s remaining ep0 xfer trbs on reset!\n", __func__);
+ return -EBUSY;
+ }
+#endif
+
+ enable_setup(crg_udc);
+
+ return 0;
+}
+
+static void update_ep0_maxpacketsize(struct crg_gadget_dev *crg_udc)
+{
+ u16 maxpacketsize = 0;
+ struct crg_udc_ep *udc_ep0 = &crg_udc->udc_ep[0];
+ u32 param0;
+
+ if (crg_udc->gadget.speed >= USB_SPEED_SUPER)
+ maxpacketsize = 512;
+ else
+ maxpacketsize = 64;
+
+ param0 = CRG_CMD1_0_MPS(maxpacketsize);
+ crg_issue_command(crg_udc, CRG_CMD_UPDATE_EP0_CFG, param0, 0);
+
+ crg_udc_ep0_desc.wMaxPacketSize = cpu_to_le16(maxpacketsize);
+ udc_ep0->usb_ep.maxpacket = maxpacketsize;
+}
+
+static int init_event_ring(struct crg_gadget_dev *crg_udc, int index)
+{
+ struct crg_uicr *uicr = crg_udc->uicr[index];
+ struct crg_udc_event *udc_event = &crg_udc->udc_event[index];
+
+ int ret;
+ u32 buff_length;
+ dma_addr_t mapping;
+
+ buff_length = CRG_ERST_SIZE * sizeof(struct erst_s);
+ if (!udc_event->erst.vaddr) {
+ udc_event->erst.vaddr =
+ dma_alloc_coherent(crg_udc->dev, buff_length,
+ &mapping, GFP_KERNEL);
+
+ if (!udc_event->erst.vaddr) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ } else {
+ mapping = udc_event->erst.dma;
+ }
+
+ udc_event->erst.len = buff_length;
+ udc_event->erst.dma = mapping;
+ udc_event->p_erst = udc_event->erst.vaddr;
+
+ buff_length = CRG_EVENT_RING_SIZE * sizeof(struct event_trb_s);
+ if (!udc_event->event_ring.vaddr) {
+ udc_event->event_ring.vaddr =
+ dma_alloc_coherent(crg_udc->dev, buff_length,
+ &mapping, GFP_KERNEL);
+
+ if (!udc_event->event_ring.vaddr) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ } else {
+ mapping = udc_event->event_ring.dma;
+ }
+
+ udc_event->event_ring.len = buff_length;
+ udc_event->event_ring.dma = mapping;
+ udc_event->evt_dq_pt = udc_event->event_ring.vaddr;
+
+ udc_event->evt_seg0_last_trb =
+ (struct event_trb_s *)(udc_event->event_ring.vaddr)
+ + (CRG_EVENT_RING_SIZE - 1);
+
+ udc_event->CCS = 1;
+
+ udc_event->p_erst->seg_addr_lo =
+ lower_32_bits(udc_event->event_ring.dma);
+ udc_event->p_erst->seg_addr_hi =
+ upper_32_bits(udc_event->event_ring.dma);
+ udc_event->p_erst->seg_size = cpu_to_le32(CRG_EVENT_RING_SIZE);
+ udc_event->p_erst->rsvd = 0;
+ /* Ensure that event ring is updated */
+ wmb();
+ /*clear the event ring, to avoid hw unexpected ops
+ *because of dirty data
+ */
+ memset(udc_event->event_ring.vaddr, 0, buff_length);
+
+ /*hw related ops ERSTBA && ERSTSZ && ERDP*/
+ /* Ensure that event ring is clear */
+ wmb();
+
+ /**************************/
+ writel(CRG_ERST_SIZE, &uicr->erstsz);
+ writel(lower_32_bits(udc_event->erst.dma), &uicr->erstbalo);
+ writel(upper_32_bits(udc_event->erst.dma), &uicr->erstbahi);
+ writel(lower_32_bits(udc_event->event_ring.dma) | CRG_U3DC_ERDPLO_EHB,
+ &uicr->erdplo);
+ writel(upper_32_bits(udc_event->event_ring.dma), &uicr->erdphi);
+
+ writel((CRG_U3DC_IMAN_INT_EN | CRG_U3DC_IMAN_INT_PEND), &uicr->iman);
+ writel((0L << 0) | (4000L << 0), &uicr->imod);
+
+ return 0;
+}
+
+static int init_device_context(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_uccr *uccr = crg_udc->uccr;
+ int ret;
+ u32 buff_length;
+ dma_addr_t mapping;
+
+ /*ep0 is not included in ep contexts in crg udc*/
+ buff_length = (CRG_NUM_EP_CX - 2) * sizeof(struct ep_cx_s);
+
+ if (!crg_udc->ep_cx.vaddr) {
+ crg_udc->ep_cx.vaddr =
+ dma_alloc_coherent(crg_udc->dev, buff_length,
+ &mapping, GFP_KERNEL);
+ /* Ensure that ep_cx.vaddr is updated */
+ wmb();
+ if (!crg_udc->ep_cx.vaddr) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ } else {
+ mapping = crg_udc->ep_cx.dma;
+ }
+
+ crg_udc->p_epcx = crg_udc->ep_cx.vaddr;
+ crg_udc->ep_cx.len = buff_length;
+ crg_udc->ep_cx.dma = mapping;
+
+ /*hw ops DCBAPLO DCBAPHI*/
+ writel(lower_32_bits(crg_udc->ep_cx.dma), &uccr->dcbaplo);
+ writel(upper_32_bits(crg_udc->ep_cx.dma), &uccr->dcbaphi);
+
+ dev_dbg(crg_udc->dev, "dcbaplo[0x%p]=0x%x\n", &uccr->dcbaplo, readl(&uccr->dcbaplo));
+ dev_dbg(crg_udc->dev, "dcbaphi[0x%p]=0x%x\n", &uccr->dcbaphi, readl(&uccr->dcbaphi));
+
+ return 0;
+}
+
+static int reset_data_struct(struct crg_gadget_dev *crg_udc)
+{
+ u32 tmp;
+ int i;
+ struct crg_uccr *uccr = crg_udc->uccr;
+ u32 val;
+
+ val = readl(&uccr->control);
+ val &= (~(CRG_U3DC_CTRL_INT_EN | CRG_U3DC_CTRL_RUN));
+ writel(val, &uccr->control);
+
+ dev_dbg(crg_udc->dev, "capability[0x%p]=0x%x\n", &uccr->capability,
+ readl(&uccr->capability));
+
+ switch (crg_udc->gadget.max_speed) {
+ case USB_SPEED_FULL:
+ tmp = CRG_U3DC_CFG0_MAXSPEED_FS;
+ break;
+ case USB_SPEED_HIGH:
+ tmp = CRG_U3DC_CFG0_MAXSPEED_HS;
+ break;
+ case USB_SPEED_SUPER:
+ tmp = CRG_U3DC_CFG0_MAXSPEED_SS;
+ break;
+ case USB_SPEED_SUPER_PLUS:
+ tmp = CRG_U3DC_CFG0_MAXSPEED_SSP;
+ break;
+ case USB_SPEED_UNKNOWN:
+ default:
+ return -EINVAL;
+ }
+ writel(tmp, &uccr->config0);
+
+ for (i = 0; i < CRG_RING_NUM; i++)
+ init_event_ring(crg_udc, i);
+
+ init_device_context(crg_udc);
+
+ if (!crg_udc->status_req) {
+ crg_udc->status_req =
+ container_of(crg_udc_alloc_request(&crg_udc->udc_ep[0].usb_ep,
+ GFP_ATOMIC), struct crg_udc_request,
+ usb_req);
+ }
+
+ /*other hw ops*/
+ return 0;
+}
+
+static int init_ep0(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_udc_ep *udc_ep_ptr = &crg_udc->udc_ep[0];
+ u32 cmd_param0;
+ u32 cmd_param1;
+
+ /* setup transfer ring */
+ if (!udc_ep_ptr->tran_ring_info.vaddr) {
+ dma_addr_t dma;
+ u32 ring_size = CRGUDC_CONTROL_EP_TD_RING_SIZE;
+ void *vaddr;
+ size_t len;
+
+ len = ring_size * sizeof(struct transfer_trb_s);
+ vaddr = dma_alloc_coherent(crg_udc->dev, len,
+ &dma, GFP_KERNEL);
+ if (!vaddr)
+ return -ENOMEM;
+
+ udc_ep_ptr->tran_ring_info.vaddr = vaddr;
+ udc_ep_ptr->tran_ring_info.dma = dma;
+ udc_ep_ptr->tran_ring_info.len = len;
+ udc_ep_ptr->first_trb = vaddr;
+ udc_ep_ptr->last_trb = udc_ep_ptr->first_trb + ring_size - 1;
+ }
+
+ memset(udc_ep_ptr->first_trb, 0, udc_ep_ptr->tran_ring_info.len);
+ /* Ensure that transfer ring is updated */
+ wmb();
+ udc_ep_ptr->enq_pt = udc_ep_ptr->first_trb;
+ udc_ep_ptr->deq_pt = udc_ep_ptr->first_trb;
+ udc_ep_ptr->pcs = 1;
+ udc_ep_ptr->tran_ring_full = false;
+
+ setup_link_trb(udc_ep_ptr->last_trb,
+ true, udc_ep_ptr->tran_ring_info.dma);
+
+ /*context related ops*/
+ cmd_param0 = (lower_32_bits(udc_ep_ptr->tran_ring_info.dma) &
+ CRG_CMD0_0_DQPTRLO_MASK) |
+ CRG_CMD0_0_DCS(udc_ep_ptr->pcs);
+ cmd_param1 = upper_32_bits(udc_ep_ptr->tran_ring_info.dma);
+
+ dev_dbg(crg_udc->dev, "ep0 ring dma addr = 0x%llx\n", udc_ep_ptr->tran_ring_info.dma);
+
+ dev_dbg(crg_udc->dev, "ep0 ring vaddr = 0x%p\n", udc_ep_ptr->tran_ring_info.vaddr);
+
+ dev_dbg(crg_udc->dev, "INIT EP0 CMD, par0=0x%x, par1=0x%x\n", cmd_param0, cmd_param1);
+
+ crg_issue_command(crg_udc, CRG_CMD_INIT_EP0, cmd_param0, cmd_param1);
+
+ udc_ep_ptr->ep_state = EP_STATE_RUNNING;
+
+ return 0;
+}
+
+static int EP0_Start(struct crg_gadget_dev *crg_udc)
+{
+ crg_udc->udc_ep[0].desc = &crg_udc_ep0_desc;
+
+ return 0;
+}
+
+static void crg_udc_start(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_uccr *uccr;
+ u32 val;
+
+ uccr = crg_udc->uccr;
+
+ /*****interrupt related*****/
+ val = readl(&uccr->config1);
+ val |= (CRG_U3DC_CFG1_CSC_EVENT_EN |
+ CRG_U3DC_CFG1_PEC_EVENT_EN |
+ CRG_U3DC_CFG1_PPC_EVENT_EN |
+ CRG_U3DC_CFG1_PRC_EVENT_EN |
+ CRG_U3DC_CFG1_PLC_EVENT_EN |
+ CRG_U3DC_CFG1_CEC_EVENT_EN);
+ writel(val, &uccr->config1);
+ dev_dbg(crg_udc->dev, "config1[0x%p]=0x%x\n", &uccr->config1, readl(&uccr->config1));
+ dev_dbg(crg_udc->dev, "config0[0x%p]=0x%x\n", &uccr->config0, readl(&uccr->config0));
+
+ val = readl(&uccr->control);
+ val |= (CRG_U3DC_CTRL_SYSERR_EN |
+ CRG_U3DC_CTRL_INT_EN);
+ writel(val, &uccr->control);
+ /*****interrupt related end*****/
+
+ val = readl(&uccr->control);
+ val |= CRG_U3DC_CTRL_RUN;
+ writel(val, &uccr->control);
+ dev_dbg(crg_udc->dev, "%s, control=0x%x\n", __func__, readl(&uccr->control));
+}
+
+static void crg_udc_clear_portpm(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_uccr *uccr = crg_udc->uccr;
+ u32 tmp;
+
+ tmp = readl(&uccr->u3portpmsc);
+
+ tmp &= (~CRG_U3DC_U3PORTPM_U1IEN);
+ tmp = SETF_VAR(CRG_U3DC_U3PORTPM_U1TMOUT, tmp, 0);
+ tmp &= (~CRG_U3DC_U3PORTPM_U2IEN);
+ tmp = SETF_VAR(CRG_U3DC_U3PORTPM_U2TMOUT, tmp, 0);
+
+ writel(tmp, &uccr->u3portpmsc);
+
+ crg_udc->feature_u1_enable = 0;
+ crg_udc->feature_u2_enable = 0;
+}
+
+static void crg_udc_reinit(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_uccr *uccr = crg_udc->uccr;
+ u32 i, tmp;
+ unsigned long flags = 0;
+ struct crg_udc_ep *udc_ep_ptr;
+
+ crg_udc->setup_status = WAIT_FOR_SETUP;
+ /* Base on Figure 9-1, default USB_STATE is attached */
+ crg_udc->device_state = USB_STATE_RECONNECTING;
+
+ /* halt all the endpoints */
+
+ dev_dbg(crg_udc->dev, "ep_enable=0x%x\n", readl(&uccr->ep_enable));
+ dev_dbg(crg_udc->dev, "ep_running=0x%x\n", readl(&uccr->ep_running));
+
+ /* disable all the endpoints */
+ tmp = readl(&uccr->ep_enable);
+ writel(tmp, &uccr->ep_enable);
+ for (i = 0; i < 50; i++) {
+ tmp = readl(&uccr->ep_enable);
+ if (tmp == 0)
+ break;
+ }
+
+ dev_dbg(crg_udc->dev, "i=%d\n", i);
+ dev_dbg(crg_udc->dev, "after ep_enable=0x%x\n", readl(&uccr->ep_enable));
+
+ for (i = 2; i < 32; i++) {
+ udc_ep_ptr = &crg_udc->udc_ep[i];
+ udc_ep_ptr->usb_ep.enabled = 0;
+ if (udc_ep_ptr->desc)
+ nuke(udc_ep_ptr, -ESHUTDOWN);
+ udc_ep_ptr->tran_ring_full = false;
+ udc_ep_ptr->ep_state = EP_STATE_DISABLED;
+ }
+ crg_udc->num_enabled_eps = 0;
+
+/* we don't handle ep0 here, we init_ep0 when event ring is empty*/
+
+ if (crg_udc->dev_addr != 0) {
+ u32 param0;
+
+ param0 = CRG_CMD2_0_DEV_ADDR(0);
+ crg_issue_command(crg_udc, CRG_CMD_SET_ADDR, param0, 0);
+ crg_udc->dev_addr = 0;
+ }
+
+ crg_udc_clear_portpm(crg_udc);
+
+ if (crg_udc->gadget_driver) {
+ dev_dbg(crg_udc->dev, "calling disconnect\n");
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ crg_udc->gadget_driver->disconnect(&crg_udc->gadget);
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+ }
+}
+
+static int crg_udc_reset(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_uccr *uccr = crg_udc->uccr;
+ u32 i, tmp, count;
+ struct crg_udc_ep *udc_ep_ptr;
+ u32 val;
+
+ dev_dbg(crg_udc->dev, "capability = %x\n", readl(&uccr->capability));
+
+ count = 0;
+ val = readl(&uccr->control);
+ val |= CRG_U3DC_CTRL_SWRST;
+ writel(val, &uccr->control);
+ do {
+ mdelay(10);
+ tmp = readl(&uccr->control);
+ count++;
+
+ if (count == 50) {
+ dev_err(crg_udc->dev, "reset error\n");
+ return -1;
+ }
+ } while ((tmp & CRG_U3DC_CTRL_SWRST) != 0);
+
+ crg_udc_clear_portpm(crg_udc);
+
+ crg_udc->setup_status = WAIT_FOR_SETUP;
+ /* Base on Figure 9-1, default USB_STATE is attached */
+ crg_udc->device_state = USB_STATE_ATTACHED;
+ crg_udc->dev_addr = 0;
+
+ /* Complete any reqs on EP1-EP15 queue */
+ for (i = 2; i < 32; i++) {
+ udc_ep_ptr = &crg_udc->udc_ep[i];
+
+ if (udc_ep_ptr->desc)
+ nuke(udc_ep_ptr, -ESHUTDOWN);
+ udc_ep_ptr->tran_ring_full = false;
+ udc_ep_ptr->ep_state = EP_STATE_DISABLED;
+ }
+ crg_udc->num_enabled_eps = 0;
+
+ /* Complete any reqs on EP0 queue */
+ udc_ep_ptr = &crg_udc->udc_ep[0];
+ if (udc_ep_ptr->desc)
+ nuke(udc_ep_ptr, -ESHUTDOWN);
+
+ crg_udc->ctrl_req_enq_idx = 0;
+ memset(crg_udc->ctrl_req_queue, 0,
+ sizeof(struct crg_setup_packet) * CTRL_REQ_QUEUE_DEPTH);
+
+ return 0;
+}
+
+/************controller related ops*******************************/
+#define gadget_to_udc(g) (container_of(g, struct crg_gadget_dev, gadget))
+int g_dnl_board_usb_cable_connected(struct crg_gadget_dev *crg_udc);
+
+static int crg_gadget_pullup(struct usb_gadget *g, int is_on)
+{
+ struct crg_gadget_dev *crg_udc;
+
+ /* No need to call g_dnl_board_usb_cable_connected upon stop
+ * controller or pull down D- event.
+ */
+ if (is_on) {
+ crg_udc = gadget_to_udc(g);
+ g_dnl_board_usb_cable_connected(crg_udc);
+ }
+ return 0;
+}
+
+static int crg_vbus_detect_thread(void *data);
+
+static void crg_vbus_detect(struct crg_gadget_dev *crg_udc, int enable)
+{
+ if (enable) {
+ if (crg_udc->vbus_task) {
+ dev_err(crg_udc->dev,
+ "vbus task already run, wake up vbus_wait\n");
+ wake_up_interruptible(&vbus_wait);
+ return;
+ }
+ /* Enable the VBUS */
+ writel((readl(crg_udc->mmio_virt_base +
+ CRG_UDC_VENDOR_REG) |
+ CRG_UDC_VBUS_DETECT),
+ crg_udc->mmio_virt_base + CRG_UDC_VENDOR_REG);
+ crg_udc->vbus_task = kthread_run(crg_vbus_detect_thread,
+ (void *)crg_udc,
+ "corigine_vbus_thread");
+ if (IS_ERR(crg_udc->vbus_task)) {
+ dev_err(crg_udc->dev,
+ "Unable to create corigine_vbus_thread.\n");
+ crg_udc->vbus_task = NULL;
+ return;
+ }
+ } else {
+ if (crg_udc->vbus_task) {
+ /* Disable VBUS to stop controller */
+ writel((readl(crg_udc->mmio_virt_base +
+ CRG_UDC_VENDOR_REG) &
+ ~(CRG_UDC_VBUS_DETECT)),
+ crg_udc->mmio_virt_base + CRG_UDC_VENDOR_REG);
+ wake_up_interruptible(&vbus_wait);
+ kthread_stop(crg_udc->vbus_task);
+ crg_udc->vbus_task = NULL;
+ return;
+ }
+ }
+}
+
+static int crg_gadget_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
+{
+ struct crg_gadget_dev *crg_udc;
+
+ crg_udc = gadget_to_udc(g);
+ crg_udc->gadget_driver = driver;
+
+ dev_dbg(crg_udc->dev, "%s %d gadget speed=%d, max speed=%d\n",
+ __func__, __LINE__, g->speed, g->max_speed);
+ dev_dbg(crg_udc->dev, "%s %d driver speed=%d\n",
+ __func__, __LINE__, driver->max_speed);
+ crg_vbus_detect(crg_udc, 1);
+ return 0;
+}
+
+static int crg_gadget_stop(struct usb_gadget *g)
+{
+ struct crg_gadget_dev *crg_udc;
+ unsigned long flags = 0;
+
+ crg_udc = gadget_to_udc(g);
+
+ crg_udc->device_state = USB_STATE_ATTACHED;
+ crg_vbus_detect(crg_udc, 0);
+
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+
+ crg_udc_reset(crg_udc);
+
+ reset_data_struct(crg_udc);
+ crg_udc->connected = 0;
+ crg_udc->gadget_driver = NULL;
+ crg_udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+ init_ep0(crg_udc);
+
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops crg_gadget_ops = {
+ .pullup = crg_gadget_pullup,
+ .udc_start = crg_gadget_start,
+ .udc_stop = crg_gadget_stop,
+};
+
+static int init_ep_info(struct crg_gadget_dev *crg_udc)
+{
+ int i;
+
+ /*udc_ep[0] is reserved, */
+ crg_ep_struct_setup(crg_udc, 0, NULL);
+
+ for (i = 1; i < CRG_NUM_EP_CX / 2; i++) {
+ char name[14];
+
+ sprintf(name, "ep%din", i);
+ crg_ep_struct_setup(crg_udc, i * 2, name);
+ sprintf(name, "ep%dout", i);
+ crg_ep_struct_setup(crg_udc, i * 2 + 1, name);
+ }
+
+ return 0;
+}
+
+static void queue_setup_pkt(struct crg_gadget_dev *crg_udc,
+ struct usb_ctrlrequest *setup_pkt,
+ u16 setup_tag)
+{
+ if (crg_udc->ctrl_req_enq_idx == CTRL_REQ_QUEUE_DEPTH) {
+ dev_err(crg_udc->dev, "ctrl request queque is full\n");
+ return;
+ }
+
+ memcpy(&crg_udc->ctrl_req_queue[crg_udc->ctrl_req_enq_idx].usbctrlreq,
+ setup_pkt, sizeof(struct usb_ctrlrequest));
+ crg_udc->ctrl_req_queue[crg_udc->ctrl_req_enq_idx].setup_tag =
+ setup_tag;
+
+ crg_udc->ctrl_req_enq_idx++;
+}
+
+static inline u32 index2DCI(u16 index)
+{
+ if (index == 0)
+ return 0;
+
+ return (index & USB_ENDPOINT_NUMBER_MASK) * 2 + ((index &
+ USB_DIR_IN) ? 0 : 1);
+}
+
+static void get_status_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+ kfree(req->buf);
+}
+
+static void getstatusrequest(struct crg_gadget_dev *crg_udc,
+ u8 RequestType, u16 value, u16 index, u16 length)
+{
+ u32 status_val = 0;
+ u32 status = -EINPROGRESS;
+ struct crg_udc_ep *udc_ep_ptr;
+ struct crg_udc_request *udc_req_ptr = crg_udc->status_req;
+
+ if (!udc_req_ptr)
+ return;
+ udc_req_ptr->usb_req.buf = kzalloc(sizeof(u16), GFP_ATOMIC);
+ dev_dbg(crg_udc->dev, "udc_req_ptr->usb_req.buf = 0x%p\n", udc_req_ptr->usb_req.buf);
+ if (!udc_req_ptr->usb_req.buf)
+ return;
+
+ if (value || length > 2 || !length) {
+ status = -EINVAL;
+ goto get_status_error;
+ }
+
+ dev_dbg(crg_udc->dev, "Get status request RequestType = 0x%x Index=%x\n",
+ RequestType, index);
+ if ((RequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+ dev_dbg(crg_udc->dev, "Get status request Device request\n");
+ if (index) {
+ status = -EINVAL;
+ goto get_status_error;
+ }
+
+ if (crg_udc->gadget.speed == USB_SPEED_HIGH ||
+ crg_udc->gadget.speed == USB_SPEED_FULL) {
+ if (crg_udc->u2_RWE)
+ status_val |= BIT(USB_DEVICE_REMOTE_WAKEUP);
+ }
+
+ if (crg_udc->gadget.speed >= USB_SPEED_SUPER) {
+ if (crg_udc->feature_u1_enable)
+ status_val |= BIT(USB_DEV_STAT_U1_ENABLED);
+ if (crg_udc->feature_u2_enable)
+ status_val |= BIT(USB_DEV_STAT_U2_ENABLED);
+ }
+
+ status_val |= USB_DEVICE_SELF_POWERED;
+ dev_dbg(crg_udc->dev, "Status = 0x%x\n", status_val);
+
+ } else if ((RequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+ dev_dbg(crg_udc->dev, "Get status request Interface request\n");
+ status_val = 0;
+ } else if ((RequestType & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
+ u32 DCI;
+
+ DCI = index2DCI(index);
+
+ dev_dbg(crg_udc->dev, "Get status request endpoint request DCI = %d\n", DCI);
+
+ if (DCI == 1) {
+ status_val = 0;
+ dev_dbg(crg_udc->dev, "Get status request INVALID! DCI = %d\n", DCI);
+ goto get_status_error;
+ }
+ /* if device state is address state, index should be 0
+ * if device state is configured state, index should be an
+ * endpoint configured.
+ */
+
+ dev_dbg(crg_udc->dev, "crg_udc->device_state = %d\n", crg_udc->device_state);
+ if (crg_udc->device_state == USB_STATE_ADDRESS && DCI != 0) {
+ status = -EINVAL;
+ goto get_status_error;
+ }
+
+ if (crg_udc->device_state == USB_STATE_CONFIGURED) {
+ if (get_ep_state(crg_udc, DCI) == EP_STATE_DISABLED) {
+ status = -EINVAL;
+ goto get_status_error;
+ }
+
+ if (get_ep_state(crg_udc, DCI) == EP_STATE_HALTED) {
+ status_val = BIT(USB_ENDPOINT_HALT);
+ dev_dbg(crg_udc->dev, "endpoint was halted = 0x%lx\n",
+ (unsigned long)status_val);
+ }
+ }
+ }
+
+get_status_error:
+ if (status != -EINPROGRESS) {
+ udc_req_ptr->usb_req.length = 0;
+ } else {
+ *(u16 *)udc_req_ptr->usb_req.buf = cpu_to_le16(status_val);
+ dev_dbg(crg_udc->dev, "usb_req.buf = 0x%x\n",
+ *((u16 *)udc_req_ptr->usb_req.buf));
+
+ dev_dbg(crg_udc->dev, "usb_req.buf addr = 0x%p\n",
+ (udc_req_ptr->usb_req.buf));
+
+ udc_req_ptr->usb_req.length = 2;
+ }
+ udc_req_ptr->usb_req.status = status;
+ udc_req_ptr->usb_req.actual = 0;
+ udc_req_ptr->usb_req.complete = get_status_cmpl;
+
+ if (udc_req_ptr->usb_req.dma == DMA_ADDR_INVALID) {
+ udc_req_ptr->usb_req.dma =
+ dma_map_single(crg_udc->dev, udc_req_ptr->usb_req.buf,
+ udc_req_ptr->usb_req.length,
+ DMA_FROM_DEVICE);
+ udc_req_ptr->mapped = 1;
+ }
+ dev_dbg(crg_udc->dev, "status_val = 0x%x, cpu_to_le16(status_val) = 0x%x\n",
+ status_val, cpu_to_le16(status_val));
+ dev_dbg(crg_udc->dev, "udc_req_ptr->usb_req.buf = 0x%p, value = 0x%x\n",
+ udc_req_ptr->usb_req.buf, *(u16 *)(udc_req_ptr->usb_req.buf));
+ dev_dbg(crg_udc->dev, "udc_req_ptr->usb_req.dma = 0x%llx\n",
+ udc_req_ptr->usb_req.dma);
+
+ udc_ep_ptr = &crg_udc->udc_ep[0];
+
+ crg_udc->setup_status = DATA_STAGE_XFER;
+ status = crg_udc_build_td(udc_ep_ptr, udc_req_ptr);
+
+ dev_dbg(crg_udc->dev, "getstatus databuf eqpt = 0x%p\n", udc_ep_ptr->enq_pt);
+
+ if (!status)
+ list_add_tail(&udc_req_ptr->queue, &udc_ep_ptr->queue);
+}
+
+static void set_address_cmpl(struct crg_gadget_dev *crg_udc)
+{
+ if (crg_udc->device_state == USB_STATE_DEFAULT &&
+ crg_udc->dev_addr != 0) {
+ crg_udc->device_state = USB_STATE_ADDRESS;
+ dev_dbg(crg_udc->dev, "USB State Addressed\n");
+
+ } else if (crg_udc->device_state == USB_STATE_ADDRESS) {
+ if (crg_udc->dev_addr == 0)
+ crg_udc->device_state = USB_STATE_DEFAULT;
+ }
+}
+
+static void setaddressrequest(struct crg_gadget_dev *crg_udc,
+ u16 value, u16 index, u16 length)
+{
+ int status = -EINPROGRESS;
+ u8 status_set_addr = 0;
+
+ if (value > 127 || index != 0 || length != 0) {
+ status = -EINVAL;
+ goto set_address_error;
+ }
+
+ if ((crg_udc->device_state == USB_STATE_DEFAULT && value != 0) ||
+ crg_udc->device_state == USB_STATE_ADDRESS) {
+ u32 param0;
+
+ crg_udc->dev_addr = value;
+
+ param0 = CRG_CMD2_0_DEV_ADDR(value);
+ crg_issue_command(crg_udc, CRG_CMD_SET_ADDR, param0, 0);
+ status_set_addr = 1;
+ } else {
+ status = -EINVAL;
+ }
+
+set_address_error:
+ dev_dbg(crg_udc->dev, "build_ep0_status for Address Device\n");
+
+ crg_udc->setup_status = STATUS_STAGE_XFER;
+ crg_udc->setup_fn_call_back = &set_address_cmpl;
+ build_ep0_status(&crg_udc->udc_ep[0],
+ true, status, NULL, status_set_addr, 0);
+}
+
+static void set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+ struct crg_udc_ep *udc_ep;
+ struct crg_gadget_dev *crg_udc;
+ struct sel_value_s *sel_value;
+
+ udc_ep = container_of(ep, struct crg_udc_ep, usb_ep);
+ crg_udc = udc_ep->crg_udc;
+
+ if (req->buf) {
+ sel_value = req->buf;
+ crg_udc->sel_value.u2_pel_value = sel_value->u2_pel_value;
+ crg_udc->sel_value.u2_sel_value = sel_value->u2_sel_value;
+ crg_udc->sel_value.u1_pel_value = sel_value->u1_pel_value;
+ crg_udc->sel_value.u1_sel_value = sel_value->u1_sel_value;
+
+ kfree(req->buf);
+ }
+
+ dev_dbg(crg_udc->dev, "u1_sel_value = 0x%x, u2_sel_value = 0x%x\n",
+ crg_udc->sel_value.u1_sel_value,
+ crg_udc->sel_value.u2_sel_value);
+}
+
+static void setselrequest(struct crg_gadget_dev *crg_udc,
+ u16 value, u16 index, u16 length, u64 data)
+{
+ int status = -EINPROGRESS;
+ struct crg_udc_request *udc_req_ptr = crg_udc->status_req;
+ struct crg_udc_ep *udc_ep_ptr = &crg_udc->udc_ep[0];
+
+ if (!udc_req_ptr)
+ return;
+
+ if (crg_udc->device_state == USB_STATE_DEFAULT)
+ status = -EINVAL;
+
+ if (index != 0 || value != 0 || length != 6)
+ status = -EINVAL;
+
+ if (status != -EINPROGRESS) {
+ } else {
+ udc_req_ptr->usb_req.length = length;
+ udc_req_ptr->usb_req.buf =
+ kzalloc(sizeof(*udc_req_ptr->usb_req.buf), GFP_ATOMIC);
+ }
+
+ udc_req_ptr->usb_req.status = -EINPROGRESS;
+ udc_req_ptr->usb_req.actual = 0;
+ udc_req_ptr->usb_req.complete = set_sel_cmpl;
+
+ if (udc_req_ptr->usb_req.dma == DMA_ADDR_INVALID) {
+ udc_req_ptr->usb_req.dma =
+ dma_map_single(crg_udc->dev, udc_req_ptr->usb_req.buf,
+ udc_req_ptr->usb_req.length,
+ DMA_TO_DEVICE);
+ udc_req_ptr->mapped = 1;
+ }
+
+ status = crg_udc_build_td(udc_ep_ptr, udc_req_ptr);
+
+ if (!status)
+ list_add_tail(&udc_req_ptr->queue, &udc_ep_ptr->queue);
+}
+
+static void set_test_mode_cmpl(struct crg_gadget_dev *crg_udc)
+{
+ if (crg_udc->set_tm != 0) {
+ u32 tmp;
+ struct crg_uccr *uccr = crg_udc->uccr;
+
+ tmp = readl(&uccr->u2portpmsc);
+ tmp = SETF_VAR(CRG_U3DC_U2PORTPM_TM, tmp, crg_udc->set_tm);
+ writel(tmp, &uccr->u2portpmsc);
+
+ crg_udc->set_tm = 0;
+ }
+}
+
+static bool setfeaturesrequest(struct crg_gadget_dev *crg_udc,
+ u8 RequestType, u8 bRequest, u16 value, u16 index, u16 length)
+{
+ int status = -EINPROGRESS;
+ u8 DCI;
+ struct crg_udc_ep *udc_ep_ptr;
+ u32 tmp;
+ bool set_feat = 0;
+ struct crg_uccr *uccr = crg_udc->uccr;
+
+ if (length != 0) {
+ status = -EINVAL;
+ goto set_feature_error;
+ }
+
+ if (crg_udc->device_state == USB_STATE_DEFAULT) {
+ status = -EINVAL;
+ goto set_feature_error;
+ }
+
+ set_feat = (bRequest == USB_REQ_SET_FEATURE) ? 1 : 0;
+ if ((RequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) ==
+ (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) {
+ dev_dbg(crg_udc->dev, "Halt/Unhalt EP\n");
+ if (crg_udc->device_state == USB_STATE_ADDRESS) {
+ if (index != 0) {
+ status = -EINVAL;
+ goto set_feature_error;
+ }
+ }
+
+ DCI = index2DCI(index);
+
+ if (DCI == 1) {
+ dev_dbg(crg_udc->dev, "setfeat INVALID DCI = 0x%x !!\n", DCI);
+ goto set_feature_error;
+ }
+
+ udc_ep_ptr = &crg_udc->udc_ep[DCI];
+ dev_dbg(crg_udc->dev, "halt/Unhalt endpoint DCI = 0x%x\n", DCI);
+
+ status = ep_halt(udc_ep_ptr,
+ (bRequest == USB_REQ_SET_FEATURE) ? 1 : 0,
+ 0);
+
+ if (status < 0)
+ goto set_feature_error;
+ } else if ((RequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) ==
+ (USB_RECIP_DEVICE | USB_TYPE_STANDARD)) {
+ switch (value) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ dev_dbg(crg_udc->dev, "USB_DEVICE_REMOTE_WAKEUP called\n");
+ /* REMOTE_WAKEUP selector is not used by USB3.0 */
+ if (crg_udc->device_state < USB_STATE_DEFAULT ||
+ crg_udc->gadget.speed >= USB_SPEED_SUPER) {
+ status = -EINVAL;
+ goto set_feature_error;
+ }
+ dev_dbg(crg_udc->dev, "%s_Feature RemoteWake\n",
+ set_feat ? "Set" : "Clear");
+
+ /*TODO corigine hardware ops needed*/
+ crg_udc->u2_RWE = set_feat;
+
+ break;
+ case USB_DEVICE_U1_ENABLE:
+ case USB_DEVICE_U2_ENABLE:
+ {
+ u32 timeout_val;
+
+ dev_dbg(crg_udc->dev, "USB_DEVICE_U12_ENABLE called\n");
+ if (crg_udc->device_state != USB_STATE_CONFIGURED) {
+ dev_err(crg_udc->dev, "%s u12 enable fail, usb state=%d\n",
+ __func__, crg_udc->device_state);
+ status = -EINVAL;
+ goto set_feature_error;
+ }
+
+ if (index & 0xff) {
+ status = -EINVAL;
+ goto set_feature_error;
+ }
+
+ if (set_feat == 1 && U12_FORBIDDEN > 0) {
+ status = -EINVAL;
+ goto set_feature_error;
+ }
+
+ tmp = readl(&uccr->u3portpmsc);
+ /*TODO corigine hardware ops needed*/
+ if (value == USB_DEVICE_U1_ENABLE) {
+#if (U12_INITIATE_FORBIDDEN == 0)
+ tmp &= (~CRG_U3DC_U3PORTPM_U1IEN);
+ tmp |= (set_feat <<
+ CRG_U3DC_U3PORTPM_U1IEN_SHIFT);
+#endif
+ if (U12_FORBIDDEN == 0)
+ timeout_val = set_feat ?
+ U1_TIMEOUT_VAL : 0;
+ else
+ timeout_val = 0;
+
+ tmp = SETF_VAR(CRG_U3DC_U3PORTPM_U1TMOUT,
+ tmp, timeout_val);
+
+ crg_udc->feature_u1_enable = set_feat;
+ }
+
+ if (value == USB_DEVICE_U2_ENABLE) {
+#if (U12_INITIATE_FORBIDDEN == 0)
+ tmp &= (~CRG_U3DC_U3PORTPM_U2IEN);
+ tmp |= (set_feat <<
+ CRG_U3DC_U3PORTPM_U2IEN_SHIFT);
+#endif
+ if (U12_FORBIDDEN == 0)
+ timeout_val = set_feat ? U1_TIMEOUT_VAL : 0;
+ else
+ timeout_val = 0;
+
+ tmp = SETF_VAR(CRG_U3DC_U3PORTPM_U2TMOUT,
+ tmp, timeout_val);
+
+ crg_udc->feature_u2_enable = set_feat;
+ }
+ writel(tmp, &uccr->u3portpmsc);
+
+ break;
+ }
+ case USB_DEVICE_TEST_MODE:
+ {
+ u32 u_pattern;
+
+ dev_dbg(crg_udc->dev, "USB_DEVICE_TEST_MODE called\n");
+ if (crg_udc->gadget.speed > USB_SPEED_HIGH)
+ goto set_feature_error;
+
+ if (crg_udc->device_state < USB_STATE_DEFAULT)
+ goto set_feature_error;
+
+ u_pattern = index >> 8;
+ /* TESTMODE is only defined for high speed device */
+ if (crg_udc->gadget.speed == USB_SPEED_HIGH) {
+ dev_dbg(crg_udc->dev, "high speed test mode enter\n");
+ crg_udc->set_tm = u_pattern;
+ crg_udc->setup_fn_call_back =
+ &set_test_mode_cmpl;
+ }
+ break;
+ }
+
+ default:
+ goto set_feature_error;
+ }
+
+ } else if ((RequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) ==
+ (USB_RECIP_INTERFACE | USB_TYPE_STANDARD)) {
+ if (crg_udc->device_state != USB_STATE_CONFIGURED) {
+ dev_err(crg_udc->dev, "%s interface u12 enable fail, usb state=%d\n",
+ __func__, crg_udc->device_state);
+ status = -EINVAL;
+ goto set_feature_error;
+ }
+
+ /* Suspend Option */
+ if (value == USB_INTRF_FUNC_SUSPEND) {
+ if (index & USB_INTR_FUNC_SUSPEND_OPT_MASK &
+ USB_INTRF_FUNC_SUSPEND_LP) {
+ if (index & USB_INTRF_FUNC_SUSPEND_RW)
+ dev_dbg(crg_udc->dev, "Interface En Remote Wakeup\n");
+ else
+ dev_dbg(crg_udc->dev, "Interface Dis RemoteWakeup\n");
+
+ /* Do not need to return status stage here
+ * Pass to composite gadget driver to process
+ * the request
+ */
+ return false;
+ }
+ }
+ }
+
+ crg_udc->setup_status = STATUS_STAGE_XFER;
+ build_ep0_status(&crg_udc->udc_ep[0], true, status, NULL, 0, 0);
+ return true;
+
+set_feature_error:
+ set_ep0_halt(crg_udc);
+ return true;
+}
+
+static bool setconfigurationrequest(struct crg_gadget_dev *crg_udc, u16 value)
+{
+ if (crg_udc->device_state <= USB_STATE_DEFAULT)
+ goto set_config_error;
+
+ /*return false means need further process by composite gadget driver*/
+ return false;
+
+set_config_error:
+ set_ep0_halt(crg_udc);
+ return true;
+}
+
+static void set_isoch_delay(struct crg_gadget_dev *crg_udc,
+ u16 value, u16 index, u16 length)
+{
+ int status = -EINPROGRESS;
+
+ if (value > 65535 || index != 0 || length != 0)
+ status = -EINVAL;
+
+ /*need further ops for isoch delay*/
+
+ crg_udc->setup_status = STATUS_STAGE_XFER;
+ build_ep0_status(&crg_udc->udc_ep[0], true, status, NULL, 0, 0);
+}
+
+static void crg_handle_setup_pkt(struct crg_gadget_dev *crg_udc,
+ struct usb_ctrlrequest *setup_pkt, u8 setup_tag)
+{
+ u16 wValue = setup_pkt->wValue;
+ u16 wIndex = setup_pkt->wIndex;
+ u16 wLength = setup_pkt->wLength;
+ u64 wData = 0;
+ unsigned long flags = 0;
+
+ dev_dbg(crg_udc->dev, "bRequest=0x%x, wValue=0x%.4x, wIndex=0x%x, wLength=%d\n",
+ setup_pkt->bRequest, wValue, wIndex, wLength);
+
+ /* EP0 come backs to running when new setup packet comes*/
+ crg_udc->udc_ep[0].ep_state = EP_STATE_RUNNING;
+
+ crg_udc->setup_tag = setup_tag;
+ crg_udc->setup_status = SETUP_PKT_PROCESS_IN_PROGRESS;
+ crg_udc->setup_fn_call_back = NULL;
+
+ if ((setup_pkt->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ switch (setup_pkt->bRequest) {
+ case USB_REQ_GET_STATUS:
+ dev_dbg(crg_udc->dev, "USB_REQ_GET_STATUS\n");
+ if ((setup_pkt->bRequestType & (USB_DIR_IN |
+ USB_TYPE_MASK))
+ != (USB_DIR_IN | USB_TYPE_STANDARD)) {
+ crg_udc->setup_status = WAIT_FOR_SETUP;
+ return;
+ }
+
+ getstatusrequest(crg_udc, setup_pkt->bRequestType,
+ wValue, wIndex, wLength);
+ return;
+ case USB_REQ_SET_ADDRESS:
+ dev_dbg(crg_udc->dev, "USB_REQ_SET_ADDRESS\n");
+ if (setup_pkt->bRequestType != (USB_DIR_OUT |
+ USB_RECIP_DEVICE |
+ USB_TYPE_STANDARD)) {
+ crg_udc->setup_status = WAIT_FOR_SETUP;
+ return;
+ }
+
+ setaddressrequest(crg_udc, wValue, wIndex, wLength);
+ return;
+ case USB_REQ_SET_SEL:
+ dev_dbg(crg_udc->dev, "USB_REQ_SET_SEL\n");
+
+ if (setup_pkt->bRequestType != (USB_DIR_OUT |
+ USB_RECIP_DEVICE |
+ USB_TYPE_STANDARD)) {
+ crg_udc->setup_status = WAIT_FOR_SETUP;
+ return;
+ }
+
+ crg_udc->setup_status = DATA_STAGE_RECV;
+ setselrequest(crg_udc, wValue, wIndex, wLength, wData);
+ return;
+ case USB_REQ_SET_ISOCH_DELAY:
+ if (setup_pkt->bRequestType != (USB_DIR_OUT |
+ USB_RECIP_DEVICE |
+ USB_TYPE_STANDARD))
+ break;
+
+ dev_dbg(crg_udc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
+ set_isoch_delay(crg_udc, wValue, wIndex, wLength);
+ return;
+
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ dev_dbg(crg_udc->dev, "USB_REQ_CLEAR/SET_FEATURE\n");
+
+ /* Need composite gadget driver
+ * to process the function remote wakeup request
+ */
+ if (setfeaturesrequest(crg_udc, setup_pkt->bRequestType,
+ setup_pkt->bRequest,
+ wValue, wIndex, wLength)) {
+ /* Get here if request has been processed.*/
+ return;
+ }
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ /*In theory we need to clear RUN bit before
+ *status stage of deconfig request sent.
+ *But seeing problem if we do it before all the
+ *endpoints belonging to the configuration
+ *disabled.
+ */
+ dev_dbg(crg_udc->dev, "USB_REQ_SET_CONFIGURATION\n");
+ dev_dbg(crg_udc->dev, "CONFIGURATION wValue=%d\n", wValue);
+
+ if (setconfigurationrequest(crg_udc, wValue)) {
+ /* Get here if request has been processed.
+ * Or error happens
+ */
+ return;
+ }
+
+ if (crg_udc->device_state == USB_STATE_ADDRESS)
+ crg_udc->device_state = USB_STATE_CONFIGURED;
+ dev_dbg(crg_udc->dev, "USB_REQ_SET_CONFIGURATION: device_state is %d\n",
+ crg_udc->device_state);
+ break;
+ default:
+ dev_dbg(crg_udc->dev, "USB_REQ default bRequest=%d, bRequestType=%d\n",
+ setup_pkt->bRequest, setup_pkt->bRequestType);
+ }
+ }
+
+ if (wLength) {
+ /* data phase from gadget like GET_CONFIGURATION
+ * call the setup routine of gadget driver.
+ * remember the request direction.
+ */
+ crg_udc->setup_status =
+ (setup_pkt->bRequestType & USB_DIR_IN) ?
+ DATA_STAGE_XFER : DATA_STAGE_RECV;
+ }
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ if (crg_udc->gadget_driver->setup(&crg_udc->gadget, setup_pkt) < 0) {
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+ set_ep0_halt(crg_udc);
+ return;
+ }
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+}
+
+static int crg_handle_xfer_event(struct crg_gadget_dev *crg_udc,
+ struct event_trb_s *event)
+{
+ u8 DCI = GETF(EVE_TRB_ENDPOINT_ID, event->dw3);
+ struct crg_udc_ep *udc_ep_ptr = &crg_udc->udc_ep[DCI];
+ /*Corigine ep contexts start from ep1*/
+ u16 comp_code;
+ struct crg_udc_request *udc_req_ptr;
+ bool trbs_dequeued = false;
+
+ if (!udc_ep_ptr->first_trb ||
+ get_ep_state(crg_udc, DCI) == EP_STATE_DISABLED)
+ return -ENODEV;
+
+ comp_code = GETF(EVE_TRB_COMPL_CODE, event->dw2);
+ if (comp_code == CMPL_CODE_STOPPED ||
+ comp_code == CMPL_CODE_STOPPED_LENGTH_INVALID ||
+ comp_code == CMPL_CODE_DISABLED ||
+ comp_code == CMPL_CODE_DISABLED_LENGTH_INVALID ||
+ comp_code == CMPL_CODE_HALTED ||
+ comp_code == CMPL_CODE_HALTED_LENGTH_INVALID) {
+ dev_dbg(crg_udc->dev, "comp_code = %d(STOPPED/HALTED/DISABLED)\n", comp_code);
+ } else {
+ update_dequeue_pt(event, udc_ep_ptr);
+ }
+
+ dev_dbg(crg_udc->dev, "%s ep%d dqpt=0x%p, eqpt=0x%p\n", __func__,
+ DCI, udc_ep_ptr->deq_pt, udc_ep_ptr->enq_pt);
+ dev_dbg(crg_udc->dev, "comp_code = %d\n", comp_code);
+
+ if (is_request_dequeued(crg_udc, udc_ep_ptr, event)) {
+ trbs_dequeued = true;
+ dev_dbg(crg_udc->dev, "WARNING: Drop the transfer event\n");
+ goto queue_more_trbs;
+ }
+
+ comp_code = GETF(EVE_TRB_COMPL_CODE, event->dw2);
+
+ switch (comp_code) {
+ case CMPL_CODE_SUCCESS:
+ {
+ dev_dbg(crg_udc->dev, "%s Complete SUCCESS\n", __func__);
+ handle_cmpl_code_success(crg_udc, event, udc_ep_ptr);
+
+ trbs_dequeued = true;
+ break;
+ }
+ case CMPL_CODE_SHORT_PKT:
+ {
+ u32 trb_transfer_length;
+
+ dev_dbg(crg_udc->dev, "handle_exfer_event CMPL_CODE_SHORT_PKT\n");
+ if (usb_endpoint_dir_out(udc_ep_ptr->desc)) {
+
+ trb_transfer_length = GETF(EVE_TRB_TRAN_LEN,
+ event->dw2);
+ udc_req_ptr = list_entry(udc_ep_ptr->queue.next,
+ struct crg_udc_request, queue);
+
+ udc_req_ptr->usb_req.actual =
+ udc_req_ptr->usb_req.length -
+ trb_transfer_length;
+ /* Ensure that req_ptr is updated */
+ wmb();
+ if (udc_req_ptr->usb_req.actual != 512 &&
+ udc_req_ptr->usb_req.actual != 31) {
+ u64 trb_pt;
+ struct transfer_trb_s *p_trb;
+
+ dev_dbg(crg_udc->dev, "Actual Data transferred = %d\n",
+ udc_req_ptr->usb_req.actual);
+
+ trb_pt = (u64)event->dw0 +
+ ((u64)(event->dw1) << 32);
+
+ p_trb = tran_trb_dma_to_virt(udc_ep_ptr, trb_pt);
+
+ dev_dbg(crg_udc->dev, "event dw0 = 0x%x\n", event->dw0);
+ dev_dbg(crg_udc->dev, "event dw1 = 0x%x\n", event->dw1);
+ dev_dbg(crg_udc->dev, "event dw2 = 0x%x\n", event->dw2);
+ dev_dbg(crg_udc->dev, "event dw3 = 0x%x\n", event->dw3);
+
+ dev_dbg(crg_udc->dev, "trb_pt = 0x%lx, p_trb = 0x%p\n",
+ (unsigned long)trb_pt, p_trb);
+
+ dev_dbg(crg_udc->dev, "trb dw0 = 0x%x\n", p_trb->dw0);
+ dev_dbg(crg_udc->dev, "trb dw1 = 0x%x\n", p_trb->dw1);
+ dev_dbg(crg_udc->dev, "trb dw2 = 0x%x\n", p_trb->dw2);
+ dev_dbg(crg_udc->dev, "trb dw3 = 0x%x\n", p_trb->dw3);
+ }
+ req_done(udc_ep_ptr, udc_req_ptr, 0);
+ } else {
+ dev_dbg(crg_udc->dev, "ep dir in\n");
+ }
+
+ trbs_dequeued = true;
+
+ /* Advance the dequeue pointer to next TD */
+ advance_dequeue_pt(udc_ep_ptr);
+
+ break;
+ }
+
+ case CMPL_CODE_PROTOCOL_STALL:
+ {
+ dev_dbg(crg_udc->dev, "%s CMPL_CODE_PROTOCOL_STALL\n", __func__);
+
+ udc_req_ptr = list_entry(udc_ep_ptr->queue.next,
+ struct crg_udc_request, queue);
+ req_done(udc_ep_ptr, udc_req_ptr, -EINVAL);
+ trbs_dequeued = true;
+ crg_udc->setup_status = WAIT_FOR_SETUP;
+ advance_dequeue_pt(udc_ep_ptr);
+ break;
+ }
+
+ case CMPL_CODE_SETUP_TAG_MISMATCH:
+ {
+ u32 enq_idx = crg_udc->ctrl_req_enq_idx;
+ struct usb_ctrlrequest *setup_pkt;
+ struct crg_setup_packet *crg_setup_pkt;
+ u16 setup_tag;
+
+ dev_err(crg_udc->dev, "%s SETUP TAG MISMATCH\n", __func__);
+ dev_dbg(crg_udc->dev, "NOW setup tag = 0x%x\n", crg_udc->setup_tag);
+
+ /* skip seqnum err event until last one arrives. */
+ if (udc_ep_ptr->deq_pt == udc_ep_ptr->enq_pt) {
+ udc_req_ptr = list_entry(udc_ep_ptr->queue.next,
+ struct crg_udc_request,
+ queue);
+
+ if (udc_req_ptr)
+ req_done(udc_ep_ptr, udc_req_ptr, -EINVAL);
+
+ /* drop all the queued setup packet, only
+ * process the latest one.
+ */
+ crg_udc->setup_status = WAIT_FOR_SETUP;
+ if (enq_idx) {
+ crg_setup_pkt =
+ &crg_udc->ctrl_req_queue[enq_idx - 1];
+ setup_pkt = &crg_setup_pkt->usbctrlreq;
+ setup_tag = crg_setup_pkt->setup_tag;
+ crg_handle_setup_pkt(crg_udc, setup_pkt,
+ setup_tag);
+ /* flash the queue after the latest
+ * setup pkt got handled..
+ */
+ memset(crg_udc->ctrl_req_queue, 0,
+ sizeof(struct crg_setup_packet)
+ * CTRL_REQ_QUEUE_DEPTH);
+ crg_udc->ctrl_req_enq_idx = 0;
+ }
+ } else {
+ dev_dbg(crg_udc->dev, "setuptag mismatch skp dpt!=ept: 0x%p, 0x%p\n",
+ udc_ep_ptr->deq_pt, udc_ep_ptr->enq_pt);
+ }
+
+ crg_udc->setup_tag_mismatch_found = 1;
+ dev_dbg(crg_udc->dev, "%s SETUP TAG MISMATCH END\n", __func__);
+ break;
+ }
+
+ case CMPL_CODE_BABBLE_DETECTED_ERR:
+ case CMPL_CODE_INVALID_STREAM_TYPE_ERR:
+ case CMPL_CODE_RING_UNDERRUN:
+ case CMPL_CODE_RING_OVERRUN:
+ case CMPL_CODE_ISOCH_BUFFER_OVERRUN:
+ case CMPL_CODE_USB_TRANS_ERR:
+ case CMPL_CODE_TRB_ERR:
+ {
+ dev_err(crg_udc->dev, "XFER event err, comp_code = 0x%x\n", comp_code);
+ set_ep_halt(crg_udc, DCI);
+ break;
+ }
+
+ case CMPL_CODE_STOPPED:
+ case CMPL_CODE_STOPPED_LENGTH_INVALID:
+ /* Any ep stop ops should deal with stopped trbs itselves
+ * Event handler didn't know whether the stopped trb should
+ * be discarded or continued. So we do nothing here
+ */
+ dev_err(crg_udc->dev, "STOP, comp_code = 0x%x\n", comp_code);
+ break;
+ default:
+ dev_dbg(crg_udc->dev, "CRG UNKNOWN comp_code = 0x%x\n", comp_code);
+ dev_dbg(crg_udc->dev, "EPDCI = 0x%x\n", udc_ep_ptr->DCI);
+ break;
+ }
+
+ dev_dbg(crg_udc->dev, "%s 2 ep%d dqpt=0x%p, eqpt=0x%p\n", __func__,
+ DCI, udc_ep_ptr->deq_pt, udc_ep_ptr->enq_pt);
+
+queue_more_trbs:
+ /* If there are some trbs dequeued by HW and the ring
+ * was full before, then schedule any pending TRB's
+ */
+ if (trbs_dequeued && udc_ep_ptr->tran_ring_full) {
+ udc_ep_ptr->tran_ring_full = false;
+ queue_pending_trbs(udc_ep_ptr);
+ }
+ return 0;
+}
+
+int g_dnl_board_usb_cable_connected(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_uccr *uccr;
+ u32 tmp, tmp_cfg0;
+ static bool pp = UDC_FALSE;
+ static int cnt = INIT_ZERO;
+
+ uccr = crg_udc->uccr;
+ crg_udc_start(crg_udc);
+
+ if (cnt < STATE_USB_LINK_STABLE) {
+ tmp = readl(crg_udc->mmio_virt_base + CRG_UDC_VENDOR_REG);
+ writel(tmp | 1 << 26,
+ crg_udc->mmio_virt_base + CRG_UDC_VENDOR_REG);
+
+ tmp = readl(&uccr->control);
+ tmp |= CRG_U3DC_CTRL_RUN;
+ dev_dbg(crg_udc->dev, "tmp1 = 0x%x\n", tmp);
+ writel(tmp, &uccr->control);
+ tmp = readl(&uccr->control);
+ dev_dbg(crg_udc->dev, "control = 0x%x\n", tmp);
+ mdelay(10);
+ pp = (readl(crg_udc->mmio_virt_base +
+ CRG_UDC_VENDOR_REG) >> 26) & 1;
+ tmp = readl(&uccr->control);
+ tmp &= ~CRG_U3DC_CTRL_RUN;
+ writel(tmp, &uccr->control);
+ tmp = readl(&uccr->control);
+ dev_dbg(crg_udc->dev, "control2 = 0x%x\n", tmp);
+ cnt++;
+ } else {
+ pp = (readl(crg_udc->mmio_virt_base +
+ CRG_UDC_VENDOR_REG) >> 26) & 1;
+ dev_dbg(crg_udc->dev, "pp = %d\n", pp);
+ if (pp) {
+ if (crg_udc->device_state < USB_STATE_POWERED) {
+ tmp_cfg0 = readl(&uccr->config0);
+ tmp_cfg0 &= (~0xf0);
+ tmp_cfg0 |= 0xf0;
+ writel(tmp_cfg0, &uccr->config0);
+
+ mdelay(3);
+ crg_udc_start(crg_udc);
+ crg_udc->device_state = USB_STATE_POWERED;
+ return 1;
+ } else
+ return 1;
+
+ } else {
+ cnt = 0;
+ return 0;
+ }
+ }
+ tmp = readl(&uccr->portsc);
+ if (tmp & CRG_U3DC_PORTSC_PP) {
+ mdelay(100);
+ tmp = readl(&uccr->portsc);
+ if (tmp & CRG_U3DC_PORTSC_PP) {
+ if (crg_udc->device_state < USB_STATE_POWERED) {
+ u32 tmp_cfg0;
+
+ dev_dbg(crg_udc->dev, "%s powered, portsc[0x%p]=0x%x\n", __func__,
+ &uccr->portsc, tmp);
+
+ /*set usb 3 disable count to 15*/
+ tmp_cfg0 = readl(&uccr->config0);
+ tmp_cfg0 &= (~0xf0);
+ tmp_cfg0 |= 0xf0;
+ writel(tmp_cfg0, &uccr->config0);
+
+ mdelay(3);
+ crg_udc_start(crg_udc);
+
+ dev_dbg(crg_udc->dev, "%s device state powered\n", __func__);
+ crg_udc->device_state = USB_STATE_POWERED;
+ }
+ }
+ return 1;
+ }
+
+ dev_dbg(crg_udc->dev, "%s no power, portsc[0x%p]=0x%x\n", __func__,
+ &uccr->portsc, tmp);
+
+ return 0;
+}
+
+static int crg_handle_port_status(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_uccr *uccr = crg_udc->uccr;
+ u32 portsc_val;
+ u32 tmp;
+ u32 speed;
+ unsigned long flags = 0;
+
+ /* handle Port Reset */
+ portsc_val = readl(&uccr->portsc);
+ writel(portsc_val, &uccr->portsc);
+
+ tmp = readl(&uccr->portsc);
+ dev_dbg(crg_udc->dev, "%s RAW,portsc[0x%p]=0x%x\n", __func__,
+ &uccr->portsc, portsc_val);
+
+ if (portsc_val & CRG_U3DC_PORTSC_PRC) {
+ mdelay(3);
+
+ tmp = readl(&uccr->portsc);
+ if (tmp & CRG_U3DC_PORTSC_PRC) {
+ dev_dbg(crg_udc->dev, "PRC is still set\n");
+ } else if (tmp & CRG_U3DC_PORTSC_PR) {
+ /* first port status change event for port reset*/
+ dev_dbg(crg_udc->dev, "PRC is not set, but PR is set!!!!!!!!\n");
+ } else {
+ if (CRG_U3DC_PORTSC_PLS_GET(tmp) != 0 ||
+ (!(tmp & CRG_U3DC_PORTSC_PED))) {
+ dev_dbg(crg_udc->dev,
+ "portsc[0x%p]=0x%x no PED\n",
+ &uccr->portsc, tmp);
+ return 0;
+ }
+
+ switch (CRG_U3DC_PORTSC_SPEED_GET(tmp)) {
+ case CRG_U3DC_PORTSC_SPEED_SSP:
+ speed = USB_SPEED_SUPER_PLUS;
+ break;
+ case CRG_U3DC_PORTSC_SPEED_SS:
+ speed = USB_SPEED_SUPER;
+ break;
+ case CRG_U3DC_PORTSC_SPEED_HS:
+ speed = USB_SPEED_HIGH;
+ break;
+
+ case CRG_U3DC_PORTSC_SPEED_FS:
+ speed = USB_SPEED_FULL;
+ break;
+
+ case CRG_U3DC_PORTSC_SPEED_LS:
+ speed = USB_SPEED_LOW;
+ break;
+ default:
+ speed = USB_SPEED_UNKNOWN;
+ return 0;
+ }
+
+ if (crg_udc->device_state >= USB_STATE_DEFAULT)
+ crg_udc_reinit(crg_udc);
+
+ crg_udc->gadget.speed = speed;
+ dev_dbg(crg_udc->dev, "gadget speed = 0x%x\n", crg_udc->gadget.speed);
+
+ update_ep0_maxpacketsize(crg_udc);
+
+ crg_udc->connected = 1;
+
+ if (crg_udc->device_state < USB_STATE_RECONNECTING)
+ enable_setup(crg_udc);
+
+ dev_dbg(crg_udc->dev, "PORTSC = 0x%x\n", readl(&uccr->portsc));
+ }
+ }
+ /* handle Port Reset end */
+
+ /* handle Port Connection Change*/
+ tmp = readl(&uccr->portsc);
+ if ((tmp & (CRG_U3DC_PORTSC_CCS | CRG_U3DC_PORTSC_PP)) ==
+ (CRG_U3DC_PORTSC_CCS | CRG_U3DC_PORTSC_PP)) {
+ dev_dbg(crg_udc->dev, "connect int checked\n");
+ dev_dbg(crg_udc->dev, "portsc[0x%p]=0x%x\n", &uccr->portsc, tmp);
+
+ if (CRG_U3DC_PORTSC_PLS_GET(tmp) != 0 ||
+ (!(tmp & CRG_U3DC_PORTSC_PED)))
+ return 0;
+
+ switch (CRG_U3DC_PORTSC_SPEED_GET(tmp)) {
+ case CRG_U3DC_PORTSC_SPEED_SSP:
+ speed = USB_SPEED_SUPER_PLUS;
+ break;
+ case CRG_U3DC_PORTSC_SPEED_SS:
+ speed = USB_SPEED_SUPER;
+ break;
+ case CRG_U3DC_PORTSC_SPEED_HS:
+ speed = USB_SPEED_HIGH;
+ break;
+
+ case CRG_U3DC_PORTSC_SPEED_FS:
+ speed = USB_SPEED_FULL;
+ break;
+
+ case CRG_U3DC_PORTSC_SPEED_LS:
+ default:
+ speed = USB_SPEED_UNKNOWN;
+ return 0;
+ }
+ crg_udc->gadget.speed = speed;
+
+ update_ep0_maxpacketsize(crg_udc);
+
+ crg_udc->connected = 1;
+
+ if (crg_udc->device_state < USB_STATE_RECONNECTING)
+ enable_setup(crg_udc);
+
+ dev_dbg(crg_udc->dev, "connect speed = %d\n", speed);
+
+ } else if (!(tmp & CRG_U3DC_PORTSC_CCS)) {
+ int cable_connected;
+
+ int ccs_drop_ignore = 0;
+
+ if ((CRG_U3DC_PORTSC_PLS_GET(tmp) == 0x0) &&
+ (CRG_U3DC_PORTSC_SPEED_GET(tmp) <
+ CRG_U3DC_PORTSC_SPEED_SS)) {
+ ccs_drop_ignore = 1;
+ dev_err(crg_udc->dev, "ccs glitch detect on HS/FS!\n");
+ }
+
+ if (!ccs_drop_ignore)
+ crg_udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+ mdelay(150);
+ cable_connected =
+ g_dnl_board_usb_cable_connected(crg_udc);
+ if (cable_connected && !ccs_drop_ignore) {
+ crg_udc->device_state = USB_STATE_POWERED;
+ dev_dbg(crg_udc->dev, "do warm reset\n");
+ crg_udc_reinit(crg_udc);
+ crg_udc->connected = 0;
+ } else if (!cable_connected) {
+ dev_dbg(crg_udc->dev, "cable disconnected, rst controller\n");
+
+ crg_udc_reset(crg_udc);
+ if (crg_udc->gadget_driver->disconnect) {
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ crg_udc->gadget_driver->disconnect(&crg_udc->gadget);
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+ }
+
+ reset_data_struct(crg_udc);
+ crg_udc->connected = 0;
+ init_ep0(crg_udc);
+
+ crg_udc->device_state = USB_STATE_ATTACHED;
+
+ wake_up_interruptible(&vbus_wait);
+ return -ECONNRESET;
+ }
+ }
+ /* handle Port Connection Change end*/
+
+ if (portsc_val & CRG_U3DC_PORTSC_PLC) {
+ tmp = readl(&uccr->portsc);
+ if (CRG_U3DC_PORTSC_PLS_GET(tmp) == 0xf) {
+ tmp = SETF_VAR(CRG_U3DC_PORTSC_PLS, tmp, 0x0);
+ writel(tmp, &uccr->portsc);
+ dev_dbg(crg_udc->dev, "pls to 0, write portsc 0x%x\n", tmp);
+ } else if (CRG_U3DC_PORTSC_PLS_GET(tmp) == 0x3) {
+ if (crg_udc->gadget_driver->disconnect) {
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+ crg_udc->gadget_driver->disconnect(&crg_udc->gadget);
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int crg_udc_handle_event(struct crg_gadget_dev *crg_udc,
+ struct event_trb_s *event)
+{
+ int ret;
+
+ switch (GETF(EVE_TRB_TYPE, event->dw3)) {
+ case TRB_TYPE_EVT_PORT_STATUS_CHANGE:
+ if (crg_udc->device_state == USB_STATE_RECONNECTING) {
+ crg_udc->portsc_on_reconnecting = 1;
+ break;
+ }
+ /* 1.Port Reset 2.Port Connection Change 3.Port Link Change */
+ ret = crg_handle_port_status(crg_udc);
+ if (ret)
+ return ret;
+
+ break;
+ case TRB_TYPE_EVT_TRANSFER:
+ if (crg_udc->device_state < USB_STATE_RECONNECTING) {
+ dev_err(crg_udc->dev,
+ "Xfer compl event rcved when dev state=%d !\n",
+ crg_udc->device_state);
+ break;
+ }
+
+ crg_handle_xfer_event(crg_udc, event);
+ break;
+ case TRB_TYPE_EVT_SETUP_PKT:
+ {
+ struct usb_ctrlrequest *setup_pkt;
+ u8 setup_tag;
+
+ dev_dbg(crg_udc->dev, "handle_setup_pkt(%d)\n",
+ crg_udc->device_state);
+
+ setup_pkt = (struct usb_ctrlrequest *)&event->dw0;
+
+ setup_tag = GETF(EVE_TRB_SETUP_TAG, event->dw3);
+ dev_dbg(crg_udc->dev, "setup_pkt = 0x%p, setup_tag = 0x%x\n",
+ setup_pkt, setup_tag);
+ if (crg_udc->setup_status != WAIT_FOR_SETUP) {
+ /*previous setup packet hasn't
+ * completed yet. Just ignore the prev setup
+ */
+ dev_err(crg_udc->dev, "consecutive setup\n");
+ queue_setup_pkt(crg_udc, setup_pkt, setup_tag);
+ break;
+ }
+
+ crg_handle_setup_pkt(crg_udc, setup_pkt, setup_tag);
+
+ break;
+ }
+ default:
+ dev_dbg(crg_udc->dev, "unexpect TRB_TYPE = 0x%x",
+ GETF(EVE_TRB_TYPE, event->dw3));
+ break;
+ }
+
+ return 0;
+}
+
+static int process_event_ring(struct crg_gadget_dev *crg_udc, int index)
+{
+ struct event_trb_s *event;
+ struct crg_udc_event *udc_event;
+ struct crg_uicr *uicr = crg_udc->uicr[index];
+ dma_addr_t tmp;
+ u32 val;
+ dma_addr_t erdp;
+ int ret;
+
+ if (!uicr)
+ return IRQ_NONE;
+
+ tmp = readl(&uicr->iman);
+ if ((tmp & (CRG_U3DC_IMAN_INT_EN | CRG_U3DC_IMAN_INT_PEND)) !=
+ (CRG_U3DC_IMAN_INT_EN | CRG_U3DC_IMAN_INT_PEND)) {
+ }
+
+ val = readl(&uicr->iman);
+ val |= CRG_U3DC_IMAN_INT_PEND;
+ writel(val, &uicr->iman);
+
+ udc_event = &crg_udc->udc_event[index];
+ while (udc_event->evt_dq_pt) {
+ /* rmb */
+ rmb();
+ event = (struct event_trb_s *)udc_event->evt_dq_pt;
+
+ if (GETF(EVE_TRB_CYCLE_BIT, event->dw3) !=
+ udc_event->CCS)
+ break;
+
+ ret = crg_udc_handle_event(crg_udc, event);
+ if (ret == -ECONNRESET)
+ return ret;
+
+ if (event == udc_event->evt_seg0_last_trb) {
+ udc_event->CCS = udc_event->CCS ? 0 : 1;
+ udc_event->evt_dq_pt = udc_event->event_ring.vaddr;
+ } else {
+ udc_event->evt_dq_pt++;
+ }
+ }
+
+ /* update dequeue pointer */
+ erdp = event_trb_virt_to_dma(udc_event, udc_event->evt_dq_pt);
+ tmp = upper_32_bits(erdp);
+ writel(upper_32_bits(erdp), &uicr->erdphi);
+ tmp = lower_32_bits(erdp);
+ tmp |= CRG_U3DC_ERDPLO_EHB;
+ writel(lower_32_bits(erdp | CRG_U3DC_ERDPLO_EHB), &uicr->erdplo);
+
+ return 0;
+}
+
+static int crg_gadget_handle_interrupt(struct crg_gadget_dev *crg_udc)
+{
+ struct crg_uccr *uccr = crg_udc->uccr;
+ u32 tmp_status;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+
+ /******************************/
+
+ tmp_status = readl(&uccr->status);
+
+ if (tmp_status & CRG_U3DC_STATUS_SYS_ERR) {
+ dev_err(crg_udc->dev, "%s System error happens!!!\n", __func__);
+ /*Handle system error*/
+ writel(CRG_U3DC_STATUS_SYS_ERR, &uccr->status);
+ }
+
+ if (tmp_status & CRG_U3DC_STATUS_EINT) {
+ int i;
+
+ writel(CRG_U3DC_STATUS_EINT, &uccr->status);
+
+ /*process event rings*/
+ for (i = 0; i < CRG_RING_NUM; i++)
+ process_event_ring(crg_udc, i);
+ }
+
+ if (crg_udc->device_state == USB_STATE_RECONNECTING &&
+ crg_udc->portsc_on_reconnecting == 1 &&
+ is_event_rings_empty(crg_udc)) {
+ crg_udc->portsc_on_reconnecting = 0;
+ crg_handle_port_status(crg_udc);
+ }
+
+ if (crg_udc->device_state == USB_STATE_RECONNECTING &&
+ crg_udc->connected == 1) {
+ dev_dbg(crg_udc->dev, "check if ready for setup\n");
+ prepare_for_setup(crg_udc);
+ }
+
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+
+ return 0;
+}
+
+static irqreturn_t crg_udc_common_irq(int irq, void *dev)
+{
+ int retval = 0;
+
+ retval = crg_gadget_handle_interrupt(dev);
+
+ return IRQ_HANDLED;
+}
+
+static int crg_gadget_irq_init(struct platform_device *pdev, struct crg_gadget_dev *crg_udc)
+{
+ int irq = 0;
+ int retval = 0;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENODEV;
+
+ crg_udc->irq = irq;
+
+ retval = request_irq(irq, crg_udc_common_irq,
+ IRQF_SHARED, "crg_udc",
+ crg_udc);
+ if (retval) {
+ dev_err(crg_udc->dev, "request of irq%d failed\n", irq);
+ retval = -EBUSY;
+ }
+
+ return 0;
+}
+
+static int crg_vbus_detect_thread(void *data)
+{
+ struct crg_gadget_dev *crg_udc = (struct crg_gadget_dev *)data;
+ unsigned long flags = 0;
+ int cable_connected = 0;
+
+ while (1) {
+ if (kthread_should_stop())
+ break;
+
+ dev_dbg(crg_udc->dev, "crg_udc->device_state is %d\n",
+ crg_udc->device_state);
+
+ spin_lock_irqsave(&crg_udc->udc_lock, flags);
+
+ cable_connected = g_dnl_board_usb_cable_connected(crg_udc);
+
+ spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
+
+ if (cable_connected) {
+ wait_event_interruptible
+ (vbus_wait, crg_udc->device_state < USB_STATE_POWERED);
+ } else {
+ dev_dbg(crg_udc->dev, "wait for vbus\n");
+ msleep(1000);
+ }
+ }
+
+ return 0;
+}
+
+
+static const struct crg_udc_priv ax3000_plat_setup_gen2 = {
+ .plat_setup_gen3 = false,
+};
+
+static const struct crg_udc_priv ax3000_plat_setup_gen3 = {
+ .plat_setup_gen3 = true,
+};
+
+/**
+ * crg_gadget_probe - Initializes gadget driver
+ *
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+
+static const struct of_device_id of_crg_udc_match[] = {
+ {
+ .compatible = "axiado,ax3000-udc",
+ .data = &ax3000_plat_setup_gen2
+ },
+ {
+ .compatible = "axiado,ax3000-udc-gen3",
+ .data = &ax3000_plat_setup_gen3
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_crg_udc_match);
+
+static int crg_udc_probe(struct platform_device *pdev)
+{
+ int ret;
+ int i;
+ struct crg_gadget_dev *crg_udc;
+ static int udc_gcnt = INIT_ZERO;
+ char udc_gname[10] = {""};
+ const struct crg_udc_priv *priv;
+
+ priv = of_device_get_match_data(&pdev->dev);
+ sprintf(udc_gname, "gadget-%d", udc_gcnt);
+ crg_udc = devm_kzalloc(&pdev->dev, sizeof(*crg_udc), GFP_KERNEL);
+ if (!crg_udc)
+ return -ENOMEM;
+ crg_udc->dev = &pdev->dev;
+
+ spin_lock_init(&crg_udc->udc_lock);
+ platform_set_drvdata(pdev, crg_udc);
+
+ dev_set_name(&crg_udc->gadget.dev, udc_gname);
+ crg_udc->gadget.ops = &crg_gadget_ops;
+ crg_udc->gadget.ep0 = &crg_udc->udc_ep[0].usb_ep;
+ crg_udc->gadget.dev.parent = &pdev->dev;
+ INIT_LIST_HEAD(&crg_udc->gadget.ep_list);
+ if (priv->plat_setup_gen3) {
+ crg_udc->gadget.max_speed = USB_SPEED_SUPER;
+ crg_udc->gadget.speed = USB_SPEED_SUPER;
+ } else {
+ crg_udc->gadget.max_speed = USB_SPEED_HIGH;
+ crg_udc->gadget.speed = USB_SPEED_HIGH;
+ }
+ crg_udc->gadget.name = udc_gname;
+ crg_udc->gadget.sg_supported = true;
+ dev_dbg(crg_udc->dev, "%s sg support\n", __func__);
+ crg_udc->connected = 0;
+ crg_udc->dev_addr = 0;
+
+ crg_udc->udc_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!crg_udc->udc_res) {
+ dev_err(&pdev->dev, "missing memory resource\n");
+ return -ENODEV;
+ }
+
+ crg_udc->mmio_virt_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(crg_udc->mmio_virt_base)) {
+ dev_err(&pdev->dev, "mmio ioremap failed\n");
+ return PTR_ERR(crg_udc->mmio_virt_base);
+ }
+
+ /* set controller device role*/
+ writel((readl(crg_udc->mmio_virt_base + CRG_UDC_MODE_REG) |
+ CRGUDC_ROLE_DEVICE),
+ crg_udc->mmio_virt_base + CRG_UDC_MODE_REG);
+ for (i = 0; i < CRG_RING_NUM; i++) {
+ crg_udc->uicr[i] = crg_udc->mmio_virt_base +
+ CRG_UICR_OFFSET + i * CRG_UICR_STRIDE;
+
+ dev_dbg(crg_udc->dev, "crg_udc->uicr[%d] = %p\n", i,
+ crg_udc->uicr[i]);
+ }
+ crg_udc->uccr = crg_udc->mmio_virt_base + CRG_UCCR_OFFSET;
+
+ crg_udc_reset(crg_udc);
+
+ crg_udc_clear_portpm(crg_udc);
+
+ ret = reset_data_struct(crg_udc);
+ if (ret) {
+ dev_err(crg_udc->dev, "reset_data_struct error\n");
+ goto err0;
+ }
+
+ init_ep_info(crg_udc);
+ init_ep0(crg_udc);
+
+ EP0_Start(crg_udc);
+
+ crg_gadget_irq_init(pdev, crg_udc);
+
+ ret = usb_add_gadget_udc(&pdev->dev, &crg_udc->gadget);
+ if (ret)
+ goto err0;
+
+ udc_gcnt++;
+
+ return 0;
+
+err0:
+ return -1;
+}
+
+static void crg_udc_remove(struct platform_device *pdev)
+{
+ struct crg_gadget_dev *crg_udc = platform_get_drvdata(pdev);
+ u32 tmp = 0;
+
+ dev_dbg(crg_udc->dev, "%s %d called\n", __func__, __LINE__);
+
+ crg_udc->device_state = USB_STATE_ATTACHED;
+ crg_vbus_detect(crg_udc, 0);
+
+ usb_del_gadget_udc(&crg_udc->gadget);
+
+ /* set controller host role*/
+ tmp = readl(crg_udc->mmio_virt_base + CRG_UDC_MODE_REG) & ~0x1;
+ writel(tmp, crg_udc->mmio_virt_base + CRG_UDC_MODE_REG);
+
+ if (crg_udc->irq)
+ free_irq(crg_udc->irq, crg_udc);
+
+ platform_set_drvdata(pdev, 0);
+
+ dev_dbg(crg_udc->dev, "%s %d gadget remove\n", __func__, __LINE__);
+
+}
+
+static void crg_udc_shutdown(struct platform_device *pdev)
+{
+ struct crg_gadget_dev *crg_udc = platform_get_drvdata(pdev);
+
+ dev_dbg(crg_udc->dev, "%s %d called\n", __func__, __LINE__);
+
+ crg_udc->device_state = USB_STATE_ATTACHED;
+ crg_vbus_detect(crg_udc, 0);
+ usb_del_gadget_udc(&crg_udc->gadget);
+
+ if (crg_udc->irq)
+ free_irq(crg_udc->irq, crg_udc);
+ /*
+ * Clear the drvdata pointer.
+ */
+ platform_set_drvdata(pdev, 0);
+}
+
+#ifdef CONFIG_PM
+static int crg_udc_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int crg_udc_resume(struct device *dev)
+{
+
+
+ return 0;
+}
+#else
+#define crg_udc_suspend NULL
+#define crg_udc_resume NULL
+#endif
+
+static const struct dev_pm_ops crg_udc_pm_ops = {
+ .suspend = crg_udc_suspend,
+ .resume = crg_udc_resume,
+};
+
+static struct platform_driver crg_udc_driver = {
+ .probe = crg_udc_probe,
+ .remove = crg_udc_remove,
+ .shutdown = crg_udc_shutdown,
+ .driver = {
+ .name = "crg_udc",
+ .owner = THIS_MODULE,
+ .pm = &crg_udc_pm_ops,
+ .of_match_table = of_match_ptr(of_crg_udc_match),
+ },
+};
+
+static void crg_gadget_exit(void)
+{
+ platform_driver_unregister(&crg_udc_driver);
+}
+
+static int crg_gadget_init(void)
+{
+
+ return platform_driver_register(&crg_udc_driver);
+}
+
+module_init(crg_gadget_init);
+module_exit(crg_gadget_exit);
+
+MODULE_DESCRIPTION("Corigine USB Device Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/crg_udc.h b/drivers/usb/gadget/udc/crg_udc.h
new file mode 100644
index 000000000000..bb0cd724582c
--- /dev/null
+++ b/drivers/usb/gadget/udc/crg_udc.h
@@ -0,0 +1,364 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+/*
+ * Copyright (c) 2019 Corigine, Inc.
+ * Copyright (c) 2022-2026 Axiado Corporation.
+ */
+
+#ifndef __CRG_UDC_H__
+#define __CRG_UDC_H__
+
+#define CRG_UCCR_OFFSET (0x2400)
+#define CRG_UICR_OFFSET (0x2500)
+#define CRG_UICR_STRIDE (0x20)
+
+/* corigine usb 3.1 device core register macros */
+struct crg_uccr {
+ u32 capability; /*0x00*/
+ u32 resv0[3];
+
+ u32 config0; /*0x10*/
+ u32 config1;
+ u32 resv1[2];
+
+ u32 control; /*0x20*/
+ u32 status;
+ u32 dcbaplo;
+ u32 dcbaphi;
+ u32 portsc;
+ u32 u3portpmsc;
+ u32 u2portpmsc;
+ u32 u3portli;
+
+ u32 doorbell; /*0x40*/
+ u32 mfindex;
+ u32 speed_select;
+ u32 resv3[5];
+
+ u32 ep_enable; /*0x60*/
+ u32 ep_running;
+ u32 resv4[2];
+
+ u32 cmd_param0; /*0x70*/
+ u32 cmd_param1;
+ u32 cmd_control;
+ u32 resv5[1];
+
+ u32 odb_capability; /*0x80*/
+ u32 resv6[3];
+ u32 odb_config[8];
+
+ u32 debug0; /*0xB0*/
+};
+
+struct crg_uicr {
+ u32 iman;
+ u32 imod;
+ u32 erstsz;
+ u32 resv0;
+
+ u32 erstbalo; /*0x10*/
+ u32 erstbahi;
+ u32 erdplo;
+ u32 erdphi;
+};
+
+#define SETF_VAR(field, var, fieldval) \
+ (((var) & ~(field ## _MASK)) | \
+ (((fieldval) << field ## _SHIFT) & (field ## _MASK)))
+
+#define GETF(field, val) \
+ (((val) & (field ## _MASK)) >> (field ## _SHIFT))
+
+#define MAKEF_VAR(field, fieldval) \
+ (((fieldval) << field ## _SHIFT) & (field ## _MASK))
+
+/* interrupt registers array */
+#define CRG_U3DC_IRS_BASE (0x100L)
+#define CRG_U3DC_IRS_IMAN(x) \
+ (CRG_U3DC_IRS_BASE + (x) * 0x20L + 0x00L)
+#define CRG_U3DC_IRS_IMOD(x) \
+ (CRG_U3DC_IRS_BASE + (x) * 0x20L + 0x04L)
+#define CRG_U3DC_IRS_ERSTSZ(x) \
+ (CRG_U3DC_IRS_BASE + (x) * 0x20L + 0x08L)
+#define CRG_U3DC_IRS_ERSTBALO(x) \
+ (CRG_U3DC_IRS_BASE + (x) * 0x20L + 0x10L)
+#define CRG_U3DC_IRS_ERSTBAHI(x) \
+ (CRG_U3DC_IRS_BASE + (x) * 0x20L + 0x14L)
+#define CRG_U3DC_IRS_ERDPLO(x) \
+ (CRG_U3DC_IRS_BASE + (x) * 0x20L + 0x18L)
+#define CRG_U3DC_IRS_ERDPHI(x) \
+ (CRG_U3DC_IRS_BASE + (x) * 0x20L + 0x20L)
+
+/* reg config 0 & 1*/
+#define CRG_U3DC_CFG0_MAXSPEED_MASK (0xfL << 0)
+#define CRG_U3DC_CFG0_MAXSPEED_FS (0x1L << 0)
+#define CRG_U3DC_CFG0_MAXSPEED_HS (0x3L << 0)
+#define CRG_U3DC_CFG0_MAXSPEED_SS (0x4L << 0)
+#define CRG_U3DC_CFG0_MAXSPEED_SSP (0x5L << 0)
+
+#define CRG_U3DC_CFG1_CSC_EVENT_EN BIT(0)
+#define CRG_U3DC_CFG1_PEC_EVENT_EN BIT(1)
+#define CRG_U3DC_CFG1_PPC_EVENT_EN BIT(3)
+#define CRG_U3DC_CFG1_PRC_EVENT_EN BIT(4)
+#define CRG_U3DC_CFG1_PLC_EVENT_EN BIT(5)
+#define CRG_U3DC_CFG1_CEC_EVENT_EN BIT(6)
+
+#define CRG_U3DC_CFG1_U3_ENTRY_EN BIT(8)
+#define CRG_U3DC_CFG1_L1_ENTRY_EN BIT(9)
+#define CRG_U3DC_CFG1_U3_RESUME_EN BIT(10)
+#define CRG_U3DC_CFG1_L1_RESUME_EN BIT(11)
+#define CRG_U3DC_CFG1_INACTIVE_PLC_EN BIT(12)
+#define CRG_U3DC_CFG1_U3_RESUME_NORESP_PLC_EN BIT(13)
+#define CRG_U3DC_CFG1_U2_RESUME_NORESP_PLC_EN BIT(14)
+
+#define CRG_U3DC_CFG1_SETUP_EVENT_EN BIT(16)
+
+/* ctrl register*/
+#define CRG_U3DC_CTRL_RUN BIT(0)
+#define CRG_U3DC_CTRL_STOP (0)
+#define CRG_U3DC_CTRL_SWRST BIT(1)
+#define CRG_U3DC_CTRL_INT_EN BIT(2)
+#define CRG_U3DC_CTRL_SYSERR_EN BIT(3)
+#define CRG_U3DC_CTRL_EWE BIT(10)
+#define CRG_U3DC_CTRL_KP_CNCT BIT(11)
+
+/*status register*/
+#define CRG_U3DC_STATUS_DEV_CTRL_HALT BIT(0)
+#define CRG_U3DC_STATUS_SYS_ERR BIT(2)
+#define CRG_U3DC_STATUS_EINT BIT(3)
+
+/*portsc register*/
+#define CRG_U3DC_PORTSC_CCS BIT(0)
+#define CRG_U3DC_PORTSC_PED BIT(1)
+#define CRG_U3DC_PORTSC_PP BIT(3)
+#define CRG_U3DC_PORTSC_PR BIT(4)
+
+#define CRG_U3DC_PORTSC_PLS_SHIFT (5)
+#define CRG_U3DC_PORTSC_PLS_MASK (0xf << CRG_U3DC_PORTSC_PLS_SHIFT)
+#define CRG_U3DC_PORTSC_PLS(fv) (MAKEF_VAR(CRG_U3DC_PORTSC_PLS, (fv)))
+#define CRG_U3DC_PORTSC_PLS_GET(v) (GETF(CRG_U3DC_PORTSC_PLS, (v)))
+
+#define CRG_U3DC_PORTSC_SPEED_SHIFT (10)
+#define CRG_U3DC_PORTSC_SPEED_MASK \
+ (0xf << CRG_U3DC_PORTSC_SPEED_SHIFT)
+#define CRG_U3DC_PORTSC_SPEED(fv) \
+ (MAKEF_VAR(CRG_U3DC_PORTSC_SPEED, (fv)))
+#define CRG_U3DC_PORTSC_SPEED_GET(v) \
+ (GETF(CRG_U3DC_PORTSC_SPEED, (v)))
+#define CRG_U3DC_PORTSC_SPEED_FS (0x1)
+#define CRG_U3DC_PORTSC_SPEED_LS (0x2)
+#define CRG_U3DC_PORTSC_SPEED_HS (0x3)
+#define CRG_U3DC_PORTSC_SPEED_SS (0x4)
+#define CRG_U3DC_PORTSC_SPEED_SSP (0x5)
+
+#define CRG_U3DC_PORTSC_LWS BIT(16)
+#define CRG_U3DC_PORTSC_CSC BIT(17)
+#define CRG_U3DC_PORTSC_PEC BIT(18)
+#define CRG_U3DC_PORTSC_PPC BIT(20)
+#define CRG_U3DC_PORTSC_PRC BIT(21)
+#define CRG_U3DC_PORTSC_PLC BIT(22)
+#define CRG_U3DC_PORTSC_CEC BIT(23)
+#define CRG_U3DC_PORTSC_WCE BIT(25)
+#define CRG_U3DC_PORTSC_WDE BIT(26)
+#define CRG_U3DC_PORTSC_WPR BIT(31)
+
+#define PORTSC_W1C_MASK (CRG_U3DC_PORTSC_CSC | \
+ CRG_U3DC_PORTSC_PEC | \
+ CRG_U3DC_PORTSC_PPC | \
+ CRG_U3DC_PORTSC_PRC | \
+ CRG_U3DC_PORTSC_PLC | \
+ CRG_U3DC_PORTSC_CEC)
+#define PORTSC_WRITE_MASK (~PORTSC_W1C_MASK)
+
+/* u3portpmsc */
+#define CRG_U3DC_U3PORTPM_U1TMOUT_SHIFT (0)
+#define CRG_U3DC_U3PORTPM_U1TMOUT_MASK \
+ (0xff << CRG_U3DC_U3PORTPM_U1TMOUT_SHIFT)
+#define CRG_U3DC_U3PORTPM_U1TMOUT(fv) \
+ (MAKEF_VAR(CRG_U3DC_U3PORTPM_U1TMOUT, (fv)))
+
+#define CRG_U3DC_U3PORTPM_U2TMOUT_SHIFT (8)
+#define CRG_U3DC_U3PORTPM_U2TMOUT_MASK \
+ (0xff << CRG_U3DC_U3PORTPM_U2TMOUT_SHIFT)
+#define CRG_U3DC_U3PORTPM_U2TMOUT(fv) \
+ (MAKEF_VAR(CRG_U3DC_U3PORTPM_U2TMOUT, (fv)))
+
+#define CRG_U3DC_U3PORTPM_FLA BIT(16)
+
+#define CRG_U3DC_U3PORTPM_U1IEN_SHIFT (20)
+#define CRG_U3DC_U3PORTPM_U1IEN \
+ (1L << CRG_U3DC_U3PORTPM_U1IEN_SHIFT)
+
+#define CRG_U3DC_U3PORTPM_U2IEN_SHIFT (21)
+#define CRG_U3DC_U3PORTPM_U2IEN \
+ (1L << CRG_U3DC_U3PORTPM_U2IEN_SHIFT)
+
+#define CRG_U3DC_U3PORTPM_U1AEN_SHIFT (22)
+#define CRG_U3DC_U3PORTPM_U1AEN \
+ (1L << CRG_U3DC_U3PORTPM_U1AEN_SHIFT)
+
+#define CRG_U3DC_U3PORTPM_U2AEN_SHIFT (23)
+#define CRG_U3DC_U3PORTPM_U2AEN \
+ (1L << CRG_U3DC_U3PORTPM_U2AEN_SHIFT)
+
+#define CRG_U3DC_U3PORTPM_U1U2TMOUT_SHIFT (24)
+#define CRG_U3DC_U3PORTPM_U1U2TMOUT_MASK \
+ (0xff << CRG_U3DC_U3PORTPM_U1U2TMOUT_SHIFT)
+
+/* u2portpmsc */
+#define CRG_U3DC_U2PORTPM_RJ_TH_SHIFT (0)
+#define CRG_U3DC_U2PORTPM_RJ_TH_MASK \
+ (0xf << CRG_U3DC_U2PORTPM_RJ_TH_SHIFT)
+#define CRG_U3DC_U2PORTPM_RJ_TH(fv) \
+ (MAKEF_VAR(CRG_U3DC_U2PORTPM_RJ_TH, (fv)))
+
+#define CRG_U3DC_U2PORTPM_DS_TH_SHIFT (4)
+#define CRG_U3DC_U2PORTPM_DS_TH_MASK \
+ (0xf << CRG_U3DC_U2PORTPM_DS_TH_SHIFT)
+#define CRG_U3DC_U2PORTPM_DS_TH(fv) \
+ (MAKEF_VAR(CRG_U3DC_U2PORTPM_DS_TH, (fv)))
+
+#define CRG_U3DC_U2PORTPM_LPM_EN BIT(8)
+#define CRG_U3DC_U2PORTPM_RJ_TH_EN BIT(9)
+#define CRG_U3DC_U2PORTPM_DS_EN BIT(10)
+#define CRG_U3DC_U2PORTPM_SLP_EN BIT(11)
+#define CRG_U3DC_U2PORTPM_LPM_FACK BIT(12)
+#define CRG_U3DC_U2PORTPM_L1_AEX BIT(13)
+#define CRG_U3DC_U2PORTPM_H_B_SHIFT (16)
+#define CRG_U3DC_U2PORTPM_H_B_MASK \
+ (0xf << CRG_U3DC_U2PORTPM_H_B_SHIFT)
+#define CRG_U3DC_U2PORTPM_H_B(fv) \
+ (MAKEF_VAR(CRG_U3DC_U2PORTPM_H_B, (fv)))
+
+#define CRG_U3DC_U2PORTPM_RWE BIT(20)
+
+#define CRG_U3DC_U2PORTPM_TM_SHIFT (28)
+#define CRG_U3DC_U2PORTPM_TM_MASK \
+ (0xf << CRG_U3DC_U2PORTPM_TM_SHIFT)
+#define CRG_U3DC_U2PORTPM_TM(fv) \
+ (MAKEF_VAR(CRG_U3DC_U2PORTPM_TM, (fv)))
+
+/* doorbell register*/
+#define CRG_U3DC_DB_TARGET_SHIFT (0)
+#define CRG_U3DC_DB_TARGET_MASK \
+ (0x1f << CRG_U3DC_DB_TARGET_SHIFT)
+#define CRG_U3DC_DB_TARGET(fv) \
+ (MAKEF_VAR(CRG_U3DC_DB_TARGET, (fv)))
+
+/* odb registers*/
+#define CRG_U3DC_ODBCFG_2N_OFFSET_SHIFT (0)
+#define CRG_U3DC_ODBCFG_2N_OFFSET_MASK \
+ (0x3ff << CRG_U3DC_ODBCFG_2N_OFFSET_SHIFT)
+#define CRG_U3DC_ODBCFG_2N_OFFSET(fv) \
+ (MAKEF_VAR(CRG_U3DC_ODBCFG_2N_OFFSET, (fv)))
+
+#define CRG_U3DC_ODBCFG_2N_SIZE_SHIFT (10)
+#define CRG_U3DC_ODBCFG_2N_SIZE_MASK \
+ (0x7 << CRG_U3DC_ODBCFG_2N_SIZE_SHIFT)
+#define CRG_U3DC_ODBCFG_2N_SIZE(fv) \
+ (MAKEF_VAR(CRG_U3DC_ODBCFG_2N_SIZE, (fv)))
+
+#define CRG_U3DC_ODBCFG_2N1_OFFSET_SHIFT (16)
+#define CRG_U3DC_ODBCFG_2N1_OFFSET_MASK \
+ (0x3ff << CRG_U3DC_ODBCFG_2N1_OFFSET_SHIFT)
+#define CRG_U3DC_ODBCFG_2N1_OFFSET(fv) \
+ (MAKEF_VAR(CRG_U3DC_ODBCFG_2N1_OFFSET, (fv)))
+
+#define CRG_U3DC_ODBCFG_2N1_SIZE_SHIFT (26)
+#define CRG_U3DC_ODBCFG_2N1_SIZE_MASK \
+ (0x7 << CRG_U3DC_ODBCFG_2N1_SIZE_SHIFT)
+#define CRG_U3DC_ODBCFG_2N1_SIZE(fv) \
+ (MAKEF_VAR(CRG_U3DC_ODBCFG_2N1_SIZE, (fv)))
+
+/* command control register*/
+#define CRG_U3DC_CMD_CTRL_ACTIVE_SHIFT (0)
+#define CRG_U3DC_CMD_CTRL_ACTIVE \
+ (1L << CRG_U3DC_CMD_CTRL_ACTIVE_SHIFT)
+#define CRG_U3DC_CMD_CTRL_IOC_SHIFT (1)
+#define CRG_U3DC_CMD_CTRL_IOC_EN \
+ (1L << CRG_U3DC_CMD_CTRL_IOC_SHIFT)
+
+#define CRG_U3DC_CMD_CTRL_TYPE_SHIFT (4)
+#define CRG_U3DC_CMD_CTRL_TYPE_MASK \
+ (0xf << CRG_U3DC_CMD_CTRL_TYPE_SHIFT)
+#define CRG_U3DC_CMD_CTRL_TYPE(fv) \
+ (MAKEF_VAR(CRG_U3DC_CMD_CTRL_TYPE, (fv)))
+
+#define CRG_U3DC_CMD_CTRL_STATUS_SHIFT (16)
+#define CRG_U3DC_CMD_CTRL_STATUS_MASK \
+ (0xf << CRG_U3DC_CMD_CTRL_STATUS_SHIFT)
+#define CRG_U3DC_CMD_CTRL_STATUS(fv) \
+ (MAKEF_VAR(CRG_U3DC_CMD_CTRL_STATUS, (fv)))
+#define CRG_U3DC_CMD_CTRL_STATUS_GET(v) \
+ (GETF(CRG_U3DC_CMD_CTRL_STATUS, (v)))
+
+#define CRG_U3DC_CMD_INIT_EP0 (0L)
+#define CRG_U3DC_CMD_UPDATE_EP0 (1L)
+#define CRG_U3DC_CMD_SET_ADDRESS (2L)
+#define CRG_U3DC_CMD_SEND_DEV_NOTIFY (3L)
+#define CRG_U3DC_CMD_CONFIG_EP (4L)
+#define CRG_U3DC_CMD_SET_HALT (5L)
+#define CRG_U3DC_CMD_CLR_HALT (6L)
+#define CRG_U3DC_CMD_RST_SEQNUM (7L)
+#define CRG_U3DC_CMD_STOP_EP (8L)
+#define CRG_U3DC_CMD_SET_TR_DQPTR (9L)
+#define CRG_U3DC_CMD_FORCE_FLOW_CTRL (10L)
+#define CRG_U3DC_CMD_REQ_LDM_EXCHAG (11L)
+
+/* int register*/
+/* iman bits*/
+#define CRG_U3DC_IMAN_INT_PEND BIT(0)
+#define CRG_U3DC_IMAN_INT_EN BIT(1)
+
+/* erdp bits*/
+#define CRG_U3DC_ERDPLO_EHB BIT(3)
+#define CRG_U3DC_ERDPLO_ADDRLO(fv) ((fv) & 0xfffffff0)
+
+/*command params*/
+/*command0 init ep0*/
+#define CRG_CMD0_0_DQPTRLO_SHIFT (4)
+#define CRG_CMD0_0_DQPTRLO_MASK \
+ (0x0fffffff << CRG_CMD0_0_DQPTRLO_SHIFT)
+
+#define CRG_CMD0_0_DCS_SHIFT (0)
+#define CRG_CMD0_0_DCS_MASK (0x1 << CRG_CMD0_0_DCS_SHIFT)
+#define CRG_CMD0_0_DCS(fv) (MAKEF_VAR(CRG_CMD0_0_DCS, (fv)))
+
+/*command1 update ep0 */
+#define CRG_CMD1_0_MPS_SHIFT (16)
+#define CRG_CMD1_0_MPS_MASK (0xffff << CRG_CMD1_0_MPS_SHIFT)
+#define CRG_CMD1_0_MPS(fv) (MAKEF_VAR(CRG_CMD1_0_MPS, (fv)))
+
+/*command2 set addr */
+#define CRG_CMD2_0_DEV_ADDR_SHIFT (0)
+#define CRG_CMD2_0_DEV_ADDR_MASK (0xff << CRG_CMD2_0_DEV_ADDR_SHIFT)
+#define CRG_CMD2_0_DEV_ADDR(fv) (MAKEF_VAR(CRG_CMD2_0_DEV_ADDR, (fv)))
+
+#define CRG_UDC_MODE_REG 0x20fc
+/* Vendor specific register */
+#define CRG_UDC_VENDOR_REG 0x210c
+#define CRG_UDC_VBUS_DETECT BIT(26)
+/*command type*/
+enum crg_cmd_type {
+ CRG_CMD_INIT_EP0 = 0,
+ CRG_CMD_UPDATE_EP0_CFG = 1,
+ CRG_CMD_SET_ADDR = 2,
+ CRG_CMD_SEND_DEV_NOTIFICATION = 3,
+ CRG_CMD_CONFIG_EP = 4,
+ CRG_CMD_SET_HALT = 5,
+ CRG_CMD_CLEAR_HALT = 6,
+ CRG_CMD_RESET_SEQNUM = 7,
+ CRG_CMD_STOP_EP = 8,
+ CRG_CMD_SET_TR_DQPTR = 9,
+ CRG_CMD_FORCE_FLOW_CONTROL = 10,
+ CRG_CMD_REQ_LDM_EXCHANGE = 11
+};
+
+struct crg_gadget_dev;
+
+struct crg_udc_platdata {
+ int num_out_eps;
+ int num_in_eps;
+};
+
+#endif /* __CRG_UDC_H__ */
--
2.34.1
On Mon, Feb 02, 2026 at 05:16:29AM -0800, Vladimir Moravcevic wrote:
> Add Corigine USB IP Driver for Axiado AX3000 SoC's
> USB peripheral (USB 2.0/3.0).
> The driver is based on the Corigine USB IP core with
> Axiado-specific enhancements including VBUS detection and USB link
> stability fixes.
>
> The driver supports both USB 2.0 High-Speed and USB 3.0 SuperSpeed
> modes with control, bulk, interrupt, and isochronous transfer types.
>
> Co-developed-by: Krutik Shah <krutikshah@axiado.com>
> Signed-off-by: Krutik Shah <krutikshah@axiado.com>
> Co-developed-by: Prasad Bolisetty <pbolisetty@axiado.com>
> Signed-off-by: Prasad Bolisetty <pbolisetty@axiado.com>
> Signed-off-by: Vladimir Moravcevic <vmoravcevic@axiado.com>
> ---
> drivers/usb/gadget/udc/Kconfig | 15 +
> drivers/usb/gadget/udc/Makefile | 1 +
> drivers/usb/gadget/udc/crg_udc.c | 4522 ++++++++++++++++++++++++++++++++++++++
> drivers/usb/gadget/udc/crg_udc.h | 364 +++
> 4 files changed, 4902 insertions(+)
>
> diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
> index 26460340fbc9..b94d113aad99 100644
> --- a/drivers/usb/gadget/udc/Kconfig
> +++ b/drivers/usb/gadget/udc/Kconfig
> @@ -417,6 +417,21 @@ config USB_ASPEED_UDC
> dynamically linked module called "aspeed_udc" and force all
> gadget drivers to also be dynamically linked.
>
> +config USB_CRG_UDC
> + tristate "AXIADO CORIGINE-based AX3000 Device Controller"
> + depends on ARCH_AXIADO || COMPILE_TEST
> + depends on USB_GADGET
> + help
> + Enables AX3000 USB device controller driver for Axiado
> + SoCs and evaluation boards.
> +
> + Based on the Corigine USB IP core driver with Axiado specific
> + enhancements. Supports USB 2.0 (High-Speed) and USB 3.0
> + (SuperSpeed), including control, bulk, interrupt, and
> + isochronous transfers.
> +
> + Say "y" to build statically, or "m" to build as a module.
What is the module name? The other entries in this file all describe
this.
> --- /dev/null
> +++ b/drivers/usb/gadget/udc/crg_udc.c
> @@ -0,0 +1,4522 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
That is very odd, and I need a bit of justification as to why, and how,
MIT is allowed here. Did you look at any of the existing gadget udc
drivers when working on this code? If so, how can MIT still work?
Anyway, as this is a "not normal" selection for this type of driver, I
will need a signed-off-by from your corporate lawyer with the reason why
it is dual licensed described in the changelog comment for it showing
that you all understand all of the issues involved in doing something
like this, and attempting to keep it under a dual license over time.
thanks,
greg k-h
> +//
> +// Copyright (c) 2019 Corigine Inc.
> +// Copyright (c) 2022-2026 Axiado Corporation.
> +//
> +
> +#include <linux/net.h>
> +#include <asm/byteorder.h>
> +#include <linux/errno.h>
> +#include <linux/io.h>
> +#include <linux/unaligned.h>
> +#include <linux/types.h>
> +#include <linux/bitops.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/usb/ch9.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/delay.h>
> +#include <linux/irq.h>
> +#include <linux/irqreturn.h>
> +#include <linux/interrupt.h>
> +#include <linux/ctype.h>
> +#include <linux/module.h>
> +#include <linux/sched.h>
> +#include <linux/kthread.h>
> +#include <linux/err.h>
> +#include <linux/wait.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_gpio.h>
> +#include <linux/scatterlist.h>
> +#include "crg_udc.h"
> +
> +#define INIT_ZERO -1
Why is "ZERO" defined as -1?
> +#define UDC_FALSE false
Please just use "false" where needed.
> +
> +#define MAX_PACKET_SIZE 1024
> +
> +#define DMA_ADDR_INVALID (~(dma_addr_t)0)
Isn't this in the dma headers somewhere instead? if not, why not?
And you mix tabs with spaces after the "#define" on these lists, please
don't.
> +
> +#define CRG_ERST_SIZE 1
> +#define CRG_EVENT_RING_SIZE 256
Why no tabs here, but:
> +#define CRG_NUM_EP_CX 32
Tabs here? Be consistent please.
> +#define TRB_MAX_BUFFER_SIZE 65536
> +#define CRGUDC_CONTROL_EP_TD_RING_SIZE 16
> +#define CRGUDC_BULK_EP_TD_RING_SIZE 1024
> +#define CRGUDC_ISOC_EP_TD_RING_SIZE 32
> +#define CRGUDC_INT_EP_TD_RING_SIZE 8
> +#define CRGUDC_ROLE_DEVICE 0x1
> +
> +#define U1_TIMEOUT_VAL 0x70
> +#define U2_TIMEOUT_VAL 0x70
And then tabs here after "define"?
Anyway, it just stands out instantly as something odd.
> +
> +#define STATE_USB_LINK_STABLE 4
> +
> +/*********Feature switches********************/
> +#define U12_FORBIDDEN 1
> +#define U12_INITIATE_FORBIDDEN 1
> +#define CRG_UDC_INT_EN
> +#define REINIT_EP0_ON_BUS_RESET
We do not have "feature switches" in kernel drivers that require you to
rebuild the code. Please handle these properly like all other drivers
do (i.e. not this way.)
> +/*Table 127*/
No spaces?
Anyway, what is "table 127"? And what is it for?
> +enum TRB_CMPL_CODES_E {
> + CMPL_CODE_INVALID = 0,
> + CMPL_CODE_SUCCESS,
> + CMPL_CODE_DATA_BUFFER_ERR,
> + CMPL_CODE_BABBLE_DETECTED_ERR,
> + CMPL_CODE_USB_TRANS_ERR,
> + CMPL_CODE_TRB_ERR, /*5*/
If this really is "5", then set it to 5!
> + CMPL_CODE_TRB_STALL,
> + CMPL_CODE_INVALID_STREAM_TYPE_ERR = 10,
> + CMPL_CODE_SHORT_PKT = 13,
> + CMPL_CODE_RING_UNDERRUN,
> + CMPL_CODE_RING_OVERRUN, /*15*/
Same here. Don't assume that enums will be properly set without
actually setting them all, as I don't think the C standard guarantees
this (I could be wrong, but it can trip you up...)
If you want a specific value, set it so you _know_ it will be correct.
> + CMPL_CODE_EVENT_RING_FULL_ERR = 21,
> + CMPL_CODE_STOPPED = 26,
> + CMPL_CODE_STOPPED_LENGTH_INVALID = 27,
> + CMPL_CODE_ISOCH_BUFFER_OVERRUN = 31,
> + /*192-224 vendor defined error*/
You are the vendor!
> + CMPL_CODE_PROTOCOL_STALL = 192,
> + CMPL_CODE_SETUP_TAG_MISMATCH = 193,
> + CMPL_CODE_HALTED = 194,
> + CMPL_CODE_HALTED_LENGTH_INVALID = 195,
> + CMPL_CODE_DISABLED = 196,
> + CMPL_CODE_DISABLED_LENGTH_INVALID = 197,
> +};
> +
> +static const char driver_name[] = "crg_udc";
Why is this needed and why not use KBUILD_MODNAME instead?
> +
> +struct buffer_info {
> + void *vaddr;
What is a vaddr?
I'll stop here. Also note the 0-day bot issues that it found, which
precludes us from take this as-is.
thanks,
greg k-h
On 02/02/2026 14:16, Vladimir Moravcevic wrote:
> Add Corigine USB IP Driver for Axiado AX3000 SoC's
> USB peripheral (USB 2.0/3.0).
> The driver is based on the Corigine USB IP core with
> Axiado-specific enhancements including VBUS detection and USB link
> stability fixes.
This driver looks way too complicated for simple USB controller, so I
guess you just re-implemented a lot of Linux stack or other drivers.
Also did not pass basic litmus test for sending usu 15 year old junk
code, which disqualifies it from review IMO. There is simply no point to
review code from 15 yaers ago - you should never start with such code.
> +static const struct crg_udc_priv ax3000_plat_setup_gen2 = {
> + .plat_setup_gen3 = false,
> +};
> +
> +static const struct crg_udc_priv ax3000_plat_setup_gen3 = {
> + .plat_setup_gen3 = true,
> +};
> +
> +/**
> + * crg_gadget_probe - Initializes gadget driver
> + *
> + *
> + * Returns 0 on success otherwise negative errno.
> + */
Completely pointless and wrongly placed comment. Do not ever add such
comments.
> +
> +static const struct of_device_id of_crg_udc_match[] = {
> + {
> + .compatible = "axiado,ax3000-udc",
> + .data = &ax3000_plat_setup_gen2
> + },
> + {
> + .compatible = "axiado,ax3000-udc-gen3",
> + .data = &ax3000_plat_setup_gen3
> + },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, of_crg_udc_match);
> +
> +static int crg_udc_probe(struct platform_device *pdev)
> +{
> + int ret;
> + int i;
> + struct crg_gadget_dev *crg_udc;
> + static int udc_gcnt = INIT_ZERO;
> + char udc_gname[10] = {""};
> + const struct crg_udc_priv *priv;
> +
> + priv = of_device_get_match_data(&pdev->dev);
> + sprintf(udc_gname, "gadget-%d", udc_gcnt);
> + crg_udc = devm_kzalloc(&pdev->dev, sizeof(*crg_udc), GFP_KERNEL);
> + if (!crg_udc)
> + return -ENOMEM;
> + crg_udc->dev = &pdev->dev;
> +
> + spin_lock_init(&crg_udc->udc_lock);
> + platform_set_drvdata(pdev, crg_udc);
> +
> + dev_set_name(&crg_udc->gadget.dev, udc_gname);
> + crg_udc->gadget.ops = &crg_gadget_ops;
> + crg_udc->gadget.ep0 = &crg_udc->udc_ep[0].usb_ep;
> + crg_udc->gadget.dev.parent = &pdev->dev;
> + INIT_LIST_HEAD(&crg_udc->gadget.ep_list);
> + if (priv->plat_setup_gen3) {
> + crg_udc->gadget.max_speed = USB_SPEED_SUPER;
> + crg_udc->gadget.speed = USB_SPEED_SUPER;
> + } else {
> + crg_udc->gadget.max_speed = USB_SPEED_HIGH;
> + crg_udc->gadget.speed = USB_SPEED_HIGH;
> + }
> + crg_udc->gadget.name = udc_gname;
> + crg_udc->gadget.sg_supported = true;
> + dev_dbg(crg_udc->dev, "%s sg support\n", __func__);
> + crg_udc->connected = 0;
> + crg_udc->dev_addr = 0;
> +
> + crg_udc->udc_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!crg_udc->udc_res) {
> + dev_err(&pdev->dev, "missing memory resource\n");
> + return -ENODEV;
> + }
> +
> + crg_udc->mmio_virt_base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(crg_udc->mmio_virt_base)) {
> + dev_err(&pdev->dev, "mmio ioremap failed\n");
> + return PTR_ERR(crg_udc->mmio_virt_base);
> + }
> +
> + /* set controller device role*/
> + writel((readl(crg_udc->mmio_virt_base + CRG_UDC_MODE_REG) |
> + CRGUDC_ROLE_DEVICE),
> + crg_udc->mmio_virt_base + CRG_UDC_MODE_REG);
> + for (i = 0; i < CRG_RING_NUM; i++) {
> + crg_udc->uicr[i] = crg_udc->mmio_virt_base +
> + CRG_UICR_OFFSET + i * CRG_UICR_STRIDE;
> +
> + dev_dbg(crg_udc->dev, "crg_udc->uicr[%d] = %p\n", i,
> + crg_udc->uicr[i]);
> + }
> + crg_udc->uccr = crg_udc->mmio_virt_base + CRG_UCCR_OFFSET;
> +
> + crg_udc_reset(crg_udc);
> +
> + crg_udc_clear_portpm(crg_udc);
> +
> + ret = reset_data_struct(crg_udc);
> + if (ret) {
> + dev_err(crg_udc->dev, "reset_data_struct error\n");
> + goto err0;
> + }
> +
> + init_ep_info(crg_udc);
> + init_ep0(crg_udc);
> +
> + EP0_Start(crg_udc);
Did you read coding style?
> +
> + crg_gadget_irq_init(pdev, crg_udc);
> +
> + ret = usb_add_gadget_udc(&pdev->dev, &crg_udc->gadget);
> + if (ret)
> + goto err0;
> +
> + udc_gcnt++;
> +
> + return 0;
> +
> +err0:
> + return -1;
What?
> +}
> +
> +static void crg_udc_remove(struct platform_device *pdev)
> +{
> + struct crg_gadget_dev *crg_udc = platform_get_drvdata(pdev);
> + u32 tmp = 0;
> +
> + dev_dbg(crg_udc->dev, "%s %d called\n", __func__, __LINE__);
> +
> + crg_udc->device_state = USB_STATE_ATTACHED;
> + crg_vbus_detect(crg_udc, 0);
> +
> + usb_del_gadget_udc(&crg_udc->gadget);
> +
> + /* set controller host role*/
> + tmp = readl(crg_udc->mmio_virt_base + CRG_UDC_MODE_REG) & ~0x1;
> + writel(tmp, crg_udc->mmio_virt_base + CRG_UDC_MODE_REG);
> +
> + if (crg_udc->irq)
> + free_irq(crg_udc->irq, crg_udc);
> +
> + platform_set_drvdata(pdev, 0);
> +
> + dev_dbg(crg_udc->dev, "%s %d gadget remove\n", __func__, __LINE__);
Drop all such debugs.
> +
> +}
> +
> +static void crg_udc_shutdown(struct platform_device *pdev)
> +{
> + struct crg_gadget_dev *crg_udc = platform_get_drvdata(pdev);
> +
> + dev_dbg(crg_udc->dev, "%s %d called\n", __func__, __LINE__);
It's really pointless code.
> +
> + crg_udc->device_state = USB_STATE_ATTACHED;
> + crg_vbus_detect(crg_udc, 0);
> + usb_del_gadget_udc(&crg_udc->gadget);
> +
> + if (crg_udc->irq)
> + free_irq(crg_udc->irq, crg_udc);
> + /*
> + * Clear the drvdata pointer.
> + */
> + platform_set_drvdata(pdev, 0);
> +}
> +
> +#ifdef CONFIG_PM
> +static int crg_udc_suspend(struct device *dev)
> +{
> + return 0;
> +}
> +
> +static int crg_udc_resume(struct device *dev)
> +{
> +
> +
> + return 0;
> +}
> +#else
> +#define crg_udc_suspend NULL
> +#define crg_udc_resume NULL
> +#endif
> +
> +static const struct dev_pm_ops crg_udc_pm_ops = {
> + .suspend = crg_udc_suspend,
> + .resume = crg_udc_resume,
> +};
> +
> +static struct platform_driver crg_udc_driver = {
> + .probe = crg_udc_probe,
> + .remove = crg_udc_remove,
> + .shutdown = crg_udc_shutdown,
> + .driver = {
> + .name = "crg_udc",
> + .owner = THIS_MODULE,
Do not upstream 10 or 15 year old driver. Why do we need to repeat all
the same comments as we repeated for last 15 years? Take newest driver
as starting point, not 15 year old code. You just replicated all old issues.
Best regards,
Krzysztof
Hi Vladimir,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 63804fed149a6750ffd28610c5c1c98cce6bd377]
url: https://github.com/intel-lab-lkp/linux/commits/Vladimir-Moravcevic/dt-bindings-usb-axiado-ax3000-udc-Add-Axiado-UDC/20260202-211951
base: 63804fed149a6750ffd28610c5c1c98cce6bd377
patch link: https://lore.kernel.org/r/20260202-axiado-ax3000-usb-device-controller-v1-2-45ce0a8b014f%40axiado.com
patch subject: [PATCH 2/3] usb: gadget: udc: Add UDC driver for Axiado Device controller IP Corigine
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20260203/202602030223.QlbiPC8d-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260203/202602030223.QlbiPC8d-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602030223.QlbiPC8d-lkp@intel.com/
All warnings (new ones prefixed by >>):
In file included from include/linux/printk.h:621,
from include/asm-generic/bug.h:31,
from ./arch/nios2/include/generated/asm/bug.h:1,
from include/linux/bug.h:5,
from include/linux/random.h:6,
from include/linux/net.h:18,
from drivers/usb/gadget/udc/crg_udc.c:7:
drivers/usb/gadget/udc/crg_udc.c: In function 'crg_udc_epcx_setup':
>> drivers/usb/gadget/udc/crg_udc.c:624:31: warning: format '%ld' expects argument of type 'long int', but argument 5 has type 'unsigned int' [-Wformat=]
624 | dev_dbg(crg_udc->dev, "DCI %d, sizeof ep_cx %ld\n", DCI, sizeof(struct ep_cx_s));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dynamic_debug.h:231:29: note: in definition of macro '__dynamic_func_call_cls'
231 | func(&id, ##__VA_ARGS__); \
| ^~~~~~~~~~~
include/linux/dynamic_debug.h:261:9: note: in expansion of macro '_dynamic_func_call_cls'
261 | _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~
include/linux/dynamic_debug.h:284:9: note: in expansion of macro '_dynamic_func_call'
284 | _dynamic_func_call(fmt, __dynamic_dev_dbg, \
| ^~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:165:9: note: in expansion of macro 'dynamic_dev_dbg'
165 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~
include/linux/dev_printk.h:165:30: note: in expansion of macro 'dev_fmt'
165 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/usb/gadget/udc/crg_udc.c:624:9: note: in expansion of macro 'dev_dbg'
624 | dev_dbg(crg_udc->dev, "DCI %d, sizeof ep_cx %ld\n", DCI, sizeof(struct ep_cx_s));
| ^~~~~~~
drivers/usb/gadget/udc/crg_udc.c:624:55: note: format string is defined here
624 | dev_dbg(crg_udc->dev, "DCI %d, sizeof ep_cx %ld\n", DCI, sizeof(struct ep_cx_s));
| ~~^
| |
| long int
| %d
In file included from include/linux/printk.h:621,
from include/asm-generic/bug.h:31,
from ./arch/nios2/include/generated/asm/bug.h:1,
from include/linux/bug.h:5,
from include/linux/random.h:6,
from include/linux/net.h:18,
from drivers/usb/gadget/udc/crg_udc.c:7:
drivers/usb/gadget/udc/crg_udc.c: In function 'setup_datastage_trb':
>> drivers/usb/gadget/udc/crg_udc.c:774:31: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'dma_addr_t' {aka 'unsigned int'} [-Wformat=]
774 | dev_dbg(crg_udc->dev, "dma = 0x%llx, ", usb_req->dma);
| ^~~~~~~~~~~~~~~~
include/linux/dynamic_debug.h:231:29: note: in definition of macro '__dynamic_func_call_cls'
231 | func(&id, ##__VA_ARGS__); \
| ^~~~~~~~~~~
include/linux/dynamic_debug.h:261:9: note: in expansion of macro '_dynamic_func_call_cls'
261 | _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~
include/linux/dynamic_debug.h:284:9: note: in expansion of macro '_dynamic_func_call'
284 | _dynamic_func_call(fmt, __dynamic_dev_dbg, \
| ^~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:165:9: note: in expansion of macro 'dynamic_dev_dbg'
165 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~
include/linux/dev_printk.h:165:30: note: in expansion of macro 'dev_fmt'
165 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/usb/gadget/udc/crg_udc.c:774:9: note: in expansion of macro 'dev_dbg'
774 | dev_dbg(crg_udc->dev, "dma = 0x%llx, ", usb_req->dma);
| ^~~~~~~
drivers/usb/gadget/udc/crg_udc.c:774:43: note: format string is defined here
774 | dev_dbg(crg_udc->dev, "dma = 0x%llx, ", usb_req->dma);
| ~~~^
| |
| long long unsigned int
| %x
In file included from include/linux/printk.h:621,
from include/asm-generic/bug.h:31,
from ./arch/nios2/include/generated/asm/bug.h:1,
from include/linux/bug.h:5,
from include/linux/random.h:6,
from include/linux/net.h:18,
from drivers/usb/gadget/udc/crg_udc.c:7:
drivers/usb/gadget/udc/crg_udc.c: In function 'crg_udc_queue_trbs':
>> drivers/usb/gadget/udc/crg_udc.c:896:25: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
896 | (void *)sg_addr, buffer_length, num_trbs_needed);
| ^
include/linux/dynamic_debug.h:231:29: note: in definition of macro '__dynamic_func_call_cls'
231 | func(&id, ##__VA_ARGS__); \
| ^~~~~~~~~~~
include/linux/dynamic_debug.h:261:9: note: in expansion of macro '_dynamic_func_call_cls'
261 | _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~
include/linux/dynamic_debug.h:284:9: note: in expansion of macro '_dynamic_func_call'
284 | _dynamic_func_call(fmt, __dynamic_dev_dbg, \
| ^~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:165:9: note: in expansion of macro 'dynamic_dev_dbg'
165 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~
drivers/usb/gadget/udc/crg_udc.c:894:17: note: in expansion of macro 'dev_dbg'
894 | dev_dbg(crg_udc->dev,
| ^~~~~~~
drivers/usb/gadget/udc/crg_udc.c:881:13: warning: variable 'num_sgs' set but not used [-Wunused-but-set-variable]
881 | u32 num_sgs = 0;
| ^~~~~~~
drivers/usb/gadget/udc/crg_udc.c: In function 'crg_udc_ep_enable':
drivers/usb/gadget/udc/crg_udc.c:1812:26: warning: variable 'uccr' set but not used [-Wunused-but-set-variable]
1812 | struct crg_uccr *uccr;
| ^~~~
drivers/usb/gadget/udc/crg_udc.c:1811:25: warning: variable 'epcx' set but not used [-Wunused-but-set-variable]
1811 | struct ep_cx_s *epcx;
| ^~~~
In file included from include/linux/printk.h:621,
from include/asm-generic/bug.h:31,
from ./arch/nios2/include/generated/asm/bug.h:1,
from include/linux/bug.h:5,
from include/linux/random.h:6,
from include/linux/net.h:18,
from drivers/usb/gadget/udc/crg_udc.c:7:
drivers/usb/gadget/udc/crg_udc.c: In function 'init_ep0':
drivers/usb/gadget/udc/crg_udc.c:2681:31: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'dma_addr_t' {aka 'unsigned int'} [-Wformat=]
2681 | dev_dbg(crg_udc->dev, "ep0 ring dma addr = 0x%llx\n", udc_ep_ptr->tran_ring_info.dma);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dynamic_debug.h:231:29: note: in definition of macro '__dynamic_func_call_cls'
231 | func(&id, ##__VA_ARGS__); \
| ^~~~~~~~~~~
include/linux/dynamic_debug.h:261:9: note: in expansion of macro '_dynamic_func_call_cls'
261 | _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~
include/linux/dynamic_debug.h:284:9: note: in expansion of macro '_dynamic_func_call'
284 | _dynamic_func_call(fmt, __dynamic_dev_dbg, \
| ^~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:165:9: note: in expansion of macro 'dynamic_dev_dbg'
165 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~
include/linux/dev_printk.h:165:30: note: in expansion of macro 'dev_fmt'
165 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/usb/gadget/udc/crg_udc.c:2681:9: note: in expansion of macro 'dev_dbg'
2681 | dev_dbg(crg_udc->dev, "ep0 ring dma addr = 0x%llx\n", udc_ep_ptr->tran_ring_info.dma);
| ^~~~~~~
drivers/usb/gadget/udc/crg_udc.c:2681:57: note: format string is defined here
2681 | dev_dbg(crg_udc->dev, "ep0 ring dma addr = 0x%llx\n", udc_ep_ptr->tran_ring_info.dma);
| ~~~^
| |
| long long unsigned int
| %x
In file included from include/linux/printk.h:621,
from include/asm-generic/bug.h:31,
from ./arch/nios2/include/generated/asm/bug.h:1,
from include/linux/bug.h:5,
from include/linux/random.h:6,
from include/linux/net.h:18,
from drivers/usb/gadget/udc/crg_udc.c:7:
drivers/usb/gadget/udc/crg_udc.c: In function 'getstatusrequest':
drivers/usb/gadget/udc/crg_udc.c:3131:31: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'dma_addr_t' {aka 'unsigned int'} [-Wformat=]
3131 | dev_dbg(crg_udc->dev, "udc_req_ptr->usb_req.dma = 0x%llx\n",
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/dynamic_debug.h:231:29: note: in definition of macro '__dynamic_func_call_cls'
231 | func(&id, ##__VA_ARGS__); \
| ^~~~~~~~~~~
include/linux/dynamic_debug.h:261:9: note: in expansion of macro '_dynamic_func_call_cls'
261 | _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~
include/linux/dynamic_debug.h:284:9: note: in expansion of macro '_dynamic_func_call'
284 | _dynamic_func_call(fmt, __dynamic_dev_dbg, \
| ^~~~~~~~~~~~~~~~~~
include/linux/dev_printk.h:165:9: note: in expansion of macro 'dynamic_dev_dbg'
165 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~
include/linux/dev_printk.h:165:30: note: in expansion of macro 'dev_fmt'
165 | dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
drivers/usb/gadget/udc/crg_udc.c:3131:9: note: in expansion of macro 'dev_dbg'
3131 | dev_dbg(crg_udc->dev, "udc_req_ptr->usb_req.dma = 0x%llx\n",
| ^~~~~~~
drivers/usb/gadget/udc/crg_udc.c:3131:64: note: format string is defined here
3131 | dev_dbg(crg_udc->dev, "udc_req_ptr->usb_req.dma = 0x%llx\n",
| ~~~^
| |
| long long unsigned int
| %x
drivers/usb/gadget/udc/crg_udc.c: In function 'crg_udc_common_irq':
drivers/usb/gadget/udc/crg_udc.c:4250:13: warning: variable 'retval' set but not used [-Wunused-but-set-variable]
4250 | int retval = 0;
vim +624 drivers/usb/gadget/udc/crg_udc.c
609
610 static void crg_udc_epcx_setup(struct crg_udc_ep *udc_ep)
611 {
612 struct crg_gadget_dev *crg_udc = udc_ep->crg_udc;
613 const struct usb_endpoint_descriptor *desc = udc_ep->desc;
614 const struct usb_ss_ep_comp_descriptor *comp_desc = udc_ep->comp_desc;
615 u8 DCI = udc_ep->DCI;
616 struct ep_cx_s *epcx = (struct ep_cx_s *)(crg_udc->p_epcx + DCI - 2);
617 enum EP_TYPE_E ep_type;
618 u16 maxburst = 0;
619 u8 maxstreams = 0;
620 u16 maxsize;
621 u32 dw;
622
623 dev_dbg(crg_udc->dev, "crgudc->p_epcx %p, epcx %p\n", crg_udc->p_epcx, epcx);
> 624 dev_dbg(crg_udc->dev, "DCI %d, sizeof ep_cx %ld\n", DCI, sizeof(struct ep_cx_s));
625 dev_dbg(crg_udc->dev, "desc epaddr = 0x%x\n", desc->bEndpointAddress);
626
627 /*corigine gadget dir should be opposite to host dir*/
628 if (usb_endpoint_dir_out(desc))
629 ep_type = usb_endpoint_type(desc) + EP_TYPE_INVALID2;
630 else
631 ep_type = usb_endpoint_type(desc);
632
633 maxsize = usb_endpoint_maxp(desc) & 0x07ff; /* D[0:10] */
634
635 if (crg_udc->gadget.speed >= USB_SPEED_SUPER) {
636 maxburst = comp_desc->bMaxBurst;
637
638 if (usb_endpoint_xfer_bulk(udc_ep->desc))
639 maxstreams = comp_desc->bmAttributes & 0x1f;
640
641 } else if ((crg_udc->gadget.speed == USB_SPEED_HIGH ||
642 crg_udc->gadget.speed == USB_SPEED_FULL) &&
643 (usb_endpoint_xfer_int(udc_ep->desc) ||
644 usb_endpoint_xfer_isoc(udc_ep->desc))) {
645 if (crg_udc->gadget.speed == USB_SPEED_HIGH)
646 maxburst = (usb_endpoint_maxp(desc) >> 11) & 0x3;
647 if (maxburst == 0x3) {
648 dev_err(crg_udc->dev, "invalid maxburst\n");
649 maxburst = 0x2;
650 }
651 }
652
653 /* fill ep_dw0 */
654 dw = 0;
655 dw = SETF_VAR(EP_CX_LOGICAL_EP_NUM, dw, udc_ep->DCI / 2);
656 dw = SETF_VAR(EP_CX_INTERVAL, dw, desc->bInterval);
657 if (maxstreams) {
658 dev_err(crg_udc->dev, "%s maxstream=%d is not expected\n",
659 __func__, maxstreams);
660 }
661 epcx->dw0 = cpu_to_le32(dw);
662
663 /* fill ep_dw1 */
664 dw = 0;
665 dw = SETF_VAR(EP_CX_EP_TYPE, dw, ep_type);
666 dw = SETF_VAR(EP_CX_MAX_PACKET_SIZE, dw, maxsize);
667 dw = SETF_VAR(EP_CX_MAX_BURST_SIZE, dw, maxburst);
668 epcx->dw1 = cpu_to_le32(dw);
669
670 /* fill ep_dw2 */
671 dw = lower_32_bits(udc_ep->tran_ring_info.dma) & EP_CX_TR_DQPT_LO_MASK;
672 dw = SETF_VAR(EP_CX_DEQ_CYC_STATE, dw, udc_ep->pcs);
673 epcx->dw2 = cpu_to_le32(dw);
674
675 /* fill ep_dw3 */
676 dw = upper_32_bits(udc_ep->tran_ring_info.dma);
677 epcx->dw3 = cpu_to_le32(dw);
678 /* Ensure that epcx is updated */
679 wmb();
680 }
681
682 static void crg_udc_epcx_update_dqptr(struct crg_udc_ep *udc_ep)
683 {
684 struct crg_gadget_dev *crg_udc = udc_ep->crg_udc;
685 u8 DCI = udc_ep->DCI;
686 struct ep_cx_s *epcx = (struct ep_cx_s *)(crg_udc->p_epcx + DCI - 2);
687 u32 dw;
688 dma_addr_t dqptaddr;
689 u32 cmd_param0;
690
691 if (DCI == 0) {
692 dev_err(crg_udc->dev, "%s Cannot update dqptr for ep0\n", __func__);
693 return;
694 }
695
696 dqptaddr = tran_trb_virt_to_dma(udc_ep, udc_ep->deq_pt);
697
698 /* fill ep_dw2 */
699 dw = lower_32_bits(dqptaddr) & EP_CX_TR_DQPT_LO_MASK;
700 dw = SETF_VAR(EP_CX_DEQ_CYC_STATE, dw, udc_ep->pcs);
701 epcx->dw2 = cpu_to_le32(dw);
702
703 /* fill ep_dw3 */
704 dw = upper_32_bits(dqptaddr);
705 epcx->dw3 = cpu_to_le32(dw);
706
707 cmd_param0 = (0x1 << udc_ep->DCI);
708 /* Ensure that dqptr is updated */
709 wmb();
710
711 crg_issue_command(crg_udc, CRG_CMD_SET_TR_DQPTR, cmd_param0, 0);
712 }
713
714 static void setup_status_trb(struct crg_gadget_dev *crg_udc,
715 struct transfer_trb_s *p_trb,
716 struct usb_request *usb_req, u8 pcs, u8 set_addr, u8 stall)
717 {
718 u32 tmp, dir = 0;
719
720 /* There are some cases where seutp_status_trb() is called with
721 * usb_req set to NULL.
722 */
723
724 p_trb->dw0 = 0;
725 p_trb->dw1 = 0;
726
727 dev_dbg(crg_udc->dev, "data_buf_ptr_lo = 0x%x, data_buf_ptr_hi = 0x%x\n",
728 p_trb->dw0, p_trb->dw1);
729
730 tmp = 0;
731 tmp = SETF_VAR(TRB_INTR_TARGET, tmp, 0);
732 p_trb->dw2 = tmp;
733
734 tmp = 0;
735 tmp = SETF_VAR(TRB_CYCLE_BIT, tmp, pcs);
736 tmp = SETF_VAR(TRB_INTR_ON_COMPLETION, tmp, 1);/*IOC:1*/
737 tmp = SETF_VAR(TRB_TYPE, tmp, TRB_TYPE_XFER_STATUS_STAGE);
738
739 dir = (crg_udc->setup_status == STATUS_STAGE_XFER) ? 0 : 1;
740 tmp = SETF_VAR(DATA_STAGE_TRB_DIR, tmp, dir);
741
742 tmp = SETF_VAR(TRB_SETUP_TAG, tmp, crg_udc->setup_tag);
743 tmp = SETF_VAR(STATUS_STAGE_TRB_STALL, tmp, stall);
744 tmp = SETF_VAR(STATUS_STAGE_TRB_SET_ADDR, tmp, set_addr);
745
746 p_trb->dw3 = tmp;
747 dev_dbg(crg_udc->dev, "trb_dword2 = 0x%x, trb_dword3 = 0x%x\n",
748 p_trb->dw2, p_trb->dw3);
749 /* Ensure that status trb is updated */
750 wmb();
751 }
752
753 static void knock_doorbell(struct crg_gadget_dev *crg_udc, int DCI)
754 {
755 u32 tmp;
756 struct crg_uccr *uccr;
757
758 uccr = crg_udc->uccr;
759 /* Ensure evreything is written before notifying the HW */
760 wmb();
761
762 tmp = CRG_U3DC_DB_TARGET(DCI);
763 dev_dbg(crg_udc->dev, "DOORBELL = 0x%x\n", tmp);
764 writel(tmp, &uccr->doorbell);
765 }
766
767 static void setup_datastage_trb(struct crg_gadget_dev *crg_udc,
768 struct transfer_trb_s *p_trb, struct usb_request *usb_req,
769 u8 pcs, u32 num_trb, u32 transfer_length, u32 td_size,
770 u8 IOC, u8 AZP, u8 dir, u8 setup_tag)
771 {
772 u32 tmp;
773
> 774 dev_dbg(crg_udc->dev, "dma = 0x%llx, ", usb_req->dma);
775 dev_dbg(crg_udc->dev, "buf = 0x%lx, ", (unsigned long)usb_req->buf);
776
777 p_trb->dw0 = lower_32_bits(usb_req->dma);
778 p_trb->dw1 = upper_32_bits(usb_req->dma);
779
780 dev_dbg(crg_udc->dev, "data_buf_ptr_lo = 0x%x, data_buf_ptr_hi = 0x%x\n",
781 p_trb->dw0, p_trb->dw1);
782
783
784 /* TRB_Transfer_Length
785 *For USB_DIR_OUT, this field is the number of data bytes expected from
786 *xhc. For USB_DIR_IN, this field is the number of data bytes the device
787 *will send.
788 */
789 tmp = 0;
790 tmp = SETF_VAR(TRB_TRANSFER_LEN, tmp, transfer_length);
791 tmp = SETF_VAR(TRB_TD_SIZE, tmp, td_size);
792 tmp = SETF_VAR(TRB_INTR_TARGET, tmp, 0);
793 p_trb->dw2 = tmp;
794
795 tmp = 0;
796 tmp = SETF_VAR(TRB_CYCLE_BIT, tmp, pcs);
797 tmp = SETF_VAR(TRB_INTR_ON_SHORT_PKT, tmp, 1);
798 tmp = SETF_VAR(TRB_INTR_ON_COMPLETION, tmp, IOC);
799 tmp = SETF_VAR(TRB_TYPE, tmp, TRB_TYPE_XFER_DATA_STAGE);
800 tmp = SETF_VAR(TRB_APPEND_ZLP, tmp, AZP);
801 tmp = SETF_VAR(DATA_STAGE_TRB_DIR, tmp, dir);
802 tmp = SETF_VAR(TRB_SETUP_TAG, tmp, setup_tag);
803
804 p_trb->dw3 = tmp;
805 /* Ensure that datastage trb is updated */
806 wmb();
807
808 dev_dbg(crg_udc->dev, "trb_dword0 = 0x%x, trb_dword1 = 0x%x trb_dword2 = 0x%x, trb_dword3 = 0x%x\n",
809 p_trb->dw0, p_trb->dw1, p_trb->dw2, p_trb->dw3);
810 }
811
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Vladimir,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 63804fed149a6750ffd28610c5c1c98cce6bd377]
url: https://github.com/intel-lab-lkp/linux/commits/Vladimir-Moravcevic/dt-bindings-usb-axiado-ax3000-udc-Add-Axiado-UDC/20260202-211951
base: 63804fed149a6750ffd28610c5c1c98cce6bd377
patch link: https://lore.kernel.org/r/20260202-axiado-ax3000-usb-device-controller-v1-2-45ce0a8b014f%40axiado.com
patch subject: [PATCH 2/3] usb: gadget: udc: Add UDC driver for Axiado Device controller IP Corigine
config: alpha-allyesconfig (https://download.01.org/0day-ci/archive/20260203/202602030131.VCTzZ4me-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260203/202602030131.VCTzZ4me-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602030131.VCTzZ4me-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/usb/gadget/udc/crg_udc.c: In function 'crg_udc_queue_trbs':
>> drivers/usb/gadget/udc/crg_udc.c:881:13: warning: variable 'num_sgs' set but not used [-Wunused-but-set-variable]
881 | u32 num_sgs = 0;
| ^~~~~~~
drivers/usb/gadget/udc/crg_udc.c: In function 'crg_udc_ep_enable':
>> drivers/usb/gadget/udc/crg_udc.c:1812:26: warning: variable 'uccr' set but not used [-Wunused-but-set-variable]
1812 | struct crg_uccr *uccr;
| ^~~~
>> drivers/usb/gadget/udc/crg_udc.c:1811:25: warning: variable 'epcx' set but not used [-Wunused-but-set-variable]
1811 | struct ep_cx_s *epcx;
| ^~~~
drivers/usb/gadget/udc/crg_udc.c: In function 'crg_udc_common_irq':
>> drivers/usb/gadget/udc/crg_udc.c:4250:13: warning: variable 'retval' set but not used [-Wunused-but-set-variable]
4250 | int retval = 0;
| ^~~~~~
drivers/usb/gadget/udc/crg_udc.c: At top level:
>> drivers/usb/gadget/udc/crg_udc.c:126:19: warning: 'driver_name' defined but not used [-Wunused-const-variable=]
126 | static const char driver_name[] = "crg_udc";
| ^~~~~~~~~~~
--
>> Warning: drivers/usb/gadget/udc/crg_udc.c:4325 cannot understand function prototype: 'const struct of_device_id of_crg_udc_match[] ='
vim +/num_sgs +881 drivers/usb/gadget/udc/crg_udc.c
858
859 static int crg_udc_queue_trbs(struct crg_udc_ep *udc_ep_ptr,
860 struct crg_udc_request *udc_req_ptr, bool b_isoc,
861 u32 xfer_ring_size,
862 u32 num_trbs_needed, u64 buffer_length)
863 {
864 struct crg_gadget_dev *crg_udc = udc_ep_ptr->crg_udc;
865 struct transfer_trb_s *p_xfer_ring = udc_ep_ptr->first_trb;
866 u32 num_trbs_ava = 0;
867 struct usb_request *usb_req = &udc_req_ptr->usb_req;
868 u64 buff_len_temp = 0;
869 u32 i, j = 1;
870 struct transfer_trb_s *enq_pt = udc_ep_ptr->enq_pt;
871 u8 td_size;
872 u8 chain_bit = 1;
873 u8 short_pkt = 0;
874 u8 intr_on_compl = 0;
875 u32 count;
876 bool full_td = true;
877 u32 intr_rate;
878 dma_addr_t trb_buf_addr;
879 bool need_zlp = false;
880 struct scatterlist *sg = NULL;
> 881 u32 num_sgs = 0;
882 u64 sg_addr = 0;
883
884 dev_dbg(crg_udc->dev, "%s %s\n", __func__, udc_ep_ptr->usb_ep.name);
885 if (udc_req_ptr->usb_req.num_sgs) {
886 num_sgs = udc_req_ptr->usb_req.num_sgs;
887 sg = udc_req_ptr->usb_req.sg;
888 sg_addr = (u64) sg_dma_address(sg);
889 buffer_length = sg_dma_len(sg);
890
891 dev_dbg(crg_udc->dev, "num_sgs = %d, num_mapped_sgs = %d\n",
892 udc_req_ptr->usb_req.num_sgs,
893 udc_req_ptr->usb_req.num_mapped_sgs);
894 dev_dbg(crg_udc->dev,
895 "sg_addr = %p, buffer_length = %llu, num_trbs = %d\n",
896 (void *)sg_addr, buffer_length, num_trbs_needed);
897 }
898
899 if (!b_isoc) {
900 if (udc_req_ptr->usb_req.zero == 1 &&
901 udc_req_ptr->usb_req.length != 0 &&
902 ((udc_req_ptr->usb_req.length %
903 udc_ep_ptr->usb_ep.maxpacket) == 0)) {
904 need_zlp = true;
905 }
906 }
907
908 td_size = num_trbs_needed;
909
910 num_trbs_ava = room_on_ring(crg_udc, xfer_ring_size,
911 p_xfer_ring, udc_ep_ptr->enq_pt, udc_ep_ptr->deq_pt);
912
913 /* trb_buf_addr points to the addr of the buffer that we write in
914 * each TRB. If this function is called to complete the pending TRB
915 * transfers of a previous request, point it to the buffer that is
916 * not transferred, or else point it to the starting address of the
917 * buffer received in usb_request
918 */
919 if (udc_req_ptr->trbs_needed) {
920 /* Here udc_req_ptr->trbs_needed is used to indicate if we
921 * are completing a previous req
922 */
923 trb_buf_addr = usb_req->dma +
924 (usb_req->length - udc_req_ptr->buff_len_left);
925 } else {
926 if (sg_addr)
927 trb_buf_addr = sg_addr;
928 else
929 trb_buf_addr = usb_req->dma;
930 }
931
932 if (num_trbs_ava >= num_trbs_needed) {
933 count = num_trbs_needed;
934 } else {
935 if (b_isoc) {
936 struct crg_udc_request *udc_req_ptr_temp;
937 u8 temp = 0;
938
939 list_for_each_entry(udc_req_ptr_temp,
940 &udc_ep_ptr->queue, queue) {
941 temp++;
942 }
943
944 if (temp >= 2) {
945 dev_err(crg_udc->dev, "%s don't do isoc discard\n", __func__);
946 /* we already scheduled two mfi in advance. */
947 return 0;
948 }
949 }
950
951 /* always keep one trb for zlp. */
952 count = num_trbs_ava;
953 full_td = false;
954 dev_dbg(crg_udc->dev, "TRB Ring Full. Avail: 0x%x Req: 0x%x\n",
955 num_trbs_ava, num_trbs_needed);
956 udc_ep_ptr->tran_ring_full = true;
957
958 /*if there is still some trb not queued,
959 *it means last queued
960 *trb is not the last trb of TD, so no need zlp
961 */
962 need_zlp = false;
963 }
964
965 for (i = 0; i < count; i++) {
966 if ((udc_req_ptr->usb_req.num_sgs) && (buffer_length == 0)) {
967 sg = sg_next(sg);
968 if (sg) {
969 trb_buf_addr = (u64) sg_dma_address(sg);
970 buffer_length = sg_dma_len(sg);
971 dev_dbg(crg_udc->dev,
972 "trb_buf_addr = %p, num_trbs = %d\n",
973 (void *)trb_buf_addr, num_trbs_needed);
974 dev_dbg(crg_udc->dev, "buffer_length = %llu\n",
975 buffer_length);
976 } else {
977 dev_err(crg_udc->dev,
978 "scatterlist ended unexpectedly (i=%d, count=%d)\n",
979 i, count);
980 return -EINVAL;
981 }
982 }
983
984 if (buffer_length > TRB_MAX_BUFFER_SIZE)
985 buff_len_temp = TRB_MAX_BUFFER_SIZE;
986 else
987 buff_len_temp = buffer_length;
988
989 buffer_length -= buff_len_temp;
990
991 if (usb_endpoint_dir_out(udc_ep_ptr->desc))
992 short_pkt = 1;
993
994 if ((buffer_length == 0) && (i == (count - 1))) {
995 chain_bit = 0;
996 intr_on_compl = 1;
997 udc_req_ptr->all_trbs_queued = 1;
998 }
999
1000
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2026 Red Hat, Inc.