Modify kretprobe tracepoint symbols for capturing DWC3 ftrace logs into IPC. This is useful for capturing any errors that may happen within the DWC3 core. Change-Id: Ic54aedd0d948263553b87bdb1e01faf4e83d6384 Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
301 lines
7.3 KiB
C
301 lines
7.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
|
|
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include "debug-ipc.h"
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
static unsigned int ep_addr_rxdbg_mask = 1;
|
|
module_param(ep_addr_rxdbg_mask, uint, 0644);
|
|
static unsigned int ep_addr_txdbg_mask = 1;
|
|
module_param(ep_addr_txdbg_mask, uint, 0644);
|
|
|
|
static int allow_dbg_print(u8 ep_num)
|
|
{
|
|
int dir, num;
|
|
|
|
/* allow bus wide events */
|
|
if (ep_num == 0xff)
|
|
return 1;
|
|
|
|
dir = ep_num & 0x1;
|
|
num = ep_num >> 1;
|
|
num = 1 << num;
|
|
|
|
if (dir && (num & ep_addr_txdbg_mask))
|
|
return 1;
|
|
if (!dir && (num & ep_addr_rxdbg_mask))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void dwc3_dbg_trace_log_ctrl(void *log_ctxt, struct usb_ctrlrequest *ctrl)
|
|
{
|
|
char *ctrl_req_str;
|
|
|
|
if (ctrl == NULL)
|
|
return;
|
|
|
|
ctrl_req_str = kzalloc(DWC3_MSG_MAX, GFP_ATOMIC);
|
|
if (!ctrl_req_str)
|
|
return;
|
|
|
|
usb_decode_ctrl(ctrl_req_str, DWC3_MSG_MAX, ctrl->bRequestType,
|
|
ctrl->bRequest, le16_to_cpu(ctrl->wValue),
|
|
le16_to_cpu(ctrl->wIndex),
|
|
le16_to_cpu(ctrl->wLength));
|
|
ipc_log_string(log_ctxt, "dbg_trace_log_ctrl: %s", ctrl_req_str);
|
|
kfree(ctrl_req_str);
|
|
}
|
|
|
|
void dwc3_dbg_trace_log_request(void *log_ctxt, struct dwc3_request *req,
|
|
char *tag)
|
|
{
|
|
struct dwc3_ep *dep;
|
|
|
|
if (req == NULL)
|
|
return;
|
|
|
|
dep = req->dep;
|
|
ipc_log_string(log_ctxt, "%s: %s: req %p length %u/%u %s%s%s ==> %d",
|
|
tag, dep->name, req, req->request.actual,
|
|
req->request.length,
|
|
req->request.zero ? "Z" : "z",
|
|
req->request.short_not_ok ? "S" : "s",
|
|
req->request.no_interrupt ? "i" : "I",
|
|
req->request.status);
|
|
}
|
|
|
|
void dwc3_dbg_trace_ep_cmd(void *log_ctxt, struct dwc3_ep *dep,
|
|
unsigned int cmd,
|
|
struct dwc3_gadget_ep_cmd_params *params,
|
|
int cmd_status)
|
|
{
|
|
ipc_log_string(log_ctxt,
|
|
"dbg_send_ep_cmd: %s: cmd '%s' [%x] params %08x %08x %08x --> status: %s",
|
|
dep->name, dwc3_gadget_ep_cmd_string(cmd), cmd, params->param0,
|
|
params->param1, params->param2, dwc3_ep_cmd_status_string(cmd_status));
|
|
}
|
|
|
|
void dwc3_dbg_trace_trb_complete(void *log_ctxt, struct dwc3_ep *dep,
|
|
struct dwc3_trb *trb, char *tag)
|
|
{
|
|
char *s;
|
|
int pcm = ((trb->size >> 24) & 3) + 1;
|
|
|
|
switch (usb_endpoint_type(dep->endpoint.desc)) {
|
|
case USB_ENDPOINT_XFER_INT:
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
switch (pcm) {
|
|
case 1:
|
|
s = "1x ";
|
|
break;
|
|
case 2:
|
|
s = "2x ";
|
|
break;
|
|
case 3:
|
|
default:
|
|
s = "3x ";
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
s = "";
|
|
}
|
|
|
|
ipc_log_string(log_ctxt,
|
|
"%s: %s: trb %p (E%d:D%d) buf %08x%08x sz %s%d ctrl %08x (%c%c%c%c:%c%c:%s)",
|
|
tag, dep->name, trb, dep->trb_enqueue,
|
|
dep->trb_dequeue, trb->bph, trb->bpl, s, trb->size, trb->ctrl,
|
|
trb->ctrl & DWC3_TRB_CTRL_HWO ? 'H' : 'h',
|
|
trb->ctrl & DWC3_TRB_CTRL_LST ? 'L' : 'l',
|
|
trb->ctrl & DWC3_TRB_CTRL_CHN ? 'C' : 'c',
|
|
trb->ctrl & DWC3_TRB_CTRL_CSP ? 'S' : 's',
|
|
trb->ctrl & DWC3_TRB_CTRL_ISP_IMI ? 'S' : 's',
|
|
trb->ctrl & DWC3_TRB_CTRL_IOC ? 'C' : 'c',
|
|
dwc3_trb_type_string(DWC3_TRBCTL_TYPE(trb->ctrl)));
|
|
}
|
|
|
|
void dwc3_dbg_trace_event(void *log_ctxt, u32 event, struct dwc3 *dwc)
|
|
{
|
|
char *event_str;
|
|
|
|
event_str = kzalloc(DWC3_MSG_MAX, GFP_ATOMIC);
|
|
if (!event_str)
|
|
return;
|
|
|
|
ipc_log_string(log_ctxt, "event (%08x): %s", event,
|
|
dwc3_decode_event(event_str, DWC3_MSG_MAX,
|
|
event, dwc->ep0state));
|
|
kfree(event_str);
|
|
}
|
|
|
|
void dwc3_dbg_trace_ep(void *log_ctxt, struct dwc3_ep *dep)
|
|
{
|
|
|
|
ipc_log_string(log_ctxt,
|
|
"%s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c",
|
|
dep->name, dep->endpoint.maxpacket,
|
|
dep->endpoint.maxpacket_limit, dep->endpoint.max_streams,
|
|
dep->endpoint.maxburst, dep->trb_enqueue,
|
|
dep->trb_dequeue,
|
|
dep->flags & DWC3_EP_ENABLED ? 'E' : 'e',
|
|
dep->flags & DWC3_EP_STALL ? 'S' : 's',
|
|
dep->flags & DWC3_EP_WEDGE ? 'W' : 'w',
|
|
dep->flags & DWC3_EP_TRANSFER_STARTED ? 'B' : 'b',
|
|
dep->flags & DWC3_EP_PENDING_REQUEST ? 'P' : 'p',
|
|
dep->direction ? '<' : '>');
|
|
}
|
|
|
|
/**
|
|
* dwc3_dbg_print: prints the common part of the event
|
|
* @addr: endpoint address
|
|
* @name: event name
|
|
* @status: status
|
|
* @extra: extra information
|
|
* @dwc3: pointer to struct dwc3
|
|
*/
|
|
void dwc3_dbg_print(void *log_ctxt, u8 ep_num, const char *name,
|
|
int status, const char *extra)
|
|
{
|
|
if (!allow_dbg_print(ep_num))
|
|
return;
|
|
|
|
if (name == NULL)
|
|
return;
|
|
|
|
ipc_log_string(log_ctxt, "%02X %-25.25s %4i ?\t%s",
|
|
ep_num, name, status, extra);
|
|
}
|
|
|
|
/**
|
|
* dwc3_dbg_done: prints a DONE event
|
|
* @addr: endpoint address
|
|
* @td: transfer descriptor
|
|
* @status: status
|
|
* @dwc3: pointer to struct dwc3
|
|
*/
|
|
void dwc3_dbg_done(void *log_ctxt, u8 ep_num,
|
|
const u32 count, int status)
|
|
{
|
|
if (!allow_dbg_print(ep_num))
|
|
return;
|
|
|
|
ipc_log_string(log_ctxt, "%02X %-25.25s %4i ?\t%d",
|
|
ep_num, "DONE", status, count);
|
|
}
|
|
|
|
/**
|
|
* dwc3_dbg_event: prints a generic event
|
|
* @addr: endpoint address
|
|
* @name: event name
|
|
* @status: status
|
|
*/
|
|
void dwc3_dbg_event(void *log_ctxt, u8 ep_num, const char *name, int status)
|
|
{
|
|
if (!allow_dbg_print(ep_num))
|
|
return;
|
|
|
|
if (name != NULL)
|
|
dwc3_dbg_print(log_ctxt, ep_num, name, status, "");
|
|
}
|
|
|
|
/*
|
|
* dwc3_dbg_queue: prints a QUEUE event
|
|
* @addr: endpoint address
|
|
* @req: USB request
|
|
* @status: status
|
|
*/
|
|
void dwc3_dbg_queue(void *log_ctxt, u8 ep_num,
|
|
const struct usb_request *req, int status)
|
|
{
|
|
if (!allow_dbg_print(ep_num))
|
|
return;
|
|
|
|
if (req != NULL) {
|
|
ipc_log_string(log_ctxt,
|
|
"%02X %-25.25s %4i ?\t%d %d", ep_num, "QUEUE", status,
|
|
!req->no_interrupt, req->length);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* dwc3_dbg_setup: prints a SETUP event
|
|
* @addr: endpoint address
|
|
* @req: setup request
|
|
*/
|
|
void dwc3_dbg_setup(void *log_ctxt, u8 ep_num,
|
|
const struct usb_ctrlrequest *req)
|
|
{
|
|
if (!allow_dbg_print(ep_num))
|
|
return;
|
|
|
|
if (req != NULL) {
|
|
ipc_log_string(log_ctxt,
|
|
"%02X %-25.25s ?\t%02X %02X %04X %04X %d",
|
|
ep_num, "SETUP", req->bRequestType,
|
|
req->bRequest, le16_to_cpu(req->wValue),
|
|
le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* dwc3_dbg_print_reg: prints a reg value
|
|
* @name: reg name
|
|
* @reg: reg value to be printed
|
|
*/
|
|
void dwc3_dbg_print_reg(void *log_ctxt, const char *name, int reg)
|
|
{
|
|
if (name == NULL)
|
|
return;
|
|
|
|
ipc_log_string(log_ctxt, "%s = 0x%08x", name, reg);
|
|
}
|
|
|
|
void dwc3_dbg_dma_unmap(void *log_ctxt, u8 ep_num, struct dwc3_request *req)
|
|
{
|
|
if (ep_num < 2)
|
|
return;
|
|
ipc_log_string(log_ctxt,
|
|
"%02X-%-3.3s %-25.25s 0x%pK %pad %u %pad %s", ep_num >> 1,
|
|
ep_num & 1 ? "IN":"OUT", "UNMAP", &req->request,
|
|
&req->request.dma, req->request.length, &req->trb_dma,
|
|
req->trb->ctrl & DWC3_TRB_CTRL_HWO ? "HWO" : "");
|
|
}
|
|
|
|
void dwc3_dbg_dma_map(void *log_ctxt, u8 ep_num, struct dwc3_request *req)
|
|
{
|
|
if (ep_num < 2)
|
|
return;
|
|
|
|
ipc_log_string(log_ctxt,
|
|
"%02X-%-3.3s %-25.25s 0x%pK %pad %u %pad", ep_num >> 1,
|
|
ep_num & 1 ? "IN":"OUT", "MAP", &req->request,
|
|
&req->request.dma, req->request.length, &req->trb_dma);
|
|
}
|
|
|
|
void dwc3_dbg_dma_dequeue(void *log_ctxt, u8 ep_num, struct dwc3_request *req)
|
|
{
|
|
if (ep_num < 2)
|
|
return;
|
|
|
|
ipc_log_string(log_ctxt,
|
|
"%02X-%-3.3s %-25.25s 0x%pK %pad %pad", ep_num >> 1,
|
|
ep_num & 1 ? "IN":"OUT", "DEQUEUE", &req->request,
|
|
&req->request.dma, &req->trb_dma);
|
|
}
|
|
|
|
void dwc3_dbg_dma_queue(void *log_ctxt, u8 ep_num, struct dwc3_request *req)
|
|
{
|
|
if (ep_num < 2)
|
|
return;
|
|
|
|
ipc_log_string(log_ctxt,
|
|
"%02X-%-3.3s %-25.25s 0x%pK", ep_num >> 1,
|
|
ep_num & 1 ? "IN":"OUT", "QUEUE", &req->request);
|
|
}
|