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:
Wesley Cheng 2022-04-25 14:23:39 -07:00
parent a5f8942a16
commit 3e7e062372
9 changed files with 8060 additions and 0 deletions

View File

@ -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

View File

@ -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

View 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 */

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

File diff suppressed because it is too large Load Diff

View 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
View 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

View 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 */

View 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 */