usb: dwc3: msm: support usb3 linear redriver

redriver framework snapshot from msm-5.15
commit 32dbffee2bb3 ("usb: redriver: add nb7vpq904m rework driver")
commit 4465e83b503c ("usb: redriver: add core api").

Change-Id: Ie9b84641fc97f9e45bfd7f7eeeee1eba4bfd64f1
Signed-off-by: Linyu Yuan <quic_linyyuan@quicinc.com>
This commit is contained in:
Linyu Yuan 2022-11-10 10:35:07 +08:00 committed by Gerrit - the friendly Code Review server
parent 043bb02f96
commit 75a57275dd
8 changed files with 1394 additions and 8 deletions

View File

@ -176,4 +176,6 @@ source "drivers/usb/roles/Kconfig"
source "drivers/usb/repeater/Kconfig"
source "drivers/usb/redriver/Kconfig"
endif # USB_SUPPORT

View File

@ -68,3 +68,5 @@ obj-$(CONFIG_TYPEC) += typec/
obj-$(CONFIG_USB_ROLE_SWITCH) += roles/
obj-$(CONFIG_USB_REPEATER) += repeater/
obj-$(CONFIG_USB_REDRIVER) += redriver/

View File

@ -5241,11 +5241,14 @@ static void dwc3_msm_clear_dp_only_params(struct dwc3_msm *mdwc)
mdwc->ss_release_called = false;
mdwc->ss_phy->flags &= ~PHY_DP_MODE;
dwc3_msm_set_max_speed(mdwc, USB_SPEED_UNKNOWN);
usb_redriver_notify_disconnect(mdwc->redriver);
}
static void dwc3_msm_set_dp_only_params(struct dwc3_msm *mdwc)
{
usb_redriver_release_lanes(mdwc->redriver, 4);
usb_redriver_release_lanes(mdwc->redriver, mdwc->ss_phy->flags & PHY_LANE_A ?
ORIENTATION_CC1 : ORIENTATION_CC2, 4);
/* restart USB host mode into high speed */
mdwc->ss_release_called = true;
@ -5306,7 +5309,8 @@ int dwc3_msm_set_dp_mode(struct device *dev, bool dp_connected, int lanes)
mdwc->dp_state = DP_2_LANE;
mdwc->refcnt_dp_usb++;
mutex_unlock(&mdwc->role_switch_mutex);
usb_redriver_release_lanes(mdwc->redriver, 2);
usb_redriver_release_lanes(mdwc->redriver, mdwc->ss_phy->flags & PHY_LANE_A ?
ORIENTATION_CC1 : ORIENTATION_CC2, 2);
pm_runtime_get_sync(&mdwc->dwc3->dev);
mdwc->ss_phy->flags |= PHY_USB_DP_CONCURRENT_MODE;
pm_runtime_put_sync(&mdwc->dwc3->dev);
@ -6200,6 +6204,10 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
dbg_event(0xFF, "StrtHost gync",
atomic_read(&mdwc->dev->power.usage_count));
clk_set_rate(mdwc->core_clk, mdwc->core_clk_rate);
usb_redriver_notify_connect(mdwc->redriver,
mdwc->ss_phy->flags & PHY_LANE_A ?
ORIENTATION_CC1 : ORIENTATION_CC2);
if (dwc3_msm_get_max_speed(mdwc) >= USB_SPEED_SUPER) {
mdwc->ss_phy->flags |= PHY_HOST_MODE;
usb_phy_notify_connect(mdwc->ss_phy,
@ -6313,6 +6321,7 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
USB_SPEED_SUPER);
mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
}
usb_redriver_notify_disconnect(mdwc->redriver);
mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
usb_unregister_notify(&mdwc->host_nb);
@ -6388,6 +6397,10 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on)
dwc3_msm_notify_event(dwc, DWC3_GSI_EVT_BUF_SETUP, 0);
dwc3_override_vbus_status(mdwc, true);
usb_redriver_notify_connect(mdwc->redriver,
mdwc->ss_phy->flags & PHY_LANE_A ?
ORIENTATION_CC1 : ORIENTATION_CC2);
if (dwc3_msm_get_max_speed(mdwc) >= USB_SPEED_SUPER)
usb_phy_notify_connect(mdwc->ss_phy, USB_SPEED_SUPER);
@ -6434,6 +6447,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on)
mdwc->in_device_mode = false;
usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH);
usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER);
usb_redriver_notify_disconnect(mdwc->redriver);
dwc3_msm_notify_event(dwc, DWC3_GSI_EVT_BUF_CLEAR, 0);
dwc3_override_vbus_status(mdwc, false);

View File

@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "USB3 linear redriver"
config USB_REDRIVER
tristate "USB super speed (plus) redriver support"
help
USB super speed (plus) redriver chip used to improve
USB signal which have long path(USB connector <-->
FPC cable <---> USB PHY).
Normally this kind of chip have i2c bus interface.
config USB_REDRIVER_NB7VPQ904M
tristate "NB7VPQ904M USB 3.1 Gen1/Gen2 10Gbps redriver"
select REGMAP_I2C
select USB_REDRIVER
help
NB7VPQ904M come from ON-SEMI company,
it enhance USB super (plus) signal on mobile platform.
it have i2c program interface.
Say Y/M here if you want to support it.
endmenu

View File

@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_USB_REDRIVER) += redriver.o
obj-$(CONFIG_USB_REDRIVER_NB7VPQ904M) += nb7vpq904m.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,204 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* USB Super Speed (Plus) redriver core module
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) "redriver-core: " fmt
#include <linux/module.h>
#include <linux/usb/redriver.h>
static LIST_HEAD(usb_redriver_list);
static DEFINE_SPINLOCK(usb_rediver_lock);
/**
* usb_add_redriver() - register a redriver from a specific chip driver.
* @redriver: redriver allocated by specific chip driver.
*
* Return:
* -EINVAL - if of node not exist
* 0 - if exist, add redriver to global list.
*/
int usb_add_redriver(struct usb_redriver *redriver)
{
struct usb_redriver *iter;
if (!redriver->of_node || redriver->bounded ||
(redriver->has_orientation && !redriver->get_orientation))
return -EINVAL;
spin_lock(&usb_rediver_lock);
list_for_each_entry(iter, &usb_redriver_list, list) {
if (iter == redriver) {
spin_unlock(&usb_rediver_lock);
return -EEXIST;
}
}
pr_debug("add redriver %s\n", of_node_full_name(redriver->of_node));
list_add_tail(&redriver->list, &usb_redriver_list);
spin_unlock(&usb_rediver_lock);
return 0;
}
EXPORT_SYMBOL(usb_add_redriver);
/**
* usb_remove_redriver() - remove a redriver from a specific chip driver.
* @redriver: redriver allocated by specific chip driver.
*
* remove redriver from global list.
* if redriver rmmod, it is better change to default state inside it's driver,
* no unbind operation here.
*
* Return:
* -EINVAL - redriver still used by uppper layer.
* 0 - redriver removed.
*/
int usb_remove_redriver(struct usb_redriver *redriver)
{
spin_lock(&usb_rediver_lock);
if (redriver->bounded) {
spin_unlock(&usb_rediver_lock);
return -EINVAL;
}
pr_debug("remove redriver %s\n", of_node_full_name(redriver->of_node));
list_del(&redriver->list);
spin_unlock(&usb_rediver_lock);
return 0;
}
EXPORT_SYMBOL(usb_remove_redriver);
/**
* usb_get_redriver_by_phandle() - find redriver to be used.
* @np: device node of device which use the redriver
* @phandle_name: phandle name which refer to the redriver
* @index: phandle index which refer to the redriver
*
* Return:
* NULL - if no phandle or redriver device tree status is disabled.
* ERR_PTR(-EPROBE_DEFER) - if redriver is not registered
* if redriver registered, return pointer of it.
*/
struct usb_redriver *usb_get_redriver_by_phandle(const struct device_node *np,
const char *phandle_name, int index)
{
struct usb_redriver *redriver;
struct device_node *node;
bool found = false;
node = of_parse_phandle(np, phandle_name, index);
if (!of_device_is_available(node)) {
of_node_put(node);
return NULL;
}
spin_lock(&usb_rediver_lock);
list_for_each_entry(redriver, &usb_redriver_list, list) {
if (redriver->of_node == node) {
found = true;
break;
}
}
if (!found) {
of_node_put(node);
spin_unlock(&usb_rediver_lock);
return ERR_PTR(-EPROBE_DEFER);
}
pr_debug("get redriver %s\n", of_node_full_name(redriver->of_node));
redriver->bounded = true;
spin_unlock(&usb_rediver_lock);
return redriver;
}
EXPORT_SYMBOL(usb_get_redriver_by_phandle);
/**
* usb_put_redriver() - redriver will not be used.
* @redriver: redriver allocated by specific chip driver.
*
* when user module exit, unbind redriver.
*/
void usb_put_redriver(struct usb_redriver *redriver)
{
if (!redriver)
return;
spin_lock(&usb_rediver_lock);
of_node_put(redriver->of_node);
pr_debug("put redriver %s\n", of_node_full_name(redriver->of_node));
redriver->bounded = false;
spin_unlock(&usb_rediver_lock);
if (redriver->unbind)
redriver->unbind(redriver);
}
EXPORT_SYMBOL(usb_put_redriver);
/* note: following exported symbol can be inlined in header file,
* export here to avoid unexpected CFI(Clang Control Flow Integrity) issue.
*/
void usb_redriver_release_lanes(struct usb_redriver *ur, int ort, int num)
{
if (ur && ur->release_usb_lanes)
ur->release_usb_lanes(ur, ort, num);
}
EXPORT_SYMBOL(usb_redriver_release_lanes);
void usb_redriver_notify_connect(struct usb_redriver *ur, int ort)
{
if (ur && ur->notify_connect)
ur->notify_connect(ur, ort);
}
EXPORT_SYMBOL(usb_redriver_notify_connect);
void usb_redriver_notify_disconnect(struct usb_redriver *ur)
{
if (ur && ur->notify_disconnect)
ur->notify_disconnect(ur);
}
EXPORT_SYMBOL(usb_redriver_notify_disconnect);
int usb_redriver_get_orientation(struct usb_redriver *ur)
{
if (ur && ur->has_orientation)
return ur->get_orientation(ur);
return -EOPNOTSUPP;
}
EXPORT_SYMBOL(usb_redriver_get_orientation);
void usb_redriver_gadget_pullup_enter(struct usb_redriver *ur,
int is_on)
{
if (ur && ur->gadget_pullup_enter)
ur->gadget_pullup_enter(ur, is_on);
}
EXPORT_SYMBOL(usb_redriver_gadget_pullup_enter);
void usb_redriver_gadget_pullup_exit(struct usb_redriver *ur,
int is_on)
{
if (ur && ur->gadget_pullup_exit)
ur->gadget_pullup_exit(ur, is_on);
}
EXPORT_SYMBOL(usb_redriver_gadget_pullup_exit);
void usb_redriver_host_powercycle(struct usb_redriver *ur)
{
if (ur && ur->host_powercycle)
ur->host_powercycle(ur);
}
EXPORT_SYMBOL(usb_redriver_host_powercycle);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("USB Super Speed (Plus) redriver core module");

View File

@ -40,10 +40,10 @@
* @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
* @get_orientation: 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
* @host_powercycle: 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
@ -52,7 +52,7 @@ struct usb_redriver {
struct list_head list;
struct device_node *of_node;
int (*release_usb_lanes)(struct usb_redriver *ur, int num);
int (*release_usb_lanes)(struct usb_redriver *ur, int ort, 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);
@ -73,7 +73,7 @@ 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_release_lanes(struct usb_redriver *ur, int ort, 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);
@ -102,12 +102,12 @@ static inline int usb_remove_redriver(struct usb_redriver *ur)
static inline int usb_redriver_get_orientation(struct usb_redriver *ur)
{
return -1;
return -EOPNOTSUPP;
}
#define usb_put_redriver(ur) do {} while (0)
#define usb_redriver_release_lanes(ur, num) do {} while (0)
#define usb_redriver_release_lanes(ur, ort, 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)