mirror of
https://github.com/torvalds/linux.git
synced 2026-08-01 20:22:08 +02:00
usb: dwc3: Add snapshot of DWC3 drivers
Snapshot of the downstream DWC3 drivers based on msm-5.15.
commit 4c957648c2ae ("Merge "usb: dwc3-msm-core: Remove
redundant dwc3_msm_set_clk_sel calls"").
Change-Id: Ifad5bc07dec41ba1edd32322940e171f198f2508
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
This commit is contained in:
parent
a5f8942a16
commit
3e7e062372
|
|
@ -159,4 +159,13 @@ config USB_DWC3_XILINX
|
|||
This driver handles both ZynqMP and Versal SoC operations.
|
||||
Say 'Y' or 'M' if you have one such device.
|
||||
|
||||
config USB_DWC3_MSM
|
||||
tristate "QTI MSM Platforms"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
help
|
||||
Applicable to QTI MSM Platforms with DesignWare Core
|
||||
USB3 IP.
|
||||
Driver supports host, device and dual-role modes of operation.
|
||||
Say 'Y' or 'M' if you have one such device.
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
# define_trace.h needs to know how to find our header
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
CFLAGS_dwc3-msm-core.o := -I$(srctree)/drivers/usb/host
|
||||
|
||||
obj-$(CONFIG_USB_DWC3) += dwc3.o
|
||||
|
||||
|
|
@ -53,3 +54,8 @@ obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
|
|||
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
|
||||
obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o
|
||||
obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
|
||||
obj-$(CONFIG_USB_DWC3_MSM) += dwc3-msm.o
|
||||
dwc3-msm-y := dwc3-msm-core.o debug_ipc.o
|
||||
ifneq ($(CONFIG_ARM64),)
|
||||
dwc3-msm-y += dwc3-msm-ops.o
|
||||
endif
|
||||
|
|
|
|||
107
drivers/usb/dwc3/debug-ipc.h
Normal file
107
drivers/usb/dwc3/debug-ipc.h
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DWC3_DEBUG_IPC_H
|
||||
#define __DWC3_DEBUG_IPC_H
|
||||
|
||||
#include "core.h"
|
||||
#include "debug.h"
|
||||
#include <linux/ipc_logging.h>
|
||||
|
||||
/*
|
||||
* NOTE: Make sure to have mdwc as local variable in function before using
|
||||
* below macros.
|
||||
*/
|
||||
#define dbg_event(ep_num, name, status) \
|
||||
dwc3_dbg_print(mdwc->dwc_ipc_log_ctxt, ep_num, name, status, "")
|
||||
|
||||
#define dbg_print(ep_num, name, status, extra) \
|
||||
dwc3_dbg_print(mdwc->dwc_ipc_log_ctxt, ep_num, name, status, extra)
|
||||
|
||||
#define dbg_print_reg(name, reg) \
|
||||
dwc3_dbg_print_reg(mdwc->dwc_ipc_log_ctxt, name, reg)
|
||||
|
||||
#define dbg_done(ep_num, count, status) \
|
||||
dwc3_dbg_done(mdwc->dwc_ipc_log_ctxt, ep_num, count, status)
|
||||
|
||||
#define dbg_queue(ep_num, req, status) \
|
||||
dwc3_dbg_queue(mdwc->dwc_ipc_log_ctxt, ep_num, req, status)
|
||||
|
||||
#define dbg_setup(ep_num, req) \
|
||||
dwc3_dbg_setup(mdwc->dwc_ipc_log_ctxt, ep_num, req)
|
||||
|
||||
#define dbg_ep_queue(ep_num, req) \
|
||||
dwc3_dbg_dma_queue(mdwc->dwc_ipc_log_ctxt, ep_num, req)
|
||||
|
||||
#define dbg_ep_dequeue(ep_num, req) \
|
||||
dwc3_dbg_dma_dequeue(mdwc->dwc_ipc_log_ctxt, ep_num, req)
|
||||
|
||||
#define dbg_ep_unmap(ep_num, req) \
|
||||
dwc3_dbg_dma_unmap(mdwc->dwc_dma_ipc_log_ctxt, ep_num, req)
|
||||
|
||||
#define dbg_ep_map(ep_num, req) \
|
||||
dwc3_dbg_dma_map(mdwc->dwc_dma_ipc_log_ctxt, ep_num, req)
|
||||
|
||||
#define dbg_log_string(fmt, ...) \
|
||||
ipc_log_string(mdwc->dwc_ipc_log_ctxt,\
|
||||
"%s: " fmt, __func__, ##__VA_ARGS__)
|
||||
|
||||
#define dbg_trace_ctrl_req(ctrl) \
|
||||
dwc3_dbg_trace_log_ctrl(dwc_trace_ipc_log_ctxt, ctrl)
|
||||
|
||||
#define dbg_trace_ep_queue(req) \
|
||||
dwc3_dbg_trace_log_request(dwc_trace_ipc_log_ctxt, req, "dbg_ep_queue")
|
||||
|
||||
#define dbg_trace_ep_dequeue(req) \
|
||||
dwc3_dbg_trace_log_request(dwc_trace_ipc_log_ctxt, req, "dbg_ep_dequeue")
|
||||
|
||||
#define dbg_trace_gadget_giveback(req) \
|
||||
dwc3_dbg_trace_log_request(dwc_trace_ipc_log_ctxt, req, "dbg_gadget_giveback")
|
||||
|
||||
#define dbg_trace_gadget_ep_cmd(dep, cmd, params, cmd_status) \
|
||||
dwc3_dbg_trace_ep_cmd(dwc_trace_ipc_log_ctxt, dep, cmd, params, cmd_status)
|
||||
|
||||
#define dbg_trace_trb_prepare(dep, event) \
|
||||
dwc3_dbg_trace_trb_complete(dwc_trace_ipc_log_ctxt, dep, trb, "dbg_prepare")
|
||||
|
||||
#define dbg_trace_trb_complete(dep, event) \
|
||||
dwc3_dbg_trace_trb_complete(dwc_trace_ipc_log_ctxt, dep, trb, "dbg_complete")
|
||||
|
||||
#define dbg_trace_event(event, dwc) \
|
||||
dwc3_dbg_trace_event(dwc_trace_ipc_log_ctxt, event, dwc)
|
||||
|
||||
void dwc3_dbg_trace_log_ctrl(void *log_ctxt, struct usb_ctrlrequest *ctrl);
|
||||
void dwc3_dbg_trace_log_request(void *log_ctxt, struct dwc3_request *req,
|
||||
char *tag);
|
||||
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);
|
||||
void dwc3_dbg_trace_trb_complete(void *log_ctxt, struct dwc3_ep *dep,
|
||||
struct dwc3_trb *trb, char *tag);
|
||||
void dwc3_dbg_trace_event(void *log_ctxt, u32 event, struct dwc3 *dwc);
|
||||
void dwc3_dbg_print(void *log_ctxt, u8 ep_num,
|
||||
const char *name, int status, const char *extra);
|
||||
void dwc3_dbg_done(void *log_ctxt, u8 ep_num,
|
||||
const u32 count, int status);
|
||||
void dwc3_dbg_event(void *log_ctxt, u8 ep_num,
|
||||
const char *name, int status);
|
||||
void dwc3_dbg_queue(void *log_ctxt, u8 ep_num,
|
||||
const struct usb_request *req, int status);
|
||||
void dwc3_dbg_setup(void *log_ctxt, u8 ep_num,
|
||||
const struct usb_ctrlrequest *req);
|
||||
void dwc3_dbg_print_reg(void *log_ctxt,
|
||||
const char *name, int reg);
|
||||
void dwc3_dbg_dma_queue(void *log_ctxt, u8 ep_num,
|
||||
struct dwc3_request *req);
|
||||
void dwc3_dbg_dma_dequeue(void *log_ctxt, u8 ep_num,
|
||||
struct dwc3_request *req);
|
||||
void dwc3_dbg_dma_map(void *log_ctxt, u8 ep_num,
|
||||
struct dwc3_request *req);
|
||||
void dwc3_dbg_dma_unmap(void *log_ctxt, u8 ep_num,
|
||||
struct dwc3_request *req);
|
||||
|
||||
#endif /* __DWC3_DEBUG_IPC_H */
|
||||
283
drivers/usb/dwc3/debug_ipc.c
Normal file
283
drivers/usb/dwc3/debug_ipc.c
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
6647
drivers/usb/dwc3/dwc3-msm-core.c
Normal file
6647
drivers/usb/dwc3/dwc3-msm-core.c
Normal file
File diff suppressed because it is too large
Load Diff
258
drivers/usb/dwc3/dwc3-msm-ops.c
Normal file
258
drivers/usb/dwc3/dwc3-msm-ops.c
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdesc.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/usb/dwc3-msm.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include "core.h"
|
||||
#include "debug-ipc.h"
|
||||
#include "gadget.h"
|
||||
|
||||
struct kprobe_data {
|
||||
struct dwc3 *dwc;
|
||||
int xi0;
|
||||
};
|
||||
|
||||
static int entry_dwc3_gadget_run_stop(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct dwc3 *dwc = (struct dwc3 *)regs->regs[0];
|
||||
int is_on = (int)regs->regs[1];
|
||||
|
||||
if (is_on) {
|
||||
/*
|
||||
* DWC3 gadget IRQ uses a threaded handler which normally runs
|
||||
* at SCHED_FIFO priority. If it gets busy processing a high
|
||||
* volume of events (usually EP events due to heavy traffic) it
|
||||
* can potentially starve non-RT taks from running and trigger
|
||||
* RT throttling in the scheduler; on some build configs this
|
||||
* will panic. So lower the thread's priority to run as non-RT
|
||||
* (with a nice value equivalent to a high-priority workqueue).
|
||||
* It has been found to not have noticeable performance impact.
|
||||
*/
|
||||
struct irq_desc *irq_desc = irq_to_desc(dwc->irq_gadget);
|
||||
struct irqaction *action = irq_desc ? irq_desc->action : NULL;
|
||||
|
||||
for ( ; action != NULL; action = action->next) {
|
||||
if (action->thread) {
|
||||
dev_info(dwc->dev, "Set IRQ thread:%s pid:%d to SCHED_NORMAL prio\n",
|
||||
action->thread->comm, action->thread->pid);
|
||||
sched_set_normal(action->thread, MIN_NICE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dwc3_core_stop_hw_active_transfers(dwc);
|
||||
dwc3_msm_notify_event(dwc, DWC3_GSI_EVT_BUF_CLEAR, 0);
|
||||
dwc3_msm_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_CLEAR_DB, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_dwc3_send_gadget_ep_cmd(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct dwc3_ep *dep = (struct dwc3_ep *)regs->regs[0];
|
||||
unsigned int cmd = (unsigned int)regs->regs[1];
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
if (cmd == DWC3_DEPCMD_ENDTRANSFER)
|
||||
dwc3_msm_notify_event(dwc,
|
||||
DWC3_CONTROLLER_NOTIFY_DISABLE_UPDXFER,
|
||||
dep->number);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_dwc3_gadget_reset_interrupt(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct dwc3 *dwc = (struct dwc3 *)regs->regs[0];
|
||||
|
||||
dwc3_msm_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_CLEAR_DB, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_dwc3_gadget_conndone_interrupt(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
|
||||
data->dwc = (struct dwc3 *)regs->regs[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_dwc3_gadget_conndone_interrupt(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
|
||||
dwc3_msm_notify_event(data->dwc, DWC3_CONTROLLER_CONNDONE_EVENT, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_dwc3_gadget_pullup(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct usb_gadget *g = (struct usb_gadget *)regs->regs[0];
|
||||
|
||||
data->dwc = gadget_to_dwc(g);
|
||||
data->xi0 = (int)regs->regs[1];
|
||||
dwc3_msm_notify_event(data->dwc, DWC3_CONTROLLER_PULLUP_ENTER,
|
||||
data->xi0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_dwc3_gadget_pullup(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
|
||||
dwc3_msm_notify_event(data->dwc, DWC3_CONTROLLER_PULLUP_EXIT,
|
||||
data->xi0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_trace_dwc3_ctrl_req(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)regs->regs[0];
|
||||
|
||||
dbg_trace_ctrl_req(ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_trace_dwc3_ep_queue(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct dwc3_request *req = (struct dwc3_request *)regs->regs[0];
|
||||
|
||||
dbg_trace_ep_queue(req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_trace_dwc3_ep_dequeue(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct dwc3_request *req = (struct dwc3_request *)regs->regs[0];
|
||||
|
||||
dbg_trace_ep_dequeue(req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_trace_dwc3_gadget_giveback(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct dwc3_request *req = (struct dwc3_request *)regs->regs[0];
|
||||
|
||||
dbg_trace_gadget_giveback(req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_trace_dwc3_gadget_ep_cmd(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct dwc3_ep *dep = (struct dwc3_ep *)regs->regs[0];
|
||||
unsigned int cmd = regs->regs[1];
|
||||
struct dwc3_gadget_ep_cmd_params *param = (struct dwc3_gadget_ep_cmd_params *)regs->regs[2];
|
||||
int cmd_status = regs->regs[3];
|
||||
|
||||
dbg_trace_gadget_ep_cmd(dep, cmd, param, cmd_status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_trace_dwc3_prepare_trb(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct dwc3_ep *dep = (struct dwc3_ep *)regs->regs[0];
|
||||
struct dwc3_trb *trb = (struct dwc3_trb *)regs->regs[1];
|
||||
|
||||
dbg_trace_trb_prepare(dep, trb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_trace_dwc3_event(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
u32 event = regs->regs[0];
|
||||
struct dwc3 *dwc = (struct dwc3 *)regs->regs[1];
|
||||
|
||||
dbg_trace_event(event, dwc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ENTRY_EXIT(name) {\
|
||||
.handler = exit_##name,\
|
||||
.entry_handler = entry_##name,\
|
||||
.data_size = sizeof(struct kprobe_data),\
|
||||
.maxactive = 8,\
|
||||
.kp.symbol_name = #name,\
|
||||
}
|
||||
|
||||
#define ENTRY(name) {\
|
||||
.entry_handler = entry_##name,\
|
||||
.data_size = sizeof(struct kprobe_data),\
|
||||
.maxactive = 8,\
|
||||
.kp.symbol_name = #name,\
|
||||
}
|
||||
|
||||
static struct kretprobe dwc3_msm_probes[] = {
|
||||
ENTRY(dwc3_gadget_run_stop),
|
||||
ENTRY(dwc3_send_gadget_ep_cmd),
|
||||
ENTRY(dwc3_gadget_reset_interrupt),
|
||||
ENTRY_EXIT(dwc3_gadget_conndone_interrupt),
|
||||
ENTRY_EXIT(dwc3_gadget_pullup),
|
||||
ENTRY(trace_dwc3_ctrl_req),
|
||||
ENTRY(trace_dwc3_ep_queue),
|
||||
ENTRY(trace_dwc3_ep_dequeue),
|
||||
ENTRY(trace_dwc3_gadget_giveback),
|
||||
ENTRY(trace_dwc3_gadget_ep_cmd),
|
||||
ENTRY(trace_dwc3_prepare_trb),
|
||||
ENTRY(trace_dwc3_event),
|
||||
};
|
||||
|
||||
|
||||
int dwc3_msm_kretprobe_init(void)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dwc3_msm_probes) ; i++) {
|
||||
ret = register_kretprobe(&dwc3_msm_probes[i]);
|
||||
if (ret < 0) {
|
||||
pr_err("register_kretprobe failed, returned %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dwc3_msm_kretprobe_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dwc3_msm_probes); i++)
|
||||
unregister_kretprobe(&dwc3_msm_probes[i]);
|
||||
}
|
||||
|
||||
288
include/linux/ipc_logging.h
Normal file
288
include/linux/ipc_logging.h
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2015,2017-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _IPC_LOGGING_H
|
||||
#define _IPC_LOGGING_H
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define MAX_MSG_SIZE 255
|
||||
|
||||
enum {
|
||||
TSV_TYPE_MSG_START = 1,
|
||||
TSV_TYPE_SKB = TSV_TYPE_MSG_START,
|
||||
TSV_TYPE_STRING,
|
||||
TSV_TYPE_MSG_END = TSV_TYPE_STRING,
|
||||
};
|
||||
|
||||
struct tsv_header {
|
||||
unsigned char type;
|
||||
unsigned char size; /* size of data field */
|
||||
};
|
||||
|
||||
struct encode_context {
|
||||
struct tsv_header hdr;
|
||||
char buff[MAX_MSG_SIZE];
|
||||
int offset;
|
||||
};
|
||||
|
||||
struct decode_context {
|
||||
int output_format; /* 0 = debugfs */
|
||||
char *buff; /* output buffer */
|
||||
int size; /* size of output buffer */
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPC_LOGGING)
|
||||
/*
|
||||
* ipc_log_context_create: Create a debug log context
|
||||
* Should not be called from atomic context
|
||||
*
|
||||
* @max_num_pages: Number of pages of logging space required (max. 10)
|
||||
* @mod_name : Name of the directory entry under DEBUGFS
|
||||
* @feature_version : First 16 bit for version number of user-defined message
|
||||
* formats and next 16 bit for enabling minidump
|
||||
*
|
||||
* returns context id on success, NULL on failure
|
||||
*/
|
||||
void *ipc_log_context_create(int max_num_pages, const char *modname,
|
||||
uint32_t feature_version);
|
||||
|
||||
/*
|
||||
* msg_encode_start: Start encoding a log message
|
||||
*
|
||||
* @ectxt: Temporary storage to hold the encoded message
|
||||
* @type: Root event type defined by the module which is logging
|
||||
*/
|
||||
void msg_encode_start(struct encode_context *ectxt, uint32_t type);
|
||||
|
||||
/*
|
||||
* tsv_timestamp_write: Writes the current timestamp count
|
||||
*
|
||||
* @ectxt: Context initialized by calling msg_encode_start()
|
||||
*/
|
||||
int tsv_timestamp_write(struct encode_context *ectxt);
|
||||
|
||||
/*
|
||||
* tsv_qtimer_write: Writes the current QTimer timestamp count
|
||||
*
|
||||
* @ectxt: Context initialized by calling msg_encode_start()
|
||||
*/
|
||||
int tsv_qtimer_write(struct encode_context *ectxt);
|
||||
|
||||
/*
|
||||
* tsv_pointer_write: Writes a data pointer
|
||||
*
|
||||
* @ectxt: Context initialized by calling msg_encode_start()
|
||||
* @pointer: Pointer value to write
|
||||
*/
|
||||
int tsv_pointer_write(struct encode_context *ectxt, void *pointer);
|
||||
|
||||
/*
|
||||
* tsv_int32_write: Writes a 32-bit integer value
|
||||
*
|
||||
* @ectxt: Context initialized by calling msg_encode_start()
|
||||
* @n: Integer to write
|
||||
*/
|
||||
int tsv_int32_write(struct encode_context *ectxt, int32_t n);
|
||||
|
||||
/*
|
||||
* tsv_byte_array_write: Writes a byte array
|
||||
*
|
||||
* @ectxt: Context initialized by calling msg_encode_start()
|
||||
* @data: Pointer to byte array
|
||||
* @data_size: Size of byte array
|
||||
*/
|
||||
int tsv_byte_array_write(struct encode_context *ectxt,
|
||||
void *data, int data_size);
|
||||
|
||||
/*
|
||||
* msg_encode_end: Complete the message encode process
|
||||
*
|
||||
* @ectxt: Temporary storage which holds the encoded message
|
||||
*/
|
||||
void msg_encode_end(struct encode_context *ectxt);
|
||||
|
||||
/*
|
||||
* ipc_log_write: Commits message to logging ring buffer
|
||||
*
|
||||
* @ctxt: Logging context
|
||||
* @ectxt: Temporary storage which holds the encoded message
|
||||
*/
|
||||
void ipc_log_write(void *ctxt, struct encode_context *ectxt);
|
||||
|
||||
/*
|
||||
* ipc_log_string: Helper function to log a string
|
||||
*
|
||||
* @ilctxt: Debug Log Context created using ipc_log_context_create()
|
||||
* @fmt: Data specified using format specifiers
|
||||
*/
|
||||
int ipc_log_string(void *ilctxt, const char *fmt, ...) __printf(2, 3);
|
||||
|
||||
/**
|
||||
* ipc_log_extract - Reads and deserializes log
|
||||
*
|
||||
* @ilctxt: logging context
|
||||
* @buff: buffer to receive the data
|
||||
* @size: size of the buffer
|
||||
* @returns: 0 if no data read; >0 number of bytes read; < 0 error
|
||||
*
|
||||
* If no data is available to be read, then the ilctxt::read_avail
|
||||
* completion is reinitialized. This allows clients to block
|
||||
* until new log data is save.
|
||||
*/
|
||||
int ipc_log_extract(void *ilctxt, char *buff, int size);
|
||||
|
||||
/*
|
||||
* Print a string to decode context.
|
||||
* @dctxt Decode context
|
||||
* @args printf args
|
||||
*/
|
||||
#define IPC_SPRINTF_DECODE(dctxt, args...) \
|
||||
do { \
|
||||
int i; \
|
||||
i = scnprintf(dctxt->buff, dctxt->size, args); \
|
||||
dctxt->buff += i; \
|
||||
dctxt->size -= i; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* tsv_timestamp_read: Reads a timestamp
|
||||
*
|
||||
* @ectxt: Context retrieved by reading from log space
|
||||
* @dctxt: Temporary storage to hold the decoded message
|
||||
* @format: Output format while dumping through DEBUGFS
|
||||
*/
|
||||
void tsv_timestamp_read(struct encode_context *ectxt,
|
||||
struct decode_context *dctxt, const char *format);
|
||||
|
||||
/*
|
||||
* tsv_qtimer_read: Reads a QTimer timestamp
|
||||
*
|
||||
* @ectxt: Context retrieved by reading from log space
|
||||
* @dctxt: Temporary storage to hold the decoded message
|
||||
* @format: Output format while dumping through DEBUGFS
|
||||
*/
|
||||
void tsv_qtimer_read(struct encode_context *ectxt,
|
||||
struct decode_context *dctxt, const char *format);
|
||||
|
||||
/*
|
||||
* tsv_pointer_read: Reads a data pointer
|
||||
*
|
||||
* @ectxt: Context retrieved by reading from log space
|
||||
* @dctxt: Temporary storage to hold the decoded message
|
||||
* @format: Output format while dumping through DEBUGFS
|
||||
*/
|
||||
void tsv_pointer_read(struct encode_context *ectxt,
|
||||
struct decode_context *dctxt, const char *format);
|
||||
|
||||
/*
|
||||
* tsv_int32_read: Reads a 32-bit integer value
|
||||
*
|
||||
* @ectxt: Context retrieved by reading from log space
|
||||
* @dctxt: Temporary storage to hold the decoded message
|
||||
* @format: Output format while dumping through DEBUGFS
|
||||
*/
|
||||
int32_t tsv_int32_read(struct encode_context *ectxt,
|
||||
struct decode_context *dctxt, const char *format);
|
||||
|
||||
/*
|
||||
* tsv_byte_array_read: Reads a byte array
|
||||
*
|
||||
* @ectxt: Context retrieved by reading from log space
|
||||
* @dctxt: Temporary storage to hold the decoded message
|
||||
* @format: Output format while dumping through DEBUGFS
|
||||
*/
|
||||
void tsv_byte_array_read(struct encode_context *ectxt,
|
||||
struct decode_context *dctxt, const char *format);
|
||||
|
||||
/*
|
||||
* add_deserialization_func: Register a deserialization function to
|
||||
* unpack the subevents of a main event
|
||||
*
|
||||
* @ctxt: Debug log context to which the deserialization function has
|
||||
* to be registered
|
||||
* @type: Main/Root event, defined by the module which is logging, to
|
||||
* which this deserialization function has to be registered.
|
||||
* @dfune: Deserialization function to be registered
|
||||
*
|
||||
* return 0 on success, -ve value on FAILURE
|
||||
*/
|
||||
int add_deserialization_func(void *ctxt, int type,
|
||||
void (*dfunc)(struct encode_context *,
|
||||
struct decode_context *));
|
||||
|
||||
/*
|
||||
* ipc_log_context_destroy: Destroy debug log context
|
||||
*
|
||||
* @ctxt: debug log context created by calling ipc_log_context_create API.
|
||||
*/
|
||||
int ipc_log_context_destroy(void *ctxt);
|
||||
|
||||
#else
|
||||
|
||||
static inline void *ipc_log_context_create(int max_num_pages,
|
||||
const char *modname, uint32_t feature_version)
|
||||
{ return NULL; }
|
||||
|
||||
static inline void msg_encode_start(struct encode_context *ectxt,
|
||||
uint32_t type) { }
|
||||
|
||||
static inline int tsv_timestamp_write(struct encode_context *ectxt)
|
||||
{ return -EINVAL; }
|
||||
|
||||
static inline int tsv_qtimer_write(struct encode_context *ectxt)
|
||||
{ return -EINVAL; }
|
||||
|
||||
static inline int tsv_pointer_write(struct encode_context *ectxt, void *pointer)
|
||||
{ return -EINVAL; }
|
||||
|
||||
static inline int tsv_int32_write(struct encode_context *ectxt, int32_t n)
|
||||
{ return -EINVAL; }
|
||||
|
||||
static inline int tsv_byte_array_write(struct encode_context *ectxt,
|
||||
void *data, int data_size)
|
||||
{ return -EINVAL; }
|
||||
|
||||
static inline void msg_encode_end(struct encode_context *ectxt) { }
|
||||
|
||||
static inline void ipc_log_write(void *ctxt, struct encode_context *ectxt) { }
|
||||
|
||||
static inline int ipc_log_string(void *ilctxt, const char *fmt, ...)
|
||||
{ return -EINVAL; }
|
||||
|
||||
static inline int ipc_log_extract(void *ilctxt, char *buff, int size)
|
||||
{ return -EINVAL; }
|
||||
|
||||
#define IPC_SPRINTF_DECODE(dctxt, args...) do { } while (0)
|
||||
|
||||
static inline void tsv_timestamp_read(struct encode_context *ectxt,
|
||||
struct decode_context *dctxt, const char *format) { }
|
||||
|
||||
static inline void tsv_qtimer_read(struct encode_context *ectxt,
|
||||
struct decode_context *dctxt, const char *format) { }
|
||||
|
||||
static inline void tsv_pointer_read(struct encode_context *ectxt,
|
||||
struct decode_context *dctxt, const char *format) { }
|
||||
|
||||
static inline int32_t tsv_int32_read(struct encode_context *ectxt,
|
||||
struct decode_context *dctxt, const char *format)
|
||||
{ return 0; }
|
||||
|
||||
static inline void tsv_byte_array_read(struct encode_context *ectxt,
|
||||
struct decode_context *dctxt, const char *format) { }
|
||||
|
||||
static inline int add_deserialization_func(void *ctxt, int type,
|
||||
void (*dfunc)(struct encode_context *,
|
||||
struct decode_context *))
|
||||
{ return 0; }
|
||||
|
||||
static inline int ipc_log_context_destroy(void *ctxt)
|
||||
{ return 0; }
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
335
include/linux/usb/dwc3-msm.h
Normal file
335
include/linux/usb/dwc3-msm.h
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_DWC3_MSM_H
|
||||
#define __LINUX_USB_DWC3_MSM_H
|
||||
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/* used for struct usb_phy flags */
|
||||
#define PHY_HOST_MODE BIT(0)
|
||||
#define DEVICE_IN_SS_MODE BIT(1)
|
||||
#define PHY_LANE_A BIT(2)
|
||||
#define PHY_LANE_B BIT(3)
|
||||
#define PHY_HSFS_MODE BIT(4)
|
||||
#define PHY_LS_MODE BIT(5)
|
||||
#define EUD_SPOOF_DISCONNECT BIT(6)
|
||||
#define EUD_SPOOF_CONNECT BIT(7)
|
||||
#define PHY_SUS_OVERRIDE BIT(8)
|
||||
#define PHY_DP_MODE BIT(9)
|
||||
#define PHY_USB_DP_CONCURRENT_MODE BIT(10)
|
||||
|
||||
/*
|
||||
* The following are bit fields describing the USB BAM options.
|
||||
* These bit fields are set by function drivers that wish to queue
|
||||
* usb_requests with sps/bam parameters.
|
||||
*/
|
||||
#define MSM_TX_PIPE_ID_OFS (16)
|
||||
#define MSM_SPS_MODE BIT(5)
|
||||
#define MSM_IS_FINITE_TRANSFER BIT(6)
|
||||
#define MSM_PRODUCER BIT(7)
|
||||
#define MSM_DISABLE_WB BIT(8)
|
||||
#define MSM_ETD_IOC BIT(9)
|
||||
#define MSM_INTERNAL_MEM BIT(10)
|
||||
#define MSM_VENDOR_ID BIT(16)
|
||||
|
||||
/* EBC TRB parameters */
|
||||
#define EBC_TRB_SIZE 16384
|
||||
|
||||
/* Operations codes for GSI enabled EPs */
|
||||
enum gsi_ep_op {
|
||||
GSI_EP_OP_CONFIG = 0,
|
||||
GSI_EP_OP_STARTXFER,
|
||||
GSI_EP_OP_STORE_DBL_INFO,
|
||||
GSI_EP_OP_ENABLE_GSI,
|
||||
GSI_EP_OP_UPDATEXFER,
|
||||
GSI_EP_OP_RING_DB,
|
||||
GSI_EP_OP_ENDXFER,
|
||||
GSI_EP_OP_GET_CH_INFO,
|
||||
GSI_EP_OP_GET_XFER_IDX,
|
||||
GSI_EP_OP_PREPARE_TRBS,
|
||||
GSI_EP_OP_FREE_TRBS,
|
||||
GSI_EP_OP_SET_CLR_BLOCK_DBL,
|
||||
GSI_EP_OP_CHECK_FOR_SUSPEND,
|
||||
GSI_EP_OP_DISABLE,
|
||||
};
|
||||
|
||||
enum usb_hw_ep_mode {
|
||||
USB_EP_NONE,
|
||||
USB_EP_BAM,
|
||||
USB_EP_GSI,
|
||||
USB_EP_EBC,
|
||||
};
|
||||
|
||||
enum dwc3_notify_event {
|
||||
DWC3_CONTROLLER_ERROR_EVENT,
|
||||
DWC3_CONTROLLER_RESET_EVENT,
|
||||
DWC3_CONTROLLER_POST_RESET_EVENT,
|
||||
DWC3_CORE_PM_SUSPEND_EVENT,
|
||||
DWC3_CORE_PM_RESUME_EVENT,
|
||||
DWC3_CONTROLLER_CONNDONE_EVENT,
|
||||
DWC3_CONTROLLER_NOTIFY_OTG_EVENT,
|
||||
DWC3_CONTROLLER_NOTIFY_DISABLE_UPDXFER,
|
||||
DWC3_CONTROLLER_PULLUP_ENTER,
|
||||
DWC3_CONTROLLER_PULLUP_EXIT,
|
||||
|
||||
/* USB GSI event buffer related notification */
|
||||
DWC3_GSI_EVT_BUF_ALLOC,
|
||||
DWC3_GSI_EVT_BUF_SETUP,
|
||||
DWC3_GSI_EVT_BUF_CLEANUP,
|
||||
DWC3_GSI_EVT_BUF_CLEAR,
|
||||
DWC3_GSI_EVT_BUF_FREE,
|
||||
DWC3_CONTROLLER_NOTIFY_CLEAR_DB,
|
||||
};
|
||||
|
||||
/*
|
||||
* @buf_base_addr: Base pointer to buffer allocated for each GSI enabled EP.
|
||||
* TRBs point to buffers that are split from this pool. The size of the
|
||||
* buffer is num_bufs times buf_len. num_bufs and buf_len are determined
|
||||
based on desired performance and aggregation size.
|
||||
* @dma: DMA address corresponding to buf_base_addr.
|
||||
* @num_bufs: Number of buffers associated with the GSI enabled EP. This
|
||||
* corresponds to the number of non-zlp TRBs allocated for the EP.
|
||||
* The value is determined based on desired performance for the EP.
|
||||
* @buf_len: Size of each individual buffer is determined based on aggregation
|
||||
* negotiated as per the protocol. In case of no aggregation supported by
|
||||
* the protocol, we use default values.
|
||||
* @db_reg_phs_addr_lsb: IPA channel doorbell register's physical address LSB
|
||||
* @mapped_db_reg_phs_addr_lsb: doorbell LSB IOVA address mapped with IOMMU
|
||||
* @db_reg_phs_addr_msb: IPA channel doorbell register's physical address MSB
|
||||
* @ep_intr_num: Interrupter number for EP.
|
||||
*/
|
||||
struct usb_gsi_request {
|
||||
void *buf_base_addr;
|
||||
dma_addr_t dma;
|
||||
size_t num_bufs;
|
||||
size_t buf_len;
|
||||
u32 db_reg_phs_addr_lsb;
|
||||
dma_addr_t mapped_db_reg_phs_addr_lsb;
|
||||
u32 db_reg_phs_addr_msb;
|
||||
u8 ep_intr_num;
|
||||
struct sg_table sgt_trb_xfer_ring;
|
||||
struct sg_table sgt_data_buff;
|
||||
};
|
||||
|
||||
/*
|
||||
* @last_trb_addr: Address (LSB - based on alignment restrictions) of
|
||||
* last TRB in queue. Used to identify rollover case.
|
||||
* @const_buffer_size: TRB buffer size in KB (similar to IPA aggregation
|
||||
* configuration). Must be aligned to Max USB Packet Size.
|
||||
* Should be 1 <= const_buffer_size <= 31.
|
||||
* @depcmd_low_addr: Used by GSI hardware to write "Update Transfer" cmd
|
||||
* @depcmd_hi_addr: Used to write "Update Transfer" command.
|
||||
* @gevntcount_low_addr: GEVNCOUNT low address for GSI hardware to read and
|
||||
* clear processed events.
|
||||
* @gevntcount_hi_addr: GEVNCOUNT high address.
|
||||
* @xfer_ring_len: length of transfer ring in bytes (must be integral
|
||||
* multiple of TRB size - 16B for xDCI).
|
||||
* @xfer_ring_base_addr: physical base address of transfer ring. Address must
|
||||
* be aligned to xfer_ring_len rounded to power of two.
|
||||
* @ch_req: Used to pass request specific info for certain operations on GSI EP
|
||||
*/
|
||||
struct gsi_channel_info {
|
||||
u16 last_trb_addr;
|
||||
u8 const_buffer_size;
|
||||
u32 depcmd_low_addr;
|
||||
u8 depcmd_hi_addr;
|
||||
u32 gevntcount_low_addr;
|
||||
u8 gevntcount_hi_addr;
|
||||
u16 xfer_ring_len;
|
||||
u64 xfer_ring_base_addr;
|
||||
struct usb_gsi_request *ch_req;
|
||||
};
|
||||
|
||||
struct dwc3;
|
||||
extern void *dwc_trace_ipc_log_ctxt;
|
||||
|
||||
/**
|
||||
* usb_gadget_autopm_get - increment PM-usage counter of usb gadget's parent
|
||||
* device.
|
||||
* @gadget: usb gadget whose parent device counter is incremented
|
||||
*
|
||||
* This routine should be called by function driver when it wants to use
|
||||
* gadget's parent device and needs to guarantee that it is not suspended. In
|
||||
* addition, the routine prevents subsequent autosuspends of gadget's parent
|
||||
* device. However if the autoresume fails then the counter is re-decremented.
|
||||
*
|
||||
* This routine can run only in process context.
|
||||
*/
|
||||
static inline int usb_gadget_autopm_get(struct usb_gadget *gadget)
|
||||
{
|
||||
int status = -ENODEV;
|
||||
|
||||
if (!gadget || !gadget->dev.parent)
|
||||
return status;
|
||||
|
||||
status = pm_runtime_get_sync(gadget->dev.parent);
|
||||
if (status < 0)
|
||||
pm_runtime_put_sync(gadget->dev.parent);
|
||||
|
||||
if (status > 0)
|
||||
status = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_gadget_autopm_get_async - increment PM-usage counter of usb gadget's
|
||||
* parent device.
|
||||
* @gadget: usb gadget whose parent device counter is incremented
|
||||
*
|
||||
* This routine increments @gadget parent device PM usage counter and queue an
|
||||
* autoresume request if the device is suspended. It does not autoresume device
|
||||
* directly (it only queues a request). After a successful call, the device may
|
||||
* not yet be resumed.
|
||||
*
|
||||
* This routine can run in atomic context.
|
||||
*/
|
||||
static inline int usb_gadget_autopm_get_async(struct usb_gadget *gadget)
|
||||
{
|
||||
int status = -ENODEV;
|
||||
|
||||
if (!gadget || !gadget->dev.parent)
|
||||
return status;
|
||||
|
||||
status = pm_runtime_get(gadget->dev.parent);
|
||||
if (status < 0 && status != -EINPROGRESS)
|
||||
pm_runtime_put_noidle(gadget->dev.parent);
|
||||
|
||||
if (status > 0 || status == -EINPROGRESS)
|
||||
status = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_gadget_autopm_get_noresume - increment PM-usage counter of usb gadget's
|
||||
* parent device.
|
||||
* @gadget: usb gadget whose parent device counter is incremented
|
||||
*
|
||||
* This routine increments PM-usage count of @gadget parent device but does not
|
||||
* carry out an autoresume.
|
||||
*
|
||||
* This routine can run in atomic context.
|
||||
*/
|
||||
static inline void usb_gadget_autopm_get_noresume(struct usb_gadget *gadget)
|
||||
{
|
||||
if (gadget && gadget->dev.parent)
|
||||
pm_runtime_get_noresume(gadget->dev.parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_gadget_autopm_put - decrement PM-usage counter of usb gadget's parent
|
||||
* device.
|
||||
* @gadget: usb gadget whose parent device counter is decremented.
|
||||
*
|
||||
* This routine should be called by function driver when it is finished using
|
||||
* @gadget parent device and wants to allow it to autosuspend. It decrements
|
||||
* PM-usage counter of @gadget parent device, when the counter reaches 0, a
|
||||
* delayed autosuspend request is attempted.
|
||||
*
|
||||
* This routine can run only in process context.
|
||||
*/
|
||||
static inline void usb_gadget_autopm_put(struct usb_gadget *gadget)
|
||||
{
|
||||
if (gadget && gadget->dev.parent)
|
||||
pm_runtime_put_sync(gadget->dev.parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_gadget_autopm_put_async - decrement PM-usage counter of usb gadget's
|
||||
* parent device.
|
||||
* @gadget: usb gadget whose parent device counter is decremented.
|
||||
*
|
||||
* This routine decrements PM-usage counter of @gadget parent device and
|
||||
* schedules a delayed autosuspend request if the counter is <= 0.
|
||||
*
|
||||
* This routine can run in atomic context.
|
||||
*/
|
||||
static inline void usb_gadget_autopm_put_async(struct usb_gadget *gadget)
|
||||
{
|
||||
if (gadget && gadget->dev.parent)
|
||||
pm_runtime_put(gadget->dev.parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_gadget_autopm_put_no_suspend - decrement PM-usage counter of usb gadget
|
||||
's
|
||||
* parent device.
|
||||
* @gadget: usb gadget whose parent device counter is decremented.
|
||||
*
|
||||
* This routine decrements PM-usage counter of @gadget parent device but does
|
||||
* not carry out an autosuspend.
|
||||
*
|
||||
* This routine can run in atomic context.
|
||||
*/
|
||||
static inline void usb_gadget_autopm_put_no_suspend(struct usb_gadget *gadget)
|
||||
{
|
||||
if (gadget && gadget->dev.parent)
|
||||
pm_runtime_put_noidle(gadget->dev.parent);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_MSM)
|
||||
void dwc3_msm_notify_event(struct dwc3 *dwc,
|
||||
enum dwc3_notify_event event, unsigned int value);
|
||||
int usb_gsi_ep_op(struct usb_ep *ep, void *op_data, enum gsi_ep_op op);
|
||||
int msm_ep_config(struct usb_ep *ep, struct usb_request *request, u32 bam_opts);
|
||||
int msm_ep_unconfig(struct usb_ep *ep);
|
||||
void dwc3_tx_fifo_resize_request(struct usb_ep *ep, bool qdss_enable);
|
||||
int msm_data_fifo_config(struct usb_ep *ep, unsigned long addr, u32 size,
|
||||
u8 dst_pipe_idx);
|
||||
int msm_dwc3_reset_dbm_ep(struct usb_ep *ep);
|
||||
int dwc3_msm_set_dp_mode(struct device *dev, bool connected, int lanes);
|
||||
int dwc3_msm_release_ss_lane(struct device *dev);
|
||||
int msm_ep_update_ops(struct usb_ep *ep);
|
||||
int msm_ep_clear_ops(struct usb_ep *ep);
|
||||
int msm_ep_set_mode(struct usb_ep *ep, enum usb_hw_ep_mode mode);
|
||||
int dwc3_core_stop_hw_active_transfers(struct dwc3 *dwc);
|
||||
#else
|
||||
void dwc3_msm_notify_event(struct dwc3 *dwc,
|
||||
enum dwc3_notify_event event, unsigned int value)
|
||||
{ }
|
||||
static inline int usb_gsi_ep_op(struct usb_ep *ep, void *op_data,
|
||||
enum gsi_ep_op op)
|
||||
{ return 0; }
|
||||
static inline int msm_data_fifo_config(struct usb_ep *ep, unsigned long addr,
|
||||
u32 size, u8 dst_pipe_idx)
|
||||
{ return -ENODEV; }
|
||||
static inline int msm_ep_config(struct usb_ep *ep, struct usb_request *request,
|
||||
u32 bam_opts)
|
||||
{ return -ENODEV; }
|
||||
static inline int msm_ep_unconfig(struct usb_ep *ep)
|
||||
{ return -ENODEV; }
|
||||
static inline void dwc3_tx_fifo_resize_request(struct usb_ep *ep,
|
||||
bool qdss_enable)
|
||||
{ }
|
||||
static inline bool msm_dwc3_reset_ep_after_lpm(struct usb_gadget *gadget)
|
||||
{ return false; }
|
||||
static inline int dwc3_msm_set_dp_mode(struct device *dev, bool connected, int lanes)
|
||||
{ return -ENODEV; }
|
||||
static inline int dwc3_msm_release_ss_lane(struct device *dev)
|
||||
{ return -ENODEV; }
|
||||
int msm_ep_update_ops(struct usb_ep *ep)
|
||||
{ return -ENODEV; }
|
||||
int msm_ep_clear_ops(struct usb_ep *ep)
|
||||
{ return -ENODEV; }
|
||||
int msm_ep_set_mode(struct usb_ep *ep, enum usb_hw_ep_mode mode)
|
||||
{ return -ENODEV; }
|
||||
inline int dwc3_core_stop_hw_active_transfers(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARM64
|
||||
int dwc3_msm_kretprobe_init(void);
|
||||
void dwc3_msm_kretprobe_exit(void);
|
||||
#else
|
||||
int dwc3_msm_kretprobe_init(void)
|
||||
{ return 0; }
|
||||
void dwc3_msm_kretprobe_exit(void)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_USB_DWC3_MSM_H */
|
||||
127
include/linux/usb/redriver.h
Normal file
127
include/linux/usb/redriver.h
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_REDRIVER_H
|
||||
#define __LINUX_USB_REDRIVER_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/*
|
||||
* design rules,
|
||||
* [a] assume pullup operation happen in kretprobe.
|
||||
* in kretprobe function, mutex lock is not allowed;
|
||||
* in kretprobe function, schedule_work() is allowed;
|
||||
* [b] this is core driver which service lower redriver and upper user.
|
||||
* [c] redriver must probe early than user, or not user will defer probe.
|
||||
* [d] redriver can rmmod only when there is no user bind to it.
|
||||
* [e] if user rmmod, redirver will change to default state.
|
||||
* [f] if redriver module insmod after new change and build,
|
||||
* user module also need insmod to work.
|
||||
* [g] when a redriver probe, set to disable state, all control from user.
|
||||
* as ssphy have no eud function which don't need to keep working.
|
||||
* [h] user should be ssphy, but current user is dwc3,
|
||||
* as seem some redriver have termination issue,
|
||||
* it need to do pullup operation from controller driver.
|
||||
*/
|
||||
|
||||
|
||||
#define ORIENTATION_CC1 0
|
||||
#define ORIENTATION_CC2 1
|
||||
|
||||
/**
|
||||
* struct usb_redriver - present a redriver chip
|
||||
* @list: link all redriver chips
|
||||
* @of_node: redriver chip device tree node
|
||||
* @release_usb_lanes: put redriver into 2/4 lanes display mode
|
||||
* @notify_connect: cable connect
|
||||
* @notify_disconnect: cable disconnect
|
||||
* @orientation_get: report orientation to user if orientation source shared
|
||||
* @gadget_pullup_enter: operation when enter gadget pullup function
|
||||
* @gadget_pullup_exit: operation when exit gadget pullup function
|
||||
* @host_power_cycle: workaround for host otg case
|
||||
* @unbind, change to default state when user unbind it
|
||||
* @has_orientation, provide orientation from chip driver or not
|
||||
* @bounded, bound to user or not
|
||||
*/
|
||||
struct usb_redriver {
|
||||
struct list_head list;
|
||||
struct device_node *of_node;
|
||||
|
||||
int (*release_usb_lanes)(struct usb_redriver *ur, int num);
|
||||
int (*notify_connect)(struct usb_redriver *ur, int ort);
|
||||
int (*notify_disconnect)(struct usb_redriver *ur);
|
||||
int (*get_orientation)(struct usb_redriver *ur);
|
||||
int (*gadget_pullup_enter)(struct usb_redriver *ur, int is_on);
|
||||
int (*gadget_pullup_exit)(struct usb_redriver *ur, int is_on);
|
||||
int (*host_powercycle)(struct usb_redriver *ur);
|
||||
void (*unbind)(struct usb_redriver *ur);
|
||||
|
||||
bool has_orientation;
|
||||
bool bounded;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_REDRIVER)
|
||||
int usb_add_redriver(struct usb_redriver *ur);
|
||||
int usb_remove_redriver(struct usb_redriver *ur);
|
||||
|
||||
struct usb_redriver *usb_get_redriver_by_phandle(
|
||||
const struct device_node *np,
|
||||
const char *phandle_name, int index);
|
||||
void usb_put_redriver(struct usb_redriver *ur);
|
||||
void usb_redriver_release_lanes(struct usb_redriver *ur, int num);
|
||||
void usb_redriver_notify_connect(struct usb_redriver *ur, int ort);
|
||||
void usb_redriver_notify_disconnect(struct usb_redriver *ur);
|
||||
int usb_redriver_get_orientation(struct usb_redriver *ur);
|
||||
void usb_redriver_gadget_pullup_enter(struct usb_redriver *ur, int is_on);
|
||||
void usb_redriver_gadget_pullup_exit(struct usb_redriver *ur, int is_on);
|
||||
void usb_redriver_host_powercycle(struct usb_redriver *ur);
|
||||
|
||||
#else
|
||||
|
||||
static inline int usb_add_redriver(struct usb_redriver *ur)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct usb_redriver *usb_get_redriver_by_phandle(
|
||||
const struct device_node *np,
|
||||
const char *phandle_name, int index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int usb_remove_redriver(struct usb_redriver *ur)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int usb_redriver_get_orientation(struct usb_redriver *ur)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define usb_put_redriver(ur) do {} while (0)
|
||||
|
||||
#define usb_redriver_release_lanes(ur, num) do {} while (0)
|
||||
#define usb_redriver_notify_connect(ur, ort) do {} while (0)
|
||||
#define usb_redriver_notify_disconnect(ur) do {} while (0)
|
||||
#define usb_redriver_gadget_pullup_enter(ur, is_on) do {} while (0)
|
||||
#define usb_redriver_gadget_pullup_exit(ur, is_on) do {} while (0)
|
||||
#define usb_redriver_host_powercycle(ur) do {} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
static inline bool usb_redriver_has_orientation(struct usb_redriver *ur)
|
||||
{
|
||||
if (ur && ur->has_orientation)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /*__LINUX_USB_REDRIVER_H */
|
||||
Loading…
Reference in New Issue
Block a user