// 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 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); }