armbian-build/patch/kernel/archive/rockchip64-6.18/rk3399-usbc-phy-rockchip-na...

245 lines
8.0 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
Date: Mon, 17 Mar 2025 10:57:59 +0100
Subject: phy: rockchip: naneng: Add fallback for old DTs
See https://lore.kernel.org/lkml/20250106070000.605284-1-amadeus@jmu.edu.cn/
Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
drivers/usb/dwc3/core.c | 46 +++++++++-
drivers/usb/dwc3/core.h | 12 +++
drivers/usb/dwc3/drd.c | 34 ++++---
3 files changed, 77 insertions(+), 15 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 111111111111..222222222222 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -153,7 +153,7 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
}
reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
- reg |= DWC3_GCTL_PRTCAPDIR(mode);
+ reg |= DWC3_GCTL_PRTCAPDIR(mode & DWC3_GCTL_PRTCAP_OTG);
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
dwc->current_dr_role = mode;
@@ -193,6 +193,7 @@ static void __dwc3_set_mode(struct work_struct *work)
dwc3_host_exit(dwc);
break;
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
dwc3_gadget_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
break;
@@ -212,12 +213,43 @@ static void __dwc3_set_mode(struct work_struct *work)
* Only perform GCTL.CoreSoftReset when there's DRD role switching.
*/
if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
- DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
+ DWC3_VER_IS_PRIOR(DWC31, 190A) || dwc->usb3_phy_reset_quirk) &&
desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
+ /*
+ * RK3399 TypeC PHY needs to be powered off and powered on again
+ * for it to apply the correct Type-C plug orientation setting
+ * and reconfigure itself.
+ *
+ * For that purpose we observe complete USB disconnect via
+ * extcon in drd.c and pass it to __dwc3_set_mode as
+ * desired_dr_role == 0.
+ *
+ * We thus handle transitions between three states of
+ * desired_dr_role here:
+ *
+ * - DWC3_GCTL_PRTCAP_HOST
+ * - DWC3_GCTL_PRTCAP_DEVICE
+ * - DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED - almost equivalent to
+ * DWC3_GCTL_PRTCAP_DEVICE, present only to distinguish
+ * disconnected state, and so that set_mode is called when
+ * user plugs in the device to the host.
+ */
+ if (dwc->usb3_phy_powered && dwc->usb3_phy_reset_quirk)
+ for (int j = 0; j < dwc->num_usb3_ports; j++)
+ phy_power_off(dwc->usb3_generic_phy[j]);
+
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg |= DWC3_GCTL_CORESOFTRESET;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ if (dwc->usb3_phy_reset_quirk) {
+ for (int j = 0; j < dwc->num_usb3_ports; j++) {
+ ret = phy_power_on(dwc->usb3_generic_phy[j]);
+ //XXX: bleh
+ dwc->usb3_phy_powered = ret >= 0;
+ }
+ }
+
/*
* Wait for internal clocks to synchronized. DWC_usb31 and
* DWC_usb32 may need at least 50ms (less for DWC_usb3). To
@@ -259,6 +291,7 @@ static void __dwc3_set_mode(struct work_struct *work)
}
break;
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
dwc3_core_soft_reset(dwc);
dwc3_event_buffers_setup(dwc);
@@ -1846,6 +1879,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->dis_split_quirk = device_property_read_bool(dev,
"snps,dis-split-quirk");
+ dwc->usb3_phy_reset_quirk = device_property_read_bool(dev,
+ "snps,usb3-phy-reset-quirk");
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
@@ -2442,6 +2477,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
if (pm_runtime_suspended(dwc->dev))
break;
ret = dwc3_gadget_suspend(dwc);
@@ -2506,11 +2542,12 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;
- dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
+ dwc3_set_prtcap(dwc, dwc->current_dr_role, true);
dwc3_gadget_resume(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
@@ -2574,6 +2611,7 @@ static int dwc3_runtime_checks(struct dwc3 *dwc)
{
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
if (dwc->connected)
return -EBUSY;
break;
@@ -2612,6 +2650,7 @@ int dwc3_runtime_resume(struct dwc3 *dwc)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
if (dwc->pending_events) {
pm_runtime_put(dev);
dwc->pending_events = false;
@@ -2636,6 +2675,7 @@ int dwc3_runtime_idle(struct dwc3 *dwc)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
if (dwc3_runtime_checks(dwc))
return -EBUSY;
break;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 111111111111..222222222222 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -264,6 +264,12 @@
#define DWC3_GCTL_PRTCAP_HOST 1
#define DWC3_GCTL_PRTCAP_DEVICE 2
#define DWC3_GCTL_PRTCAP_OTG 3
+/* This is not a real register value, but a special state used for
+ * current_dr_role to mean DWC3_GCTL_PRTCAP_DEVICE in disconnected
+ * state. Value is chosen so that masking with register width
+ * produces DWC3_GCTL_PRTCAP_DEVICE value.
+ */
+#define DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED 6
#define DWC3_GCTL_CORESOFTRESET BIT(11)
#define DWC3_GCTL_SOFITPSYNC BIT(10)
@@ -1164,6 +1170,10 @@ struct dwc3_glue_ops {
* @sys_wakeup: set if the device may do system wakeup.
* @wakeup_configured: set if the device is configured for remote wakeup.
* @suspended: set to track suspend event due to U3/L2.
+ * @usb3_phy_reset_quirk: set to power cycle the USB3 PHY during mode
+ * changes. Useful on RK3399 that needs this
+ * to apply Type-C orientation changes in
+ * Type-C phy driver.
* @susphy_state: state of DWC3_GUSB2PHYCFG_SUSPHY + DWC3_GUSB3PIPECTL_SUSPHY
* before PM suspend.
* @imod_interval: set the interrupt moderation interval in 250ns
@@ -1406,6 +1416,8 @@ struct dwc3 {
unsigned suspended:1;
unsigned susphy_state:1;
+ unsigned usb3_phy_reset_quirk:1;
+
u16 imod_interval;
int max_cfg_eps;
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 111111111111..222222222222 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -417,15 +417,28 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
static void dwc3_drd_update(struct dwc3 *dwc)
{
- int id;
+ u32 mode = DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED;
+ int ret;
if (dwc->edev) {
- id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
- if (id < 0)
- id = 0;
- dwc3_set_mode(dwc, id ?
- DWC3_GCTL_PRTCAP_HOST :
- DWC3_GCTL_PRTCAP_DEVICE);
+ ret = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
+ if (ret > 0)
+ mode = DWC3_GCTL_PRTCAP_HOST;
+
+ if (dwc->usb3_phy_reset_quirk) {
+ /*
+ * With this quirk enabled, we want to pass 0
+ * to dwc3_set_mode to signal no USB connection
+ * state.
+ */
+ ret = extcon_get_state(dwc->edev, EXTCON_USB);
+ if (ret > 0)
+ mode = DWC3_GCTL_PRTCAP_DEVICE;
+ } else {
+ mode = DWC3_GCTL_PRTCAP_DEVICE;
+ }
+
+ dwc3_set_mode(dwc, mode);
}
}
@@ -434,9 +447,7 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
{
struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb);
- dwc3_set_mode(dwc, event ?
- DWC3_GCTL_PRTCAP_HOST :
- DWC3_GCTL_PRTCAP_DEVICE);
+ dwc3_drd_update(dwc);
return NOTIFY_DONE;
}
@@ -548,8 +559,7 @@ int dwc3_drd_init(struct dwc3 *dwc)
if (dwc->edev) {
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
- ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
- &dwc->edev_nb);
+ ret = extcon_register_notifier_all(dwc->edev, &dwc->edev_nb);
if (ret < 0) {
dev_err(dwc->dev, "couldn't register cable notifier\n");
return ret;
--
Armbian