mirror of
https://github.com/torvalds/linux.git
synced 2026-08-01 20:22:08 +02:00
Merge "pineapple: Add USB modules to modules.list.msm.pineapple"
This commit is contained in:
commit
82665d760a
|
|
@ -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]);
|
||||
}
|
||||
|
||||
|
|
@ -192,4 +192,47 @@ config JZ4770_PHY
|
|||
This driver provides PHY support for the USB controller found
|
||||
on the JZ-series and X-series SoCs from Ingenic.
|
||||
|
||||
config USB_QCOM_EMU_PHY
|
||||
tristate "Qualcomm Technologies, Inc. emulation USB PHY driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
select USB_PHY
|
||||
help
|
||||
Enable this to support the USB transceiver used on
|
||||
Qualcomm Technologies, Inc. emulation platforms. It simply performs
|
||||
PHY initialization given a basic register write sequence.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called phy-qcom-emu.
|
||||
|
||||
config USB_MSM_SSPHY_QMP
|
||||
tristate "MSM SSUSB QMP PHY Driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
select USB_PHY
|
||||
help
|
||||
Enable this to support the SuperSpeed USB transceiver on MSM chips.
|
||||
This driver supports the PHY which uses the QSCRATCH-based register
|
||||
set for its control sequences, normally paired with newer DWC3-based
|
||||
SuperSpeed controllers.
|
||||
|
||||
config MSM_HSUSB_PHY
|
||||
tristate "MSM HSUSB PHY Driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
select USB_PHY
|
||||
help
|
||||
Enable this to support the HSUSB PHY on MSM chips. This driver supports
|
||||
the high-speed PHY which is usually paired with either the ChipIdea or
|
||||
Synopsys DWC3 USB IPs on MSM SOCs. This driver expects to configure the
|
||||
PHY with a dedicated register I/O memory region.
|
||||
|
||||
config USB_MSM_EUSB2_PHY
|
||||
tristate "MSM EUSB2 PHY Driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
select USB_PHY
|
||||
help
|
||||
Enable this to support the USB EUSB2 PHY on MSM chips. This driver
|
||||
supports reset and initialization sequence, and also perform required
|
||||
set of operations with used repeater for USB HS/FS/LS functionality.
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
endmenu
|
||||
|
|
|
|||
|
|
@ -25,3 +25,7 @@ obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
|
|||
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
|
||||
obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o
|
||||
obj-$(CONFIG_JZ4770_PHY) += phy-jz4770.o
|
||||
obj-$(CONFIG_USB_QCOM_EMU_PHY) += phy-qcom-emu.o
|
||||
obj-$(CONFIG_USB_MSM_SSPHY_QMP) += phy-msm-ssusb-qmp.o
|
||||
obj-$(CONFIG_MSM_HSUSB_PHY) += phy-msm-snps-hs.o
|
||||
obj-$(CONFIG_USB_MSM_EUSB2_PHY) += phy-msm-snps-eusb2.o
|
||||
|
|
|
|||
1088
drivers/usb/phy/phy-msm-snps-eusb2.c
Normal file
1088
drivers/usb/phy/phy-msm-snps-eusb2.c
Normal file
File diff suppressed because it is too large
Load Diff
939
drivers/usb/phy/phy-msm-snps-hs.c
Normal file
939
drivers/usb/phy/phy-msm-snps-hs.c
Normal file
|
|
@ -0,0 +1,939 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/usb/dwc3-msm.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define USB2_PHY_USB_PHY_UTMI_CTRL0 (0x3c)
|
||||
#define OPMODE_MASK (0x3 << 3)
|
||||
#define OPMODE_NONDRIVING (0x1 << 3)
|
||||
#define SLEEPM BIT(0)
|
||||
|
||||
#define USB2_PHY_USB_PHY_UTMI_CTRL5 (0x50)
|
||||
#define POR BIT(1)
|
||||
|
||||
#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54)
|
||||
#define SIDDQ BIT(2)
|
||||
#define RETENABLEN BIT(3)
|
||||
#define FSEL_MASK (0x7 << 4)
|
||||
#define FSEL_DEFAULT (0x3 << 4)
|
||||
|
||||
#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1 (0x58)
|
||||
#define VBUSVLDEXTSEL0 BIT(4)
|
||||
#define PLLBTUNE BIT(5)
|
||||
|
||||
#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2 (0x5c)
|
||||
#define VREGBYPASS BIT(0)
|
||||
|
||||
#define USB2_PHY_USB_PHY_HS_PHY_CTRL1 (0x60)
|
||||
#define VBUSVLDEXT0 BIT(0)
|
||||
|
||||
#define USB2_PHY_USB_PHY_HS_PHY_CTRL2 (0x64)
|
||||
#define USB2_AUTO_RESUME BIT(0)
|
||||
#define USB2_SUSPEND_N BIT(2)
|
||||
#define USB2_SUSPEND_N_SEL BIT(3)
|
||||
|
||||
#define USB2_PHY_USB_PHY_CFG0 (0x94)
|
||||
#define UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN BIT(0)
|
||||
#define UTMI_PHY_CMN_CTRL_OVERRIDE_EN BIT(1)
|
||||
|
||||
#define USB2_PHY_USB_PHY_REFCLK_CTRL (0xa0)
|
||||
#define REFCLK_SEL_MASK (0x3 << 0)
|
||||
#define REFCLK_SEL_DEFAULT (0x2 << 0)
|
||||
|
||||
#define USB2_PHY_USB_PHY_PWRDOWN_CTRL (0xa4)
|
||||
#define PWRDOWN_B BIT(0)
|
||||
|
||||
#define USB2PHY_USB_PHY_RTUNE_SEL (0xb4)
|
||||
#define RTUNE_SEL BIT(0)
|
||||
|
||||
#define TXPREEMPAMPTUNE0(x) (x << 6)
|
||||
#define TXPREEMPAMPTUNE0_MASK (BIT(7) | BIT(6))
|
||||
#define USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X0 0x6c
|
||||
#define USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X1 0x70
|
||||
#define USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X2 0x74
|
||||
#define USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X3 0x78
|
||||
#define TXVREFTUNE0_MASK 0xF
|
||||
#define PARAM_OVRD_MASK 0xFF
|
||||
|
||||
#define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */
|
||||
#define USB_HSPHY_3P3_VOL_MAX 3300000 /* uV */
|
||||
#define USB_HSPHY_3P3_HPM_LOAD 16000 /* uA */
|
||||
#define USB_HSPHY_3P3_VOL_FSHOST 3150000 /* uV */
|
||||
|
||||
#define USB_HSPHY_1P8_VOL_MIN 1704000 /* uV */
|
||||
#define USB_HSPHY_1P8_VOL_MAX 1800000 /* uV */
|
||||
#define USB_HSPHY_1P8_HPM_LOAD 19000 /* uA */
|
||||
|
||||
#define USB_HSPHY_VDD_HPM_LOAD 30000 /* uA */
|
||||
|
||||
struct msm_hsphy {
|
||||
struct usb_phy phy;
|
||||
void __iomem *base;
|
||||
phys_addr_t eud_reg;
|
||||
void __iomem *eud_enable_reg;
|
||||
bool re_enable_eud;
|
||||
|
||||
struct clk *ref_clk_src;
|
||||
struct clk *cfg_ahb_clk;
|
||||
struct reset_control *phy_reset;
|
||||
|
||||
struct regulator *vdd;
|
||||
struct regulator *vdda33;
|
||||
struct regulator *vdda18;
|
||||
int vdd_levels[3]; /* none, low, high */
|
||||
|
||||
bool clocks_enabled;
|
||||
bool power_enabled;
|
||||
bool suspended;
|
||||
bool cable_connected;
|
||||
bool dpdm_enable;
|
||||
|
||||
int *param_override_seq;
|
||||
int param_override_seq_cnt;
|
||||
|
||||
void __iomem *phy_rcal_reg;
|
||||
u32 rcal_mask;
|
||||
|
||||
struct mutex phy_lock;
|
||||
struct regulator_desc dpdm_rdesc;
|
||||
struct regulator_dev *dpdm_rdev;
|
||||
|
||||
struct power_supply *usb_psy;
|
||||
unsigned int vbus_draw;
|
||||
struct work_struct vbus_draw_work;
|
||||
|
||||
/* debugfs entries */
|
||||
struct dentry *root;
|
||||
u8 txvref_tune0;
|
||||
u8 pre_emphasis;
|
||||
u8 param_ovrd0;
|
||||
u8 param_ovrd1;
|
||||
u8 param_ovrd2;
|
||||
u8 param_ovrd3;
|
||||
};
|
||||
|
||||
static void msm_hsphy_enable_clocks(struct msm_hsphy *phy, bool on)
|
||||
{
|
||||
dev_dbg(phy->phy.dev, "%s(): clocks_enabled:%d on:%d\n",
|
||||
__func__, phy->clocks_enabled, on);
|
||||
|
||||
if (!phy->clocks_enabled && on) {
|
||||
clk_prepare_enable(phy->ref_clk_src);
|
||||
|
||||
if (phy->cfg_ahb_clk)
|
||||
clk_prepare_enable(phy->cfg_ahb_clk);
|
||||
|
||||
phy->clocks_enabled = true;
|
||||
}
|
||||
|
||||
if (phy->clocks_enabled && !on) {
|
||||
if (phy->cfg_ahb_clk)
|
||||
clk_disable_unprepare(phy->cfg_ahb_clk);
|
||||
|
||||
clk_disable_unprepare(phy->ref_clk_src);
|
||||
phy->clocks_enabled = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int msm_hsphy_enable_power(struct msm_hsphy *phy, bool on)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(phy->phy.dev, "%s turn %s regulators. power_enabled:%d\n",
|
||||
__func__, on ? "on" : "off", phy->power_enabled);
|
||||
|
||||
if (phy->power_enabled == on) {
|
||||
dev_dbg(phy->phy.dev, "PHYs' regulators are already ON.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!on)
|
||||
goto disable_vdda33;
|
||||
|
||||
ret = regulator_set_load(phy->vdd, USB_HSPHY_VDD_HPM_LOAD);
|
||||
if (ret < 0) {
|
||||
dev_err(phy->phy.dev, "Unable to set HPM of vdd:%d\n", ret);
|
||||
goto err_vdd;
|
||||
}
|
||||
|
||||
ret = regulator_set_voltage(phy->vdd, phy->vdd_levels[1],
|
||||
phy->vdd_levels[2]);
|
||||
if (ret) {
|
||||
dev_err(phy->phy.dev, "unable to set voltage for hsusb vdd\n");
|
||||
goto put_vdd_lpm;
|
||||
}
|
||||
|
||||
ret = regulator_enable(phy->vdd);
|
||||
if (ret) {
|
||||
dev_err(phy->phy.dev, "Unable to enable VDD\n");
|
||||
goto unconfig_vdd;
|
||||
}
|
||||
|
||||
ret = regulator_set_load(phy->vdda18, USB_HSPHY_1P8_HPM_LOAD);
|
||||
if (ret < 0) {
|
||||
dev_err(phy->phy.dev, "Unable to set HPM of vdda18:%d\n", ret);
|
||||
goto disable_vdd;
|
||||
}
|
||||
|
||||
ret = regulator_set_voltage(phy->vdda18, USB_HSPHY_1P8_VOL_MIN,
|
||||
USB_HSPHY_1P8_VOL_MAX);
|
||||
if (ret) {
|
||||
dev_err(phy->phy.dev,
|
||||
"Unable to set voltage for vdda18:%d\n", ret);
|
||||
goto put_vdda18_lpm;
|
||||
}
|
||||
|
||||
ret = regulator_enable(phy->vdda18);
|
||||
if (ret) {
|
||||
dev_err(phy->phy.dev, "Unable to enable vdda18:%d\n", ret);
|
||||
goto unset_vdda18;
|
||||
}
|
||||
|
||||
ret = regulator_set_load(phy->vdda33, USB_HSPHY_3P3_HPM_LOAD);
|
||||
if (ret < 0) {
|
||||
dev_err(phy->phy.dev, "Unable to set HPM of vdda33:%d\n", ret);
|
||||
goto disable_vdda18;
|
||||
}
|
||||
|
||||
ret = regulator_set_voltage(phy->vdda33, USB_HSPHY_3P3_VOL_MIN,
|
||||
USB_HSPHY_3P3_VOL_MAX);
|
||||
if (ret) {
|
||||
dev_err(phy->phy.dev,
|
||||
"Unable to set voltage for vdda33:%d\n", ret);
|
||||
goto put_vdda33_lpm;
|
||||
}
|
||||
|
||||
ret = regulator_enable(phy->vdda33);
|
||||
if (ret) {
|
||||
dev_err(phy->phy.dev, "Unable to enable vdda33:%d\n", ret);
|
||||
goto unset_vdd33;
|
||||
}
|
||||
|
||||
phy->power_enabled = true;
|
||||
|
||||
pr_debug("%s(): HSUSB PHY's regulators are turned ON.\n", __func__);
|
||||
return ret;
|
||||
|
||||
disable_vdda33:
|
||||
ret = regulator_disable(phy->vdda33);
|
||||
if (ret)
|
||||
dev_err(phy->phy.dev, "Unable to disable vdda33:%d\n", ret);
|
||||
|
||||
unset_vdd33:
|
||||
ret = regulator_set_voltage(phy->vdda33, 0, USB_HSPHY_3P3_VOL_MAX);
|
||||
if (ret)
|
||||
dev_err(phy->phy.dev,
|
||||
"Unable to set (0) voltage for vdda33:%d\n", ret);
|
||||
|
||||
put_vdda33_lpm:
|
||||
ret = regulator_set_load(phy->vdda33, 0);
|
||||
if (ret < 0)
|
||||
dev_err(phy->phy.dev, "Unable to set (0) HPM of vdda33\n");
|
||||
|
||||
disable_vdda18:
|
||||
ret = regulator_disable(phy->vdda18);
|
||||
if (ret)
|
||||
dev_err(phy->phy.dev, "Unable to disable vdda18:%d\n", ret);
|
||||
|
||||
unset_vdda18:
|
||||
ret = regulator_set_voltage(phy->vdda18, 0, USB_HSPHY_1P8_VOL_MAX);
|
||||
if (ret)
|
||||
dev_err(phy->phy.dev,
|
||||
"Unable to set (0) voltage for vdda18:%d\n", ret);
|
||||
|
||||
put_vdda18_lpm:
|
||||
ret = regulator_set_load(phy->vdda18, 0);
|
||||
if (ret < 0)
|
||||
dev_err(phy->phy.dev, "Unable to set LPM of vdda18\n");
|
||||
|
||||
disable_vdd:
|
||||
ret = regulator_disable(phy->vdd);
|
||||
if (ret)
|
||||
dev_err(phy->phy.dev, "Unable to disable vdd:%d\n", ret);
|
||||
|
||||
unconfig_vdd:
|
||||
ret = regulator_set_voltage(phy->vdd, phy->vdd_levels[0],
|
||||
phy->vdd_levels[2]);
|
||||
if (ret)
|
||||
dev_err(phy->phy.dev, "unable to set voltage for hsusb vdd\n");
|
||||
|
||||
put_vdd_lpm:
|
||||
ret = regulator_set_load(phy->vdd, 0);
|
||||
if (ret < 0)
|
||||
dev_err(phy->phy.dev, "Unable to set LPM of vdd\n");
|
||||
err_vdd:
|
||||
phy->power_enabled = false;
|
||||
dev_dbg(phy->phy.dev, "HSUSB PHY's regulators are turned OFF.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void msm_usb_write_readback(void __iomem *base, u32 offset,
|
||||
const u32 mask, u32 val)
|
||||
{
|
||||
u32 write_val, tmp = readl_relaxed(base + offset);
|
||||
|
||||
tmp &= ~mask; /* retain other bits */
|
||||
write_val = tmp | val;
|
||||
|
||||
writel_relaxed(write_val, base + offset);
|
||||
|
||||
/* Read back to see if val was written */
|
||||
tmp = readl_relaxed(base + offset);
|
||||
tmp &= mask; /* clear other bits */
|
||||
|
||||
if (tmp != val)
|
||||
pr_err("%s: write: %x to QSCRATCH: %x FAILED\n",
|
||||
__func__, val, offset);
|
||||
}
|
||||
|
||||
static void msm_hsphy_reset(struct msm_hsphy *phy)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = reset_control_assert(phy->phy_reset);
|
||||
if (ret)
|
||||
dev_err(phy->phy.dev, "%s: phy_reset assert failed\n",
|
||||
__func__);
|
||||
usleep_range(100, 150);
|
||||
|
||||
ret = reset_control_deassert(phy->phy_reset);
|
||||
if (ret)
|
||||
dev_err(phy->phy.dev, "%s: phy_reset deassert failed\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
static void hsusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt,
|
||||
unsigned long delay)
|
||||
{
|
||||
int i;
|
||||
|
||||
pr_debug("Seq count:%d\n", cnt);
|
||||
for (i = 0; i < cnt; i = i+2) {
|
||||
pr_debug("write 0x%02x to 0x%02x\n", seq[i], seq[i+1]);
|
||||
writel_relaxed(seq[i], base + seq[i+1]);
|
||||
if (delay)
|
||||
usleep_range(delay, (delay + 2000));
|
||||
}
|
||||
}
|
||||
|
||||
#define EUD_EN2 BIT(0)
|
||||
static int msm_hsphy_init(struct usb_phy *uphy)
|
||||
{
|
||||
struct msm_hsphy *phy = container_of(uphy, struct msm_hsphy, phy);
|
||||
int ret;
|
||||
u32 rcal_code = 0, eud_csr_reg = 0;
|
||||
|
||||
dev_dbg(uphy->dev, "%s phy_flags:0x%x\n", __func__, phy->phy.flags);
|
||||
if (phy->eud_enable_reg) {
|
||||
eud_csr_reg = readl_relaxed(phy->eud_enable_reg);
|
||||
if (eud_csr_reg & EUD_EN2) {
|
||||
dev_dbg(phy->phy.dev, "csr:0x%x eud is enabled\n",
|
||||
eud_csr_reg);
|
||||
/* if in host mode, disable EUD */
|
||||
if (phy->phy.flags & PHY_HOST_MODE) {
|
||||
qcom_scm_io_writel(phy->eud_reg, 0x0);
|
||||
phy->re_enable_eud = true;
|
||||
} else {
|
||||
ret = msm_hsphy_enable_power(phy, true);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = msm_hsphy_enable_power(phy, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msm_hsphy_enable_clocks(phy, true);
|
||||
|
||||
msm_hsphy_reset(phy);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_CFG0,
|
||||
UTMI_PHY_CMN_CTRL_OVERRIDE_EN,
|
||||
UTMI_PHY_CMN_CTRL_OVERRIDE_EN);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
|
||||
POR, POR);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,
|
||||
FSEL_MASK, 0);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
|
||||
PLLBTUNE, PLLBTUNE);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_REFCLK_CTRL,
|
||||
REFCLK_SEL_MASK, REFCLK_SEL_DEFAULT);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
|
||||
VBUSVLDEXTSEL0, VBUSVLDEXTSEL0);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL1,
|
||||
VBUSVLDEXT0, VBUSVLDEXT0);
|
||||
|
||||
/* set parameter ovrride if needed */
|
||||
if (phy->param_override_seq)
|
||||
hsusb_phy_write_seq(phy->base, phy->param_override_seq,
|
||||
phy->param_override_seq_cnt, 0);
|
||||
|
||||
if (phy->pre_emphasis) {
|
||||
u8 val = TXPREEMPAMPTUNE0(phy->pre_emphasis) &
|
||||
TXPREEMPAMPTUNE0_MASK;
|
||||
if (val)
|
||||
msm_usb_write_readback(phy->base,
|
||||
USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X1,
|
||||
TXPREEMPAMPTUNE0_MASK, val);
|
||||
}
|
||||
|
||||
if (phy->txvref_tune0) {
|
||||
u8 val = phy->txvref_tune0 & TXVREFTUNE0_MASK;
|
||||
|
||||
msm_usb_write_readback(phy->base,
|
||||
USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X1,
|
||||
TXVREFTUNE0_MASK, val);
|
||||
}
|
||||
|
||||
if (phy->param_ovrd0) {
|
||||
msm_usb_write_readback(phy->base,
|
||||
USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X0,
|
||||
PARAM_OVRD_MASK, phy->param_ovrd0);
|
||||
}
|
||||
|
||||
if (phy->param_ovrd1) {
|
||||
msm_usb_write_readback(phy->base,
|
||||
USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X1,
|
||||
PARAM_OVRD_MASK, phy->param_ovrd1);
|
||||
}
|
||||
|
||||
if (phy->param_ovrd2) {
|
||||
msm_usb_write_readback(phy->base,
|
||||
USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X2,
|
||||
PARAM_OVRD_MASK, phy->param_ovrd2);
|
||||
}
|
||||
|
||||
if (phy->param_ovrd3) {
|
||||
msm_usb_write_readback(phy->base,
|
||||
USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X3,
|
||||
PARAM_OVRD_MASK, phy->param_ovrd3);
|
||||
}
|
||||
|
||||
dev_dbg(uphy->dev, "x0:%08x x1:%08x x2:%08x x3:%08x\n",
|
||||
readl_relaxed(phy->base + USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X0),
|
||||
readl_relaxed(phy->base + USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X1),
|
||||
readl_relaxed(phy->base + USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X2),
|
||||
readl_relaxed(phy->base + USB2PHY_USB_PHY_PARAMETER_OVERRIDE_X3));
|
||||
|
||||
if (phy->phy_rcal_reg) {
|
||||
rcal_code = readl_relaxed(phy->phy_rcal_reg) & phy->rcal_mask;
|
||||
|
||||
dev_dbg(uphy->dev, "rcal_mask:%08x reg:%pK code:%08x\n",
|
||||
phy->rcal_mask, phy->phy_rcal_reg, rcal_code);
|
||||
}
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2,
|
||||
VREGBYPASS, VREGBYPASS);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
|
||||
USB2_SUSPEND_N_SEL | USB2_SUSPEND_N,
|
||||
USB2_SUSPEND_N_SEL | USB2_SUSPEND_N);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
|
||||
SLEEPM, SLEEPM);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,
|
||||
SIDDQ, 0);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
|
||||
POR, 0);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
|
||||
USB2_SUSPEND_N_SEL, 0);
|
||||
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_CFG0,
|
||||
UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_hsphy_set_suspend(struct usb_phy *uphy, int suspend)
|
||||
{
|
||||
struct msm_hsphy *phy = container_of(uphy, struct msm_hsphy, phy);
|
||||
|
||||
if (phy->suspended && suspend) {
|
||||
if (phy->phy.flags & PHY_SUS_OVERRIDE)
|
||||
goto suspend;
|
||||
|
||||
dev_dbg(uphy->dev, "%s: USB PHY is already suspended\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
suspend:
|
||||
if (suspend) { /* Bus suspend */
|
||||
if (phy->cable_connected) {
|
||||
/* Enable auto-resume functionality during host mode
|
||||
* bus suspend with some FS/HS peripheral connected.
|
||||
*/
|
||||
if ((phy->phy.flags & PHY_HOST_MODE) &&
|
||||
(phy->phy.flags & PHY_HSFS_MODE)) {
|
||||
/* Enable auto-resume functionality by pulsing
|
||||
* signal
|
||||
*/
|
||||
msm_usb_write_readback(phy->base,
|
||||
USB2_PHY_USB_PHY_HS_PHY_CTRL2,
|
||||
USB2_AUTO_RESUME, USB2_AUTO_RESUME);
|
||||
usleep_range(500, 1000);
|
||||
msm_usb_write_readback(phy->base,
|
||||
USB2_PHY_USB_PHY_HS_PHY_CTRL2,
|
||||
USB2_AUTO_RESUME, 0);
|
||||
}
|
||||
msm_hsphy_enable_clocks(phy, false);
|
||||
} else {/* Cable disconnect */
|
||||
mutex_lock(&phy->phy_lock);
|
||||
dev_dbg(uphy->dev, "phy->flags:0x%x\n", phy->phy.flags);
|
||||
if (phy->re_enable_eud) {
|
||||
dev_dbg(uphy->dev, "re-enabling EUD\n");
|
||||
qcom_scm_io_writel(phy->eud_reg, 0x1);
|
||||
phy->re_enable_eud = false;
|
||||
}
|
||||
|
||||
if (!phy->dpdm_enable) {
|
||||
if (!(phy->phy.flags & EUD_SPOOF_DISCONNECT)) {
|
||||
dev_dbg(uphy->dev, "turning off clocks/ldo\n");
|
||||
msm_usb_write_readback(phy->base,
|
||||
USB2_PHY_USB_PHY_PWRDOWN_CTRL,
|
||||
PWRDOWN_B, 0);
|
||||
msm_hsphy_enable_clocks(phy, false);
|
||||
msm_hsphy_enable_power(phy, false);
|
||||
}
|
||||
} else {
|
||||
dev_dbg(uphy->dev, "dpdm reg still active. Keep clocks/ldo ON\n");
|
||||
}
|
||||
mutex_unlock(&phy->phy_lock);
|
||||
}
|
||||
phy->suspended = true;
|
||||
} else { /* Bus resume and cable connect */
|
||||
msm_hsphy_enable_clocks(phy, true);
|
||||
phy->suspended = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_hsphy_notify_connect(struct usb_phy *uphy,
|
||||
enum usb_device_speed speed)
|
||||
{
|
||||
struct msm_hsphy *phy = container_of(uphy, struct msm_hsphy, phy);
|
||||
|
||||
phy->cable_connected = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_hsphy_notify_disconnect(struct usb_phy *uphy,
|
||||
enum usb_device_speed speed)
|
||||
{
|
||||
struct msm_hsphy *phy = container_of(uphy, struct msm_hsphy, phy);
|
||||
|
||||
phy->cable_connected = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_hsphy_vbus_draw_work(struct work_struct *w)
|
||||
{
|
||||
struct msm_hsphy *phy = container_of(w, struct msm_hsphy,
|
||||
vbus_draw_work);
|
||||
union power_supply_propval val = {0};
|
||||
int ret;
|
||||
|
||||
if (!phy->usb_psy) {
|
||||
phy->usb_psy = power_supply_get_by_name("usb");
|
||||
if (!phy->usb_psy) {
|
||||
dev_err(phy->phy.dev, "Could not get usb psy\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(phy->phy.dev, "Avail curr from USB = %u\n", phy->vbus_draw);
|
||||
|
||||
/* Set max current limit in uA */
|
||||
val.intval = 1000 * phy->vbus_draw;
|
||||
ret = power_supply_set_property(phy->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
|
||||
if (ret) {
|
||||
dev_dbg(phy->phy.dev, "Error (%d) setting input current limit\n", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int msm_hsphy_set_power(struct usb_phy *uphy, unsigned int mA)
|
||||
{
|
||||
struct msm_hsphy *phy = container_of(uphy, struct msm_hsphy, phy);
|
||||
|
||||
phy->vbus_draw = mA;
|
||||
schedule_work(&phy->vbus_draw_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_hsphy_dpdm_regulator_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct msm_hsphy *phy = rdev_get_drvdata(rdev);
|
||||
|
||||
dev_dbg(phy->phy.dev, "%s dpdm_enable:%d\n",
|
||||
__func__, phy->dpdm_enable);
|
||||
|
||||
if (phy->eud_enable_reg && readl_relaxed(phy->eud_enable_reg)) {
|
||||
dev_err(phy->phy.dev, "eud is enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_lock(&phy->phy_lock);
|
||||
if (!phy->dpdm_enable) {
|
||||
ret = msm_hsphy_enable_power(phy, true);
|
||||
if (ret) {
|
||||
mutex_unlock(&phy->phy_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msm_hsphy_enable_clocks(phy, true);
|
||||
|
||||
msm_hsphy_reset(phy);
|
||||
|
||||
/*
|
||||
* For PMIC charger detection, place PHY in UTMI non-driving
|
||||
* mode which leaves Dp and Dm lines in high-Z state.
|
||||
*/
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
|
||||
USB2_SUSPEND_N_SEL | USB2_SUSPEND_N,
|
||||
USB2_SUSPEND_N_SEL | USB2_SUSPEND_N);
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
|
||||
OPMODE_MASK, OPMODE_NONDRIVING);
|
||||
msm_usb_write_readback(phy->base, USB2_PHY_USB_PHY_CFG0,
|
||||
UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN,
|
||||
UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN);
|
||||
|
||||
phy->dpdm_enable = true;
|
||||
}
|
||||
mutex_unlock(&phy->phy_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_hsphy_dpdm_regulator_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct msm_hsphy *phy = rdev_get_drvdata(rdev);
|
||||
|
||||
dev_dbg(phy->phy.dev, "%s dpdm_enable:%d\n",
|
||||
__func__, phy->dpdm_enable);
|
||||
|
||||
mutex_lock(&phy->phy_lock);
|
||||
if (phy->dpdm_enable) {
|
||||
if (!phy->cable_connected) {
|
||||
msm_hsphy_enable_clocks(phy, false);
|
||||
ret = msm_hsphy_enable_power(phy, false);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&phy->phy_lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
phy->dpdm_enable = false;
|
||||
}
|
||||
mutex_unlock(&phy->phy_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_hsphy_dpdm_regulator_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct msm_hsphy *phy = rdev_get_drvdata(rdev);
|
||||
|
||||
dev_dbg(phy->phy.dev, "%s dpdm_enable:%d\n",
|
||||
__func__, phy->dpdm_enable);
|
||||
|
||||
return phy->dpdm_enable;
|
||||
}
|
||||
|
||||
static const struct regulator_ops msm_hsphy_dpdm_regulator_ops = {
|
||||
.enable = msm_hsphy_dpdm_regulator_enable,
|
||||
.disable = msm_hsphy_dpdm_regulator_disable,
|
||||
.is_enabled = msm_hsphy_dpdm_regulator_is_enabled,
|
||||
};
|
||||
|
||||
static int msm_hsphy_regulator_init(struct msm_hsphy *phy)
|
||||
{
|
||||
struct device *dev = phy->phy.dev;
|
||||
struct regulator_config cfg = {};
|
||||
struct regulator_init_data *init_data;
|
||||
|
||||
init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL);
|
||||
if (!init_data)
|
||||
return -ENOMEM;
|
||||
|
||||
init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_STATUS;
|
||||
phy->dpdm_rdesc.owner = THIS_MODULE;
|
||||
phy->dpdm_rdesc.type = REGULATOR_VOLTAGE;
|
||||
phy->dpdm_rdesc.ops = &msm_hsphy_dpdm_regulator_ops;
|
||||
phy->dpdm_rdesc.name = kbasename(dev->of_node->full_name);
|
||||
|
||||
cfg.dev = dev;
|
||||
cfg.init_data = init_data;
|
||||
cfg.driver_data = phy;
|
||||
cfg.of_node = dev->of_node;
|
||||
|
||||
phy->dpdm_rdev = devm_regulator_register(dev, &phy->dpdm_rdesc, &cfg);
|
||||
return PTR_ERR_OR_ZERO(phy->dpdm_rdev);
|
||||
}
|
||||
|
||||
static void msm_hsphy_create_debugfs(struct msm_hsphy *phy)
|
||||
{
|
||||
phy->root = debugfs_create_dir(dev_name(phy->phy.dev), NULL);
|
||||
debugfs_create_x8("pre_emphasis", 0644, phy->root, &phy->pre_emphasis);
|
||||
debugfs_create_x8("txvref_tune0", 0644, phy->root, &phy->txvref_tune0);
|
||||
debugfs_create_x8("param_ovrd0", 0644, phy->root, &phy->param_ovrd0);
|
||||
debugfs_create_x8("param_ovrd1", 0644, phy->root, &phy->param_ovrd1);
|
||||
debugfs_create_x8("param_ovrd2", 0644, phy->root, &phy->param_ovrd2);
|
||||
debugfs_create_x8("param_ovrd3", 0644, phy->root, &phy->param_ovrd3);
|
||||
}
|
||||
|
||||
static int msm_hsphy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_hsphy *phy;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int ret = 0;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy) {
|
||||
ret = -ENOMEM;
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
phy->phy.dev = dev;
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"hsusb_phy_base");
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory base resource\n");
|
||||
ret = -ENODEV;
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
phy->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(phy->base)) {
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
ret = -ENODEV;
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"phy_rcal_reg");
|
||||
if (res) {
|
||||
phy->phy_rcal_reg = devm_ioremap(dev,
|
||||
res->start, resource_size(res));
|
||||
if (IS_ERR(phy->phy_rcal_reg)) {
|
||||
dev_err(dev, "couldn't ioremap phy_rcal_reg\n");
|
||||
phy->phy_rcal_reg = NULL;
|
||||
}
|
||||
if (of_property_read_u32(dev->of_node,
|
||||
"qcom,rcal-mask", &phy->rcal_mask)) {
|
||||
dev_err(dev, "unable to read phy rcal mask\n");
|
||||
phy->phy_rcal_reg = NULL;
|
||||
}
|
||||
dev_dbg(dev, "rcal_mask:%08x reg:%pK\n", phy->rcal_mask,
|
||||
phy->phy_rcal_reg);
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"eud_enable_reg");
|
||||
if (res) {
|
||||
phy->eud_enable_reg = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(phy->eud_enable_reg)) {
|
||||
dev_err(dev, "err getting eud_enable_reg address\n");
|
||||
return PTR_ERR(phy->eud_enable_reg);
|
||||
}
|
||||
phy->eud_reg = res->start;
|
||||
}
|
||||
|
||||
/* ref_clk_src is needed irrespective of SE_CLK or DIFF_CLK usage */
|
||||
phy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
|
||||
if (IS_ERR(phy->ref_clk_src)) {
|
||||
dev_dbg(dev, "clk get failed for ref_clk_src\n");
|
||||
ret = PTR_ERR(phy->ref_clk_src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (of_property_match_string(pdev->dev.of_node,
|
||||
"clock-names", "cfg_ahb_clk") >= 0) {
|
||||
phy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk");
|
||||
if (IS_ERR(phy->cfg_ahb_clk)) {
|
||||
ret = PTR_ERR(phy->cfg_ahb_clk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev,
|
||||
"clk get failed for cfg_ahb_clk ret %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
phy->phy_reset = devm_reset_control_get(dev, "phy_reset");
|
||||
if (IS_ERR(phy->phy_reset))
|
||||
return PTR_ERR(phy->phy_reset);
|
||||
|
||||
phy->param_override_seq_cnt = of_property_count_elems_of_size(
|
||||
dev->of_node,
|
||||
"qcom,param-override-seq",
|
||||
sizeof(*phy->param_override_seq));
|
||||
if (phy->param_override_seq_cnt > 0) {
|
||||
phy->param_override_seq = devm_kcalloc(dev,
|
||||
phy->param_override_seq_cnt,
|
||||
sizeof(*phy->param_override_seq),
|
||||
GFP_KERNEL);
|
||||
if (!phy->param_override_seq)
|
||||
return -ENOMEM;
|
||||
|
||||
if (phy->param_override_seq_cnt % 2) {
|
||||
dev_err(dev, "invalid param_override_seq_len\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(dev->of_node,
|
||||
"qcom,param-override-seq",
|
||||
phy->param_override_seq,
|
||||
phy->param_override_seq_cnt);
|
||||
if (ret) {
|
||||
dev_err(dev, "qcom,param-override-seq read failed %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level",
|
||||
(u32 *) phy->vdd_levels,
|
||||
ARRAY_SIZE(phy->vdd_levels));
|
||||
if (ret) {
|
||||
dev_err(dev, "error reading qcom,vdd-voltage-level property\n");
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
|
||||
phy->vdd = devm_regulator_get(dev, "vdd");
|
||||
if (IS_ERR(phy->vdd)) {
|
||||
dev_err(dev, "unable to get vdd supply\n");
|
||||
ret = PTR_ERR(phy->vdd);
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
phy->vdda33 = devm_regulator_get(dev, "vdda33");
|
||||
if (IS_ERR(phy->vdda33)) {
|
||||
dev_err(dev, "unable to get vdda33 supply\n");
|
||||
ret = PTR_ERR(phy->vdda33);
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
phy->vdda18 = devm_regulator_get(dev, "vdda18");
|
||||
if (IS_ERR(phy->vdda18)) {
|
||||
dev_err(dev, "unable to get vdda18 supply\n");
|
||||
ret = PTR_ERR(phy->vdda18);
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
mutex_init(&phy->phy_lock);
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
phy->phy.init = msm_hsphy_init;
|
||||
phy->phy.set_suspend = msm_hsphy_set_suspend;
|
||||
phy->phy.notify_connect = msm_hsphy_notify_connect;
|
||||
phy->phy.notify_disconnect = msm_hsphy_notify_disconnect;
|
||||
phy->phy.set_power = msm_hsphy_set_power;
|
||||
phy->phy.type = USB_PHY_TYPE_USB2;
|
||||
|
||||
ret = usb_add_phy_dev(&phy->phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = msm_hsphy_regulator_init(phy);
|
||||
if (ret) {
|
||||
usb_remove_phy(&phy->phy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
INIT_WORK(&phy->vbus_draw_work, msm_hsphy_vbus_draw_work);
|
||||
msm_hsphy_create_debugfs(phy);
|
||||
|
||||
/*
|
||||
* EUD may be enable in boot loader and to keep EUD session alive across
|
||||
* kernel boot till USB phy driver is initialized based on cable status,
|
||||
* keep LDOs on here.
|
||||
*/
|
||||
if (phy->eud_enable_reg && readl_relaxed(phy->eud_enable_reg))
|
||||
msm_hsphy_enable_power(phy, true);
|
||||
return 0;
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_hsphy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_hsphy *phy = platform_get_drvdata(pdev);
|
||||
|
||||
if (!phy)
|
||||
return 0;
|
||||
|
||||
if (phy->usb_psy)
|
||||
power_supply_put(phy->usb_psy);
|
||||
|
||||
debugfs_remove_recursive(phy->root);
|
||||
|
||||
usb_remove_phy(&phy->phy);
|
||||
clk_disable_unprepare(phy->ref_clk_src);
|
||||
|
||||
msm_hsphy_enable_clocks(phy, false);
|
||||
msm_hsphy_enable_power(phy, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id msm_usb_id_table[] = {
|
||||
{
|
||||
.compatible = "qcom,usb-hsphy-snps-femto",
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, msm_usb_id_table);
|
||||
|
||||
static struct platform_driver msm_hsphy_driver = {
|
||||
.probe = msm_hsphy_probe,
|
||||
.remove = msm_hsphy_remove,
|
||||
.driver = {
|
||||
.name = "msm-usb-hsphy",
|
||||
.of_match_table = of_match_ptr(msm_usb_id_table),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(msm_hsphy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("MSM USB HS PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1169
drivers/usb/phy/phy-msm-ssusb-qmp.c
Normal file
1169
drivers/usb/phy/phy-msm-ssusb-qmp.c
Normal file
File diff suppressed because it is too large
Load Diff
188
drivers/usb/phy/phy-qcom-emu.c
Normal file
188
drivers/usb/phy/phy-qcom-emu.c
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2014-2018, 2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
/* QSCRATCH registers */
|
||||
#define HS_PHY_CTRL_REG 0x10
|
||||
#define SW_SESSVLD_SEL BIT(28)
|
||||
|
||||
/* HSPHY registers */
|
||||
#define HS2_LOCAL_RESET_REG_ADDR 0x04
|
||||
#define HS2_CLK_STATUS_ADDR 0x10
|
||||
#define HS2_CLK_STATUS_SEL_ADDR 0x14
|
||||
#define HS2_USB30_CTRL_ADDR 0x34
|
||||
#define HS2_USB30_PHY_POWER_OFF BIT(25)
|
||||
|
||||
struct qcusb_emu_phy {
|
||||
struct usb_phy phy;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
void __iomem *qscratch_base;
|
||||
int *emu_init_seq;
|
||||
int emu_init_seq_len;
|
||||
};
|
||||
|
||||
static int qcusb_emu_phy_init(struct usb_phy *phy)
|
||||
{
|
||||
struct qcusb_emu_phy *qphy = container_of(phy,
|
||||
struct qcusb_emu_phy, phy);
|
||||
u32 tmp;
|
||||
int i;
|
||||
|
||||
/* reset everything */
|
||||
writel_relaxed(0xffffffff, qphy->base + HS2_LOCAL_RESET_REG_ADDR);
|
||||
usleep_range(10000, 12000);
|
||||
|
||||
/* power down HS phy */
|
||||
tmp = readl_relaxed(qphy->base + HS2_USB30_CTRL_ADDR) |
|
||||
HS2_USB30_PHY_POWER_OFF;
|
||||
writel_relaxed(tmp, qphy->base + HS2_USB30_CTRL_ADDR);
|
||||
usleep_range(10000, 12000);
|
||||
|
||||
/* power up HS phy */
|
||||
tmp = readl_relaxed(qphy->base + HS2_USB30_CTRL_ADDR) &
|
||||
(~HS2_USB30_PHY_POWER_OFF);
|
||||
writel_relaxed(tmp, qphy->base + HS2_USB30_CTRL_ADDR);
|
||||
usleep_range(10000, 12000);
|
||||
|
||||
writel_relaxed(0xfffffff3, qphy->base + HS2_LOCAL_RESET_REG_ADDR);
|
||||
usleep_range(10000, 12000);
|
||||
|
||||
/* put phy out of reset */
|
||||
writel_relaxed(0xfffffff0, qphy->base + HS2_LOCAL_RESET_REG_ADDR);
|
||||
usleep_range(10000, 12000);
|
||||
|
||||
/* selection of HS phy clock MMCM value */
|
||||
for (i = 0; i < qphy->emu_init_seq_len; i = i+2) {
|
||||
dev_dbg(phy->dev, "write 0x%02x to 0x%02x\n",
|
||||
qphy->emu_init_seq[i], qphy->emu_init_seq[i+1]);
|
||||
writel_relaxed(qphy->emu_init_seq[i],
|
||||
qphy->base + qphy->emu_init_seq[i+1]);
|
||||
/* 10ms to ensure write propagates across bus */
|
||||
usleep_range(10000, 12000);
|
||||
}
|
||||
|
||||
/* clear other reset */
|
||||
writel_relaxed(0x0, qphy->base + HS2_LOCAL_RESET_REG_ADDR);
|
||||
usleep_range(10000, 12000);
|
||||
|
||||
/* clock select to read UTMI/ULPI clock */
|
||||
writel_relaxed(0x9, qphy->base + HS2_CLK_STATUS_SEL_ADDR);
|
||||
usleep_range(10000, 12000);
|
||||
dev_info(phy->dev, "PHY UTMI/ULPI CLK frequency:%d MHz\n",
|
||||
(readl_relaxed(qphy->base + HS2_CLK_STATUS_ADDR) / 1000));
|
||||
|
||||
if (qphy->qscratch_base) {
|
||||
/* Use UTMI VBUS signal from HW */
|
||||
tmp = readl_relaxed(qphy->qscratch_base + HS_PHY_CTRL_REG);
|
||||
tmp &= ~SW_SESSVLD_SEL;
|
||||
writel_relaxed(tmp, qphy->qscratch_base + HS_PHY_CTRL_REG);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcusb_emu_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct qcusb_emu_phy *qphy;
|
||||
struct resource *res;
|
||||
int ret, size;
|
||||
|
||||
qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
|
||||
if (!qphy)
|
||||
return -ENOMEM;
|
||||
|
||||
qphy->phy.dev = dev;
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
qphy->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(qphy->base))
|
||||
return PTR_ERR(qphy->base);
|
||||
|
||||
of_get_property(dev->of_node, "qcom,emu-init-seq", &size);
|
||||
if (!size) {
|
||||
dev_err(dev, "emu-init-seq not specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qphy->emu_init_seq = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||
if (!qphy->emu_init_seq)
|
||||
return -ENOMEM;
|
||||
|
||||
qphy->emu_init_seq_len = (size / sizeof(*qphy->emu_init_seq));
|
||||
if (qphy->emu_init_seq_len % 2) {
|
||||
dev_err(dev, "invalid emu_init_seq_len, must be in <data,addr> pairs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(dev->of_node, "qcom,emu-init-seq",
|
||||
qphy->emu_init_seq, qphy->emu_init_seq_len);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not read emu-init-seq, returned %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, qphy);
|
||||
|
||||
qphy->phy.label = "qcom-usb-emu-phy";
|
||||
qphy->phy.init = qcusb_emu_phy_init;
|
||||
qphy->phy.type = USB_PHY_TYPE_USB2;
|
||||
|
||||
ret = usb_add_phy_dev(&qphy->phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"qscratch_base");
|
||||
if (res) {
|
||||
qphy->qscratch_base = devm_ioremap(dev, res->start,
|
||||
resource_size(res));
|
||||
if (IS_ERR(qphy->qscratch_base)) {
|
||||
dev_dbg(dev, "error mapping qscratch\n");
|
||||
qphy->qscratch_base = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcusb_emu_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcusb_emu_phy *qcphy = platform_get_drvdata(pdev);
|
||||
|
||||
usb_remove_phy(&qcphy->phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id emu_phy_dt_ids[] = {
|
||||
{ .compatible = "qcom,usb-emu-phy" },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, emu_phy_dt_ids);
|
||||
|
||||
static struct platform_driver qcusb_emu_phy_driver = {
|
||||
.probe = qcusb_emu_phy_probe,
|
||||
.remove = qcusb_emu_phy_remove,
|
||||
.driver = {
|
||||
.name = "usb_emu_phy",
|
||||
.of_match_table = emu_phy_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(qcusb_emu_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. USB Emulation PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
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 */
|
||||
|
|
@ -8,3 +8,6 @@ stub-regulator.ko
|
|||
phy-qcom-ufs.ko
|
||||
phy-qcom-ufs-qrbtc-sdm845.ko
|
||||
ufs_qcom.ko
|
||||
dwc3-msm.ko
|
||||
phy-qcom-emu.ko
|
||||
phy-generic.ko
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user