Skip to content

Commit

Permalink
usb: cdns2: Add tracepoints for CDNS2 driver
Browse files Browse the repository at this point in the history
Patch adds the series of tracepoints that can be used for
debugging issues detected in driver.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
  • Loading branch information
pawellcdns authored and intel-lab-lkp committed Feb 16, 2023
1 parent c245020 commit edb55f1
Show file tree
Hide file tree
Showing 6 changed files with 897 additions and 3 deletions.
2 changes: 2 additions & 0 deletions drivers/usb/gadget/udc/cdns2/Makefile
@@ -1,5 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
# define_trace.h needs to know how to find our header
CFLAGS_cdns2-trace.o := -I$(src)

obj-$(CONFIG_USB_CDNS2_UDC) += cdns2-udc-pci.o
cdns2-udc-pci-$(CONFIG_USB_CDNS2_UDC) += cdns2-pci.o cdns2-gadget.o cdns2-ep0.o
cdns2-udc-pci-$(CONFIG_TRACING) += cdns2-trace.o
200 changes: 200 additions & 0 deletions drivers/usb/gadget/udc/cdns2/cdns2-debug.h
@@ -0,0 +1,200 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence USBHS-DEV Driver.
* Debug header file.
*
* Copyright (C) 2023 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/

#ifndef __LINUX_CDNS2_DEBUG
#define __LINUX_CDNS2_DEBUG

static inline const char *cdns2_decode_usb_irq(char *str, size_t size,
u8 usb_irq, u8 ext_irq)
{
int ret;

ret = snprintf(str, size, "usbirq: 0x%02x - ", usb_irq);

if (usb_irq & USBIRQ_SOF)
ret += snprintf(str + ret, size - ret, "SOF ");
if (usb_irq & USBIRQ_SUTOK)
ret += snprintf(str + ret, size - ret, "SUTOK ");
if (usb_irq & USBIRQ_SUDAV)
ret += snprintf(str + ret, size - ret, "SETUP ");
if (usb_irq & USBIRQ_SUSPEND)
ret += snprintf(str + ret, size - ret, "Suspend ");
if (usb_irq & USBIRQ_URESET)
ret += snprintf(str + ret, size - ret, "Reset ");
if (usb_irq & USBIRQ_HSPEED)
ret += snprintf(str + ret, size - ret, "HS ");
if (usb_irq & USBIRQ_LPM)
ret += snprintf(str + ret, size - ret, "LPM ");

ret += snprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq);

if (ext_irq & EXTIRQ_WAKEUP)
ret += snprintf(str + ret, size - ret, "Wakupe ");
if (ext_irq & EXTIRQ_VBUSFAULT_FALL)
ret += snprintf(str + ret, size - ret, "VBUS_FALL ");
if (ext_irq & EXTIRQ_VBUSFAULT_RISE)
ret += snprintf(str + ret, size - ret, "VBUS_RISE ");

if (ret >= size)
pr_info("CDNS2: buffer overflowed.\n");

return str;
}

static inline const char *cdns2_decode_dma_irq(char *str, size_t size,
u32 ep_ists, u32 ep_sts,
const char *ep_name)
{
int ret;

ret = snprintf(str, size, "ISTS: %08x, %s: %08x ",
ep_ists, ep_name, ep_sts);

if (ep_sts & DMA_EP_STS_IOC)
ret += snprintf(str + ret, size - ret, "IOC ");
if (ep_sts & DMA_EP_STS_ISP)
ret += snprintf(str + ret, size - ret, "ISP ");
if (ep_sts & DMA_EP_STS_DESCMIS)
ret += snprintf(str + ret, size - ret, "DESCMIS ");
if (ep_sts & DMA_EP_STS_TRBERR)
ret += snprintf(str + ret, size - ret, "TRBERR ");
if (ep_sts & DMA_EP_STS_OUTSMM)
ret += snprintf(str + ret, size - ret, "OUTSMM ");
if (ep_sts & DMA_EP_STS_ISOERR)
ret += snprintf(str + ret, size - ret, "ISOERR ");
if (ep_sts & DMA_EP_STS_DBUSY)
ret += snprintf(str + ret, size - ret, "DBUSY ");
if (DMA_EP_STS_CCS(ep_sts))
ret += snprintf(str + ret, size - ret, "CCS ");

if (ret >= size)
pr_info("CDNS2: buffer overflowed.\n");

return str;
}

static inline const char *cdns2_decode_epx_irq(char *str, size_t size,
char *ep_name, u32 ep_ists,
u32 ep_sts)
{
return cdns2_decode_dma_irq(str, size, ep_ists, ep_sts, ep_name);
}

static inline const char *cdns2_decode_ep0_irq(char *str, size_t size,
u32 ep_ists, u32 ep_sts,
int dir)
{
return cdns2_decode_dma_irq(str, size, ep_ists, ep_sts,
dir ? "ep0IN" : "ep0OUT");
}

static inline const char *cdns2_raw_ring(struct cdns2_endpoint *pep,
struct cdns2_trb *trbs,
char *str, size_t size)
{
struct cdns2_ring *ring = &pep->ring;
struct cdns2_trb *trb;
dma_addr_t dma;
int ret;
int i;

ret = snprintf(str, size, "\n\t\tTR for %s:", pep->name);

trb = &trbs[ring->dequeue];
dma = cdns2_trb_virt_to_dma(pep, trb);
ret += snprintf(str + ret, size - ret,
"\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n",
ring->dequeue, trb, &dma);

trb = &trbs[ring->enqueue];
dma = cdns2_trb_virt_to_dma(pep, trb);
ret += snprintf(str + ret, size - ret,
"\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n",
ring->enqueue, trb, &dma);

ret += snprintf(str + ret, size - ret,
"\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
ring->free_trbs, ring->ccs, ring->pcs);

if (TRBS_PER_SEGMENT > 40) {
ret += snprintf(str + ret, size - ret,
"\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT);
return str;
}

dma = ring->dma;
for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
trb = &trbs[i];
ret += snprintf(str + ret, size - ret,
"\t\t@%pad %08x %08x %08x\n", &dma,
le32_to_cpu(trb->buffer),
le32_to_cpu(trb->length),
le32_to_cpu(trb->control));
dma += sizeof(*trb);
}

if (ret >= size)
pr_info("CDNS2: buffer overflowed.\n");

return str;
}

static inline const char *cdns2_trb_type_string(u8 type)
{
switch (type) {
case TRB_NORMAL:
return "Normal";
case TRB_LINK:
return "Link";
default:
return "UNKNOWN";
}
}

static inline const char *cdns2_decode_trb(char *str, size_t size, u32 flags,
u32 length, u32 buffer)
{
int type = TRB_FIELD_TO_TYPE(flags);
int ret;

switch (type) {
case TRB_LINK:
ret = snprintf(str, size,
"LINK %08x type '%s' flags %c:%c:%c%c:%c",
buffer, cdns2_trb_type_string(type),
flags & TRB_CYCLE ? 'C' : 'c',
flags & TRB_TOGGLE ? 'T' : 't',
flags & TRB_CHAIN ? 'C' : 'c',
flags & TRB_CHAIN ? 'H' : 'h',
flags & TRB_IOC ? 'I' : 'i');
break;
case TRB_NORMAL:
ret = snprintf(str, size,
"type: '%s', Buffer: %08x, length: %ld, burst len: %ld, "
"flags %c:%c:%c%c:%c",
cdns2_trb_type_string(type),
buffer, TRB_LEN(length),
TRB_FIELD_TO_BURST(length),
flags & TRB_CYCLE ? 'C' : 'c',
flags & TRB_ISP ? 'I' : 'i',
flags & TRB_CHAIN ? 'C' : 'c',
flags & TRB_CHAIN ? 'H' : 'h',
flags & TRB_IOC ? 'I' : 'i');
break;
default:
ret = snprintf(str, size, "type '%s' -> raw %08x %08x %08x",
cdns2_trb_type_string(type),
buffer, length, flags);
}

return str;
}

#endif /*__LINUX_CDNS2_DEBUG*/
26 changes: 24 additions & 2 deletions drivers/usb/gadget/udc/cdns2/cdns2-ep0.c
Expand Up @@ -10,6 +10,7 @@
#include <linux/usb/composite.h>

#include "cdns2-gadget.h"
#include "cdns2-trace.h"

static struct usb_endpoint_descriptor cdns2_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
Expand Down Expand Up @@ -59,6 +60,8 @@ static void cdns2_ep0_enqueue(struct cdns2_device *pdev, dma_addr_t dma_addr,
ring->trbs[1].control = 0;
}

trace_cdns2_queue_trb(pep, ring->trbs);

if (!pep->dir)
writel(0, &pdev->ep0_regs->rxbc);

Expand All @@ -67,6 +70,8 @@ static void cdns2_ep0_enqueue(struct cdns2_device *pdev, dma_addr_t dma_addr,
writel(DMA_EP_STS_TRBERR, &regs->ep_sts);
writel(pep->ring.dma, &regs->ep_traddr);

trace_cdns2_doorbell_ep0(pep, readl(&regs->ep_traddr));

writel(DMA_EP_CMD_DRDY, &regs->ep_cmd);
}

Expand Down Expand Up @@ -129,6 +134,8 @@ static int cdns2_req_ep0_set_configuration(struct cdns2_device *pdev,
if (ret)
return ret;

trace_cdns2_device_state(config ? "configured" : "addressed");

if (!config)
usb_gadget_set_state(&pdev->gadget, USB_STATE_ADDRESS);

Expand Down Expand Up @@ -159,6 +166,8 @@ static int cdns2_req_ep0_set_address(struct cdns2_device *pdev, u32 addr)
usb_gadget_set_state(&pdev->gadget,
(addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));

trace_cdns2_device_state(addr ? "addressed" : "default");

return 0;
}

Expand Down Expand Up @@ -388,8 +397,12 @@ void cdns2_handle_setup_packet(struct cdns2_device *pdev)
* If SETUP packet was modified while reading just simple ignore it.
* The new one will be handled latter.
*/
if (cdns2_check_new_setup(pdev))
if (cdns2_check_new_setup(pdev)) {
trace_cdns2_ep0_setup("overridden");
return;
}

trace_cdns2_ctrl_req(ctrl);

if (!pdev->gadget_driver)
goto out;
Expand Down Expand Up @@ -434,8 +447,10 @@ void cdns2_handle_setup_packet(struct cdns2_device *pdev)
else
ret = cdns2_ep0_delegate_req(pdev);

if (ret == USB_GADGET_DELAYED_STATUS)
if (ret == USB_GADGET_DELAYED_STATUS) {
trace_cdns2_ep0_status_stage("delayed");
return;
}

out:
if (ret < 0)
Expand All @@ -451,6 +466,7 @@ static void cdns2_transfer_completed(struct cdns2_device *pdev)
if (!list_empty(&pep->pending_list)) {
struct cdns2_request *preq;

trace_cdns2_complete_trb(pep, pep->ring.trbs);
preq = cdns2_next_preq(&pep->pending_list);

preq->request.actual =
Expand All @@ -467,6 +483,8 @@ void cdns2_handle_ep0_interrupt(struct cdns2_device *pdev, int dir)

cdns2_select_ep(pdev, dir);

trace_cdns2_ep0_irq(pdev);

ep_sts_reg = readl(&pdev->adma_regs->ep_sts);
writel(ep_sts_reg, &pdev->adma_regs->ep_sts);

Expand Down Expand Up @@ -533,8 +551,11 @@ static int cdns2_gadget_ep0_queue(struct usb_ep *ep,

preq = to_cdns2_request(request);

trace_cdns2_request_enqueue(preq);

/* Cancel the request if controller receive new SETUP packet. */
if (cdns2_check_new_setup(pdev)) {
trace_cdns2_ep0_setup("overridden");
spin_unlock_irqrestore(&pdev->lock, flags);
return -ECONNRESET;
}
Expand All @@ -559,6 +580,7 @@ static int cdns2_gadget_ep0_queue(struct usb_ep *ep,
}

if (!list_empty(&pep->pending_list)) {
trace_cdns2_ep0_setup("pending");
dev_err(pdev->dev,
"can't handle multiple requests for ep0\n");
spin_unlock_irqrestore(&pdev->lock, flags);
Expand Down

0 comments on commit edb55f1

Please sign in to comment.