mirror of
https://github.com/armbian/build
synced 2025-09-24 19:47:06 +07:00
rockchip64-6.16: Partial fix of DP alt mode on some rk3399 boards
The old method carried along with board-pbp-add-dp-alt-mode.patch only makes typec work in one(normal) orientation. This patch introduces a proper extcon driver and makes the workaround cleaner, so orientation switch is working. Improvements: - type-c DP on rk3399 works with both orientations - type-c USB 3.0 on rk3399 works with both orientations, with minor issues, see below Caveats: - Powered USB-C hubs may be not recognized, and can be worked around by loading a gadget driver, or manually toggling the mode once for each connection. - Some dual-role devices(phone, tablet) may be not recognized. Affected boards: - TinkerBoard 2/2S - Pinebook Pro - NanoPC T4 - Orange Pi 4 - Orange Pi 4 LTS Tested on tinkerboard 2s. This patch contains other minor fixes for tinker2's device tree, including adding a missing fan node, adding color labels to leds. The 2 patches adding dp support for nanopc t4 and pinebook pro are also updated accordingly. The device trees of Orange Pi 4 / 4 LTS are also updated to match the new implementation.
This commit is contained in:
@@ -2635,6 +2635,7 @@ CONFIG_TYPEC_UCSI=m
|
||||
CONFIG_UCSI_CCG=m
|
||||
CONFIG_TYPEC_TPS6598X=m
|
||||
CONFIG_TYPEC_HD3SS3220=m
|
||||
CONFIG_TYPEC_EXTCON=m
|
||||
CONFIG_TYPEC_MUX_FSA4480=m
|
||||
CONFIG_TYPEC_MUX_PI3USB30532=m
|
||||
CONFIG_TYPEC_DP_ALTMODE=m
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: tonymac32 <tonymckahan@gmail.com>
|
||||
Date: Wed, 17 Feb 2021 00:54:00 -0500
|
||||
Subject: Patching something
|
||||
From: hyx0329 <hyx0329@outlook.com>
|
||||
Date: Mon, 2 Jun 2025 07:59:27 +0000
|
||||
Subject: Enable type-c dp alt mode for nanopc t4 in the device tree
|
||||
|
||||
Signed-off-by: tonymac32 <tonymckahan@gmail.com>
|
||||
Signed-off-by: hyx0329 <hyx0329@outlook.com>
|
||||
---
|
||||
arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts | 96 ++++++++++
|
||||
1 file changed, 96 insertions(+)
|
||||
arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts | 116 ++++++++++
|
||||
1 file changed, 116 insertions(+)
|
||||
|
||||
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts b/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts
|
||||
index 111111111111..222222222222 100644
|
||||
@@ -20,24 +20,34 @@ index 111111111111..222222222222 100644
|
||||
#include "rk3399-nanopi4.dtsi"
|
||||
|
||||
/ {
|
||||
@@ -66,6 +67,12 @@ fan: pwm-fan {
|
||||
@@ -64,6 +65,20 @@ fan: pwm-fan {
|
||||
fan-supply = <&vcc12v0_sys>;
|
||||
pwms = <&pwm1 0 50000 0>;
|
||||
};
|
||||
};
|
||||
|
||||
+&cdn_dp {
|
||||
+ status = "okay";
|
||||
+ extcon = <&fusb0>;
|
||||
+ phys = <&tcphy0_dp>;
|
||||
+
|
||||
+ typec_extcon_bridge: typec-extcon {
|
||||
+ compatible = "linux,typec-extcon-bridge";
|
||||
+ usb-role-switch;
|
||||
+ orientation-switch;
|
||||
+ mode-switch;
|
||||
+ svid = /bits/ 16 <0xff01>;
|
||||
+ };
|
||||
+};
|
||||
+
|
||||
+&cdn_dp {
|
||||
+ status = "okay";
|
||||
+ extcon = <&typec_extcon_bridge>;
|
||||
+ phys = <&tcphy0_dp>;
|
||||
};
|
||||
|
||||
&cpu_thermal {
|
||||
trips {
|
||||
cpu_warm: cpu_warm {
|
||||
@@ -94,6 +101,50 @@ map3 {
|
||||
@@ -94,6 +109,59 @@ map3 {
|
||||
};
|
||||
};
|
||||
|
||||
+&fusb0 {
|
||||
+ usb-role-switch = <&typec_extcon_bridge>;
|
||||
+ extcon = <&typec_extcon_bridge>;
|
||||
+
|
||||
+ connector {
|
||||
+ compatible = "usb-c-connector";
|
||||
@@ -49,8 +59,15 @@ index 111111111111..222222222222 100644
|
||||
+ sink-pdos = <PDO_FIXED(5000, 500, PDO_FIXED_USB_COMM)>;
|
||||
+ op-sink-microwatt = <5000000>;
|
||||
+
|
||||
+ extcon-cables = <1 2 5 6 9 10 12 44>;
|
||||
+ typec-altmodes = <0xff01 1 0x001c0000 1>;
|
||||
+ mode-switch = <&typec_extcon_bridge>;
|
||||
+ orientation-switch = <&typec_extcon_bridge>;
|
||||
+
|
||||
+ altmodes {
|
||||
+ dp {
|
||||
+ svid = /bits/ 16 <0xff01>;
|
||||
+ vdo = <0x1c46>;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ ports {
|
||||
+ #address-cells = <1>;
|
||||
@@ -84,12 +101,12 @@ index 111111111111..222222222222 100644
|
||||
&pcie0 {
|
||||
ep-gpios = <&gpio2 RK_PA4 GPIO_ACTIVE_HIGH>;
|
||||
num-lanes = <4>;
|
||||
@@ -114,12 +165,57 @@ &sdhci {
|
||||
@@ -114,12 +182,60 @@ &sdhci {
|
||||
mmc-hs400-enhanced-strobe;
|
||||
};
|
||||
|
||||
+&tcphy0 {
|
||||
+ extcon = <&fusb0>;
|
||||
+ extcon = <&typec_extcon_bridge>;
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
@@ -110,7 +127,8 @@ index 111111111111..222222222222 100644
|
||||
+};
|
||||
+
|
||||
+&u2phy0 {
|
||||
+ extcon = <&fusb0>;
|
||||
+ extcon = <&typec_extcon_bridge>;
|
||||
+ extcon,ignore-usb; /* let extcon handle role switch */
|
||||
+};
|
||||
+
|
||||
&u2phy0_host {
|
||||
@@ -138,7 +156,9 @@ index 111111111111..222222222222 100644
|
||||
+};
|
||||
+
|
||||
+&usbdrd_dwc3_0 {
|
||||
+ extcon = <&fusb0>;
|
||||
+ dr_mode = "otg"; /* MUST be otg so phy can reset properly */
|
||||
+ extcon = <&typec_extcon_bridge>;
|
||||
+ snps,usb3-phy-reset-quirk;
|
||||
};
|
||||
|
||||
&vcc5v0_sys {
|
||||
|
||||
@@ -1,417 +1,104 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dan Johansen <strit@manjaro.org>
|
||||
Date: Tue, 2 Jun 2020 20:20:29 +0200
|
||||
Subject: add-dp-alt-mode-to-PBP
|
||||
From: hyx0329 <hyx0329@outlook.com>
|
||||
Date: Mon, 2 Jun 2025 07:33:51 +0000
|
||||
Subject: Add dp alt mode to pinebook pro
|
||||
|
||||
Signed-off-by: hyx0329 <hyx0329@outlook.com>
|
||||
---
|
||||
arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | 5 +
|
||||
drivers/phy/rockchip/phy-rockchip-typec.c | 17 ++
|
||||
drivers/usb/typec/altmodes/displayport.c | 52 +++-
|
||||
drivers/usb/typec/tcpm/tcpm.c | 138 +++++++++-
|
||||
4 files changed, 209 insertions(+), 3 deletions(-)
|
||||
arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | 36 +++++++++-
|
||||
1 file changed, 33 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
|
||||
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
|
||||
@@ -421,6 +421,7 @@ edp_out_panel: endpoint@0 {
|
||||
|
||||
&emmc_phy {
|
||||
status = "okay";
|
||||
+ extcon = <&fusb0>;
|
||||
@@ -373,6 +373,14 @@ mains_charger: dc-charger {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&dc_det_pin>;
|
||||
};
|
||||
+
|
||||
+ typec_extcon_bridge: typec-extcon {
|
||||
+ compatible = "linux,typec-extcon-bridge";
|
||||
+ usb-role-switch;
|
||||
+ orientation-switch;
|
||||
+ mode-switch;
|
||||
+ svid = /bits/ 16 <0xff01>;
|
||||
+ };
|
||||
};
|
||||
|
||||
&gpu {
|
||||
@@ -705,6 +706,9 @@ connector {
|
||||
<PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>;
|
||||
try-power-role = "sink";
|
||||
&cpu_b0 {
|
||||
@@ -399,6 +407,12 @@ &cpu_l3 {
|
||||
cpu-supply = <&vdd_cpu_l>;
|
||||
};
|
||||
|
||||
+ extcon-cables = <1 2 5 6 9 10 12 44>;
|
||||
+ typec-altmodes = <0xff01 1 0x001c0000 1>;
|
||||
+&cdn_dp {
|
||||
+ status = "okay";
|
||||
+ extcon = <&typec_extcon_bridge>;
|
||||
+ phys = <&tcphy0_dp>;
|
||||
+};
|
||||
+
|
||||
&edp {
|
||||
force-hpd;
|
||||
pinctrl-names = "default";
|
||||
@@ -692,6 +706,8 @@ fusb0: fusb30x@22 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&fusb0_int_pin>;
|
||||
vbus-supply = <&vbus_typec>;
|
||||
+ usb-role-switch = <&typec_extcon_bridge>;
|
||||
+ extcon = <&typec_extcon_bridge>;
|
||||
|
||||
connector {
|
||||
compatible = "usb-c-connector";
|
||||
@@ -700,10 +716,19 @@ connector {
|
||||
op-sink-microwatt = <1000000>;
|
||||
power-role = "dual";
|
||||
sink-pdos =
|
||||
- <PDO_FIXED(5000, 2500, PDO_FIXED_USB_COMM)>;
|
||||
+ <PDO_FIXED(5000, 2500, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>;
|
||||
source-pdos =
|
||||
- <PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>;
|
||||
+ <PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>;
|
||||
try-power-role = "sink";
|
||||
+ mode-switch = <&typec_extcon_bridge>;
|
||||
+ orientation-switch = <&typec_extcon_bridge>;
|
||||
+
|
||||
+ altmodes {
|
||||
+ dp {
|
||||
+ svid = /bits/ 16 <0xff01>;
|
||||
+ vdo = <0x1c46>;
|
||||
+ };
|
||||
+ };
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@@ -970,6 +974,7 @@ spiflash: flash@0 {
|
||||
@@ -970,6 +995,7 @@ spiflash: flash@0 {
|
||||
};
|
||||
|
||||
&tcphy0 {
|
||||
+ extcon = <&fusb0>;
|
||||
+ extcon = <&typec_extcon_bridge>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-typec.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-typec.c
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/extcon.h>
|
||||
+#include <linux/extcon-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
@@ -1156,6 +1157,22 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
|
||||
dev_err(dev, "Invalid or missing extcon\n");
|
||||
return PTR_ERR(tcphy->extcon);
|
||||
}
|
||||
+ } else {
|
||||
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB,
|
||||
+ EXTCON_PROP_USB_SS);
|
||||
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB_HOST,
|
||||
+ EXTCON_PROP_USB_SS);
|
||||
+ extcon_set_property_capability(tcphy->extcon, EXTCON_DISP_DP,
|
||||
+ EXTCON_PROP_USB_SS);
|
||||
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB,
|
||||
+ EXTCON_PROP_USB_TYPEC_POLARITY);
|
||||
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB_HOST,
|
||||
+ EXTCON_PROP_USB_TYPEC_POLARITY);
|
||||
+ extcon_set_property_capability(tcphy->extcon, EXTCON_DISP_DP,
|
||||
+ EXTCON_PROP_USB_TYPEC_POLARITY);
|
||||
+ extcon_sync(tcphy->extcon, EXTCON_USB);
|
||||
+ extcon_sync(tcphy->extcon, EXTCON_USB_HOST);
|
||||
+ extcon_sync(tcphy->extcon, EXTCON_DISP_DP);
|
||||
}
|
||||
@@ -1003,6 +1029,8 @@ &tsadc {
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/usb/typec/altmodes/displayport.c
|
||||
+++ b/drivers/usb/typec/altmodes/displayport.c
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
&u2phy0 {
|
||||
status = "okay";
|
||||
+ extcon = <&typec_extcon_bridge>;
|
||||
+ extcon,ignore-usb;
|
||||
|
||||
#include <linux/delay.h>
|
||||
+#include <linux/extcon.h>
|
||||
+#include <linux/extcon-provider.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
@@ -74,6 +76,8 @@ struct dp_altmode {
|
||||
struct typec_altmode *plug_prime;
|
||||
u2phy0_otg: otg-port {
|
||||
status = "okay";
|
||||
@@ -1079,7 +1107,9 @@ &usbdrd3_0 {
|
||||
};
|
||||
|
||||
+void dp_altmode_update_extcon(struct dp_altmode *dp, bool disconnect);
|
||||
+
|
||||
static int dp_altmode_notify(struct dp_altmode *dp)
|
||||
{
|
||||
unsigned long conf;
|
||||
@@ -82,7 +86,9 @@ static int dp_altmode_notify(struct dp_altmode *dp)
|
||||
if (dp->data.conf) {
|
||||
state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
|
||||
conf = TYPEC_MODAL_STATE(state);
|
||||
+ dp_altmode_update_extcon(dp, false);
|
||||
} else {
|
||||
+ dp_altmode_update_extcon(dp, true);
|
||||
conf = TYPEC_STATE_USB;
|
||||
}
|
||||
&usbdrd_dwc3_0 {
|
||||
- dr_mode = "host";
|
||||
+ dr_mode = "otg";
|
||||
+ extcon = <&typec_extcon_bridge>;
|
||||
+ snps,usb3-phy-reset-quirk;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
@@ -182,6 +188,40 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
+void dp_altmode_update_extcon(struct dp_altmode *dp, bool disconnect) {
|
||||
+ const struct device *dev = &dp->port->dev;
|
||||
+ struct extcon_dev* edev = NULL;
|
||||
+
|
||||
+ while (dev) {
|
||||
+ edev = extcon_find_edev_by_node(dev->of_node);
|
||||
+ if(!IS_ERR(edev)) {
|
||||
+ break;
|
||||
+ }
|
||||
+ dev = dev->parent;
|
||||
+ }
|
||||
+
|
||||
+ if (IS_ERR_OR_NULL(edev)) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (disconnect || !dp->data.conf) {
|
||||
+ extcon_set_state_sync(edev, EXTCON_DISP_DP, false);
|
||||
+ } else {
|
||||
+ union extcon_property_value extcon_true = { .intval = true };
|
||||
+ extcon_set_state(edev, EXTCON_DISP_DP, true);
|
||||
+ if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf) & DP_PIN_ASSIGN_MULTI_FUNC_MASK) {
|
||||
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
|
||||
+ extcon_set_property(edev, EXTCON_DISP_DP, EXTCON_PROP_USB_SS,
|
||||
+ extcon_true);
|
||||
+ } else {
|
||||
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
|
||||
+ }
|
||||
+ extcon_sync(edev, EXTCON_DISP_DP);
|
||||
+ extcon_set_state_sync(edev, EXTCON_USB, false);
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
+
|
||||
static int dp_altmode_configured(struct dp_altmode *dp)
|
||||
{
|
||||
sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration");
|
||||
@@ -298,6 +338,8 @@ static void dp_altmode_work(struct work_struct *work)
|
||||
case DP_STATE_EXIT:
|
||||
if (typec_altmode_exit(dp->alt))
|
||||
dev_err(&dp->alt->dev, "Exit Mode Failed!\n");
|
||||
+ else
|
||||
+ dp_altmode_update_extcon(dp, true);
|
||||
break;
|
||||
case DP_STATE_EXIT_PRIME:
|
||||
if (typec_cable_altmode_exit(dp->plug_prime, TYPEC_PLUG_SOP_P))
|
||||
@@ -737,8 +779,14 @@ int dp_altmode_probe(struct typec_altmode *alt)
|
||||
if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) &
|
||||
DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) &&
|
||||
!(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) &
|
||||
- DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)))
|
||||
- return -ENODEV;
|
||||
+ DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo))) {
|
||||
+ dev_err(&alt->dev, "No compatible pin configuration found:"\
|
||||
+ "%04lx -> %04lx, %04lx <- %04lx",
|
||||
+ DP_CAP_PIN_ASSIGN_DFP_D(port->vdo), DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo),
|
||||
+ DP_CAP_PIN_ASSIGN_UFP_D(port->vdo), DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo));
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
|
||||
dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
|
||||
if (!dp)
|
||||
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/usb/typec/tcpm/tcpm.c
|
||||
+++ b/drivers/usb/typec/tcpm/tcpm.c
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <linux/completion.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
+#include <linux/extcon-provider.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
@@ -580,6 +581,11 @@ struct tcpm_port {
|
||||
unsigned int message_id_prime;
|
||||
unsigned int rx_msgid_prime;
|
||||
|
||||
+#ifdef CONFIG_EXTCON
|
||||
+ struct extcon_dev *extcon;
|
||||
+ unsigned int *extcon_cables;
|
||||
+#endif
|
||||
+
|
||||
/* Timer deadline values configured at runtime */
|
||||
struct pd_timings timings;
|
||||
|
||||
@@ -983,6 +989,35 @@ static void tcpm_ams_finish(struct tcpm_port *port)
|
||||
port->ams = NONE_AMS;
|
||||
}
|
||||
|
||||
+static void tcpm_update_extcon_data(struct tcpm_port *port, bool attached) {
|
||||
+#ifdef CONFIG_EXTCON
|
||||
+ unsigned int *capability = port->extcon_cables;
|
||||
+ if (port->data_role == TYPEC_HOST) {
|
||||
+ extcon_set_state(port->extcon, EXTCON_USB, false);
|
||||
+ extcon_set_state(port->extcon, EXTCON_USB_HOST, attached);
|
||||
+ } else {
|
||||
+ extcon_set_state(port->extcon, EXTCON_USB, true);
|
||||
+ extcon_set_state(port->extcon, EXTCON_USB_HOST, attached);
|
||||
+ }
|
||||
+ while (*capability != EXTCON_NONE) {
|
||||
+ if (attached) {
|
||||
+ union extcon_property_value val;
|
||||
+ val.intval = (port->polarity == TYPEC_POLARITY_CC2);
|
||||
+ extcon_set_property(port->extcon, *capability,
|
||||
+ EXTCON_PROP_USB_TYPEC_POLARITY, val);
|
||||
+ } else {
|
||||
+ extcon_set_state(port->extcon, *capability, false);
|
||||
+ }
|
||||
+ extcon_sync(port->extcon, *capability);
|
||||
+ capability++;
|
||||
+ }
|
||||
+ tcpm_log(port, "Extcon update (%s): %s, %s",
|
||||
+ attached ? "attached" : "detached",
|
||||
+ port->data_role == TYPEC_HOST ? "host" : "device",
|
||||
+ port->polarity == TYPEC_POLARITY_CC1 ? "normal" : "flipped");
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
static int tcpm_pd_transmit(struct tcpm_port *port,
|
||||
enum tcpm_transmit_type tx_sop_type,
|
||||
const struct pd_message *msg)
|
||||
@@ -1222,6 +1257,8 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached, int state,
|
||||
typec_set_data_role(port->typec_port, data);
|
||||
typec_set_pwr_role(port->typec_port, role);
|
||||
|
||||
+ tcpm_update_extcon_data(port, attached);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1845,7 +1882,7 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt,
|
||||
paltmode->mode = i;
|
||||
paltmode->vdo = p[i];
|
||||
|
||||
- tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
|
||||
+ tcpm_log(port, "Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
|
||||
pmdata->altmodes, paltmode->svid,
|
||||
paltmode->mode, paltmode->vdo);
|
||||
|
||||
@@ -1869,6 +1906,8 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port)
|
||||
tcpm_log(port, "Failed to register partner SVID 0x%04x",
|
||||
modep->altmode_desc[i].svid);
|
||||
altmode = NULL;
|
||||
+ } else {
|
||||
+ tcpm_log(port, "Registered altmode 0x%04x", modep->altmode_desc[i].svid);
|
||||
}
|
||||
port->partner_altmode[i] = altmode;
|
||||
}
|
||||
@@ -2245,11 +2284,13 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
|
||||
modep->svid_index++;
|
||||
if (modep->svid_index < modep->nsvids) {
|
||||
u16 svid = modep->svids[modep->svid_index];
|
||||
+ tcpm_log(port, "More modes available, sending discover");
|
||||
*response_tx_sop_type = TCPC_TX_SOP;
|
||||
response[0] = VDO(svid, 1, svdm_version,
|
||||
CMD_DISCOVER_MODES);
|
||||
rlen = 1;
|
||||
} else if (tcpm_cable_vdm_supported(port)) {
|
||||
+ tcpm_log(port, "Got all patner modes, registering");
|
||||
*response_tx_sop_type = TCPC_TX_SOP_PRIME;
|
||||
response[0] = VDO(USB_SID_PD, 1,
|
||||
typec_get_cable_svdm_version(typec),
|
||||
@@ -4471,6 +4512,7 @@ static void tcpm_typec_disconnect(struct tcpm_port *port)
|
||||
port->cable = NULL;
|
||||
if (port->connected) {
|
||||
if (port->partner) {
|
||||
+ tcpm_update_extcon_data(port, false);
|
||||
typec_partner_set_usb_power_delivery(port->partner, NULL);
|
||||
typec_unregister_partner(port->partner);
|
||||
port->partner = NULL;
|
||||
@@ -4565,6 +4607,8 @@ static void tcpm_detach(struct tcpm_port *port)
|
||||
}
|
||||
|
||||
tcpm_reset_port(port);
|
||||
+
|
||||
+ tcpm_update_extcon_data(port, false);
|
||||
}
|
||||
|
||||
static void tcpm_src_detach(struct tcpm_port *port)
|
||||
@@ -7274,6 +7318,64 @@ static void tcpm_fw_get_timings(struct tcpm_port *port, struct fwnode_handle *fw
|
||||
port->timings.snk_bc12_cmpletion_time = val;
|
||||
}
|
||||
|
||||
+unsigned int default_supported_cables[] = {
|
||||
+ EXTCON_NONE
|
||||
+};
|
||||
+
|
||||
+static int tcpm_fw_get_caps_late(struct tcpm_port *port,
|
||||
+ struct fwnode_handle *fwnode)
|
||||
+{
|
||||
+ int ret, i;
|
||||
+ ret = fwnode_property_count_u32(fwnode, "typec-altmodes");
|
||||
+ if (ret > 0) {
|
||||
+ u32 *props;
|
||||
+ if (ret % 4) {
|
||||
+ dev_err(port->dev, "Length of typec altmode array must be divisible by 4");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ props = devm_kzalloc(port->dev, sizeof(u32) * ret, GFP_KERNEL);
|
||||
+ if (!props) {
|
||||
+ dev_err(port->dev, "Failed to allocate memory for altmode properties");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ if(fwnode_property_read_u32_array(fwnode, "typec-altmodes", props, ret) < 0) {
|
||||
+ dev_err(port->dev, "Failed to read altmodes from port");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ i = 0;
|
||||
+ while (ret > 0 && i < ARRAY_SIZE(port->port_altmode)) {
|
||||
+ struct typec_altmode *alt;
|
||||
+ struct typec_altmode_desc alt_desc = {
|
||||
+ .svid = props[i * 4],
|
||||
+ .mode = props[i * 4 + 1],
|
||||
+ .vdo = props[i * 4 + 2],
|
||||
+ .roles = props[i * 4 + 3],
|
||||
+ };
|
||||
+
|
||||
+
|
||||
+ tcpm_log(port, "Adding altmode SVID: 0x%04x, mode: %d, vdo: %u, role: %d",
|
||||
+ alt_desc.svid, alt_desc.mode, alt_desc.vdo, alt_desc.roles);
|
||||
+ alt = typec_port_register_altmode(port->typec_port,
|
||||
+ &alt_desc);
|
||||
+ if (IS_ERR(alt)) {
|
||||
+ tcpm_log(port,
|
||||
+ "%s: failed to register port alternate mode 0x%x",
|
||||
+ dev_name(port->dev), alt_desc.svid);
|
||||
+ break;
|
||||
+ }
|
||||
+ typec_altmode_set_drvdata(alt, port);
|
||||
+ alt->ops = &tcpm_altmode_ops;
|
||||
+ port->port_altmode[i] = alt;
|
||||
+ i++;
|
||||
+ ret -= 4;
|
||||
+ }
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct fwnode_handle *capabilities, *caps = NULL;
|
||||
@@ -7287,6 +7389,23 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode
|
||||
if (!fwnode)
|
||||
return -EINVAL;
|
||||
|
||||
+#ifdef CONFIG_EXTCON
|
||||
+ ret = fwnode_property_count_u32(fwnode, "extcon-cables");
|
||||
+ if (ret > 0) {
|
||||
+ port->extcon_cables = devm_kzalloc(port->dev, sizeof(u32) * ret, GFP_KERNEL);
|
||||
+ if (!port->extcon_cables) {
|
||||
+ dev_err(port->dev, "Failed to allocate memory for extcon cable types. "\
|
||||
+ "Using default tyes");
|
||||
+ goto extcon_default;
|
||||
+ }
|
||||
+ fwnode_property_read_u32_array(fwnode, "extcon-cables", port->extcon_cables, ret);
|
||||
+ } else {
|
||||
+extcon_default:
|
||||
+ dev_info(port->dev, "No cable types defined, using default cables");
|
||||
+ port->extcon_cables = default_supported_cables;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
/*
|
||||
* This fwnode has a "compatible" property, but is never populated as a
|
||||
* struct device. Instead we simply parse it to read the properties.
|
||||
@@ -7856,6 +7975,17 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
|
||||
tcpm_fw_get_pd_revision(port, tcpc->fwnode);
|
||||
|
||||
port->try_role = port->typec_caps.prefer_role;
|
||||
+#ifdef CONFIG_EXTCON
|
||||
+ port->extcon = devm_extcon_dev_allocate(dev, port->extcon_cables);
|
||||
+ if (IS_ERR(port->extcon)) {
|
||||
+ dev_err(dev, "Failed to allocate extcon device: %ld", PTR_ERR(port->extcon));
|
||||
+ goto out_destroy_wq;
|
||||
+ }
|
||||
+ if((err = devm_extcon_dev_register(dev, port->extcon))) {
|
||||
+ dev_err(dev, "Failed to register extcon device: %d", err);
|
||||
+ goto out_destroy_wq;
|
||||
+ }
|
||||
+#endif
|
||||
|
||||
port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */
|
||||
|
||||
@@ -7905,6 +8035,12 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
|
||||
&tcpm_cable_ops);
|
||||
port->registered = true;
|
||||
|
||||
+ err = tcpm_fw_get_caps_late(port, tcpc->fwnode);
|
||||
+ if (err < 0) {
|
||||
+ dev_err(dev, "Failed to get altmodes from fwnode");
|
||||
+ goto out_destroy_wq;
|
||||
+ }
|
||||
+
|
||||
mutex_lock(&port->lock);
|
||||
tcpm_init(port);
|
||||
mutex_unlock(&port->lock);
|
||||
--
|
||||
Armbian
|
||||
|
||||
|
||||
@@ -89,7 +89,6 @@
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&vcc5v0_typec_en>;
|
||||
regulator-name = "vbus-5v";
|
||||
regulator-always-on;
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
vin-supply = <&vcc5v0_sys>;
|
||||
@@ -381,6 +380,13 @@
|
||||
|
||||
};
|
||||
|
||||
typec_extcon_bridge: typec-extcon {
|
||||
compatible = "linux,typec-extcon-bridge";
|
||||
usb-role-switch;
|
||||
orientation-switch;
|
||||
mode-switch;
|
||||
svid = /bits/ 16 <0xff01>;
|
||||
};
|
||||
};
|
||||
|
||||
&cpu_l0 {
|
||||
@@ -497,7 +503,7 @@
|
||||
|
||||
&cdn_dp {
|
||||
status = "okay";
|
||||
extcon = <&fusb0>;
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
phys = <&tcphy0_dp>;
|
||||
};
|
||||
|
||||
@@ -785,6 +791,8 @@
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <RK_PA2 IRQ_TYPE_LEVEL_LOW>;
|
||||
vbus-supply = <&vbus_typec>;
|
||||
usb-role-switch = <&typec_extcon_bridge>;
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
status = "okay";
|
||||
|
||||
connector {
|
||||
@@ -798,9 +806,17 @@
|
||||
source-pdos =
|
||||
<PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>;
|
||||
try-power-role = "sink";
|
||||
mode-switch = <&typec_extcon_bridge>;
|
||||
orientation-switch = <&typec_extcon_bridge>;
|
||||
|
||||
extcon-cables = <1 2 5 6 9 10 12 44>;
|
||||
typec-altmodes = <0xff01 1 0x001c0000 1>;
|
||||
altmodes {
|
||||
dp {
|
||||
svid = /bits/ 16 <0xff01>;
|
||||
/* what is this? megous uses 0x1c46 for pinebook pro.
|
||||
* dp alt spec is not open so it's based on reverse engineering and guess work */
|
||||
vdo = <0x1c46>;
|
||||
};
|
||||
};
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
@@ -984,7 +1000,7 @@
|
||||
};
|
||||
|
||||
&tcphy0 {
|
||||
extcon = <&fusb0>;
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
@@ -1010,6 +1026,8 @@
|
||||
|
||||
&u2phy0 {
|
||||
status = "okay";
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
extcon,ignore-usb; /* u2phy must not handle role switch */
|
||||
|
||||
u2phy0_otg: otg-port {
|
||||
status = "okay";
|
||||
@@ -1043,7 +1061,6 @@
|
||||
|
||||
&usbdrd3_0 {
|
||||
status = "okay";
|
||||
extcon = <&fusb0>;
|
||||
};
|
||||
|
||||
&usbdrd3_1 {
|
||||
@@ -1052,6 +1069,10 @@
|
||||
|
||||
&usbdrd_dwc3_0 {
|
||||
status = "okay";
|
||||
/* MUST be otg when DP is used so phy can reset properly */
|
||||
dr_mode = "otg";
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
snps,usb3-phy-reset-quirk;
|
||||
};
|
||||
|
||||
&usbdrd_dwc3_1 {
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&vcc5v0_typec_en>;
|
||||
regulator-name = "vbus_typec";
|
||||
regulator-always-on;
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
vin-supply = <&vcc5v0_sys>;
|
||||
@@ -334,6 +333,14 @@
|
||||
*/
|
||||
reset-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; /* GPIO0_B2 */
|
||||
};
|
||||
|
||||
typec_extcon_bridge: typec-extcon {
|
||||
compatible = "linux,typec-extcon-bridge";
|
||||
usb-role-switch;
|
||||
orientation-switch;
|
||||
mode-switch;
|
||||
svid = /bits/ 16 <0xff01>;
|
||||
};
|
||||
};
|
||||
|
||||
&cpu_l0 {
|
||||
@@ -456,7 +463,7 @@
|
||||
|
||||
&cdn_dp {
|
||||
status = "okay";
|
||||
extcon = <&fusb0>;
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
phys = <&tcphy0_dp>;
|
||||
};
|
||||
|
||||
@@ -740,6 +747,8 @@
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <RK_PA2 IRQ_TYPE_LEVEL_LOW>;
|
||||
vbus-supply = <&vbus_typec>;
|
||||
usb-role-switch = <&typec_extcon_bridge>;
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
status = "okay";
|
||||
|
||||
connector {
|
||||
@@ -753,9 +762,17 @@
|
||||
source-pdos =
|
||||
<PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>;
|
||||
try-power-role = "sink";
|
||||
mode-switch = <&typec_extcon_bridge>;
|
||||
orientation-switch = <&typec_extcon_bridge>;
|
||||
|
||||
extcon-cables = <1 2 5 6 9 10 12 44>;
|
||||
typec-altmodes = <0xff01 1 0x001c0000 1>;
|
||||
altmodes {
|
||||
dp {
|
||||
svid = /bits/ 16 <0xff01>;
|
||||
/* what is this? megous uses 0x1c46 for pinebook pro.
|
||||
* dp alt spec is not open so it's based on reverse engineering and guess work */
|
||||
vdo = <0x1c46>;
|
||||
};
|
||||
};
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
@@ -949,7 +966,7 @@
|
||||
};
|
||||
|
||||
&tcphy0 {
|
||||
extcon = <&fusb0>;
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
@@ -975,6 +992,8 @@
|
||||
|
||||
&u2phy0 {
|
||||
status = "okay";
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
extcon,ignore-usb; /* u2phy must not handle role switch */
|
||||
|
||||
u2phy0_otg: otg-port {
|
||||
status = "okay";
|
||||
@@ -1007,7 +1026,6 @@
|
||||
|
||||
&usbdrd3_0 {
|
||||
status = "okay";
|
||||
extcon = <&fusb0>;
|
||||
};
|
||||
|
||||
&usbdrd3_1 {
|
||||
@@ -1016,6 +1034,10 @@
|
||||
|
||||
&usbdrd_dwc3_0 {
|
||||
status = "okay";
|
||||
/* MUST be otg when DP is used so phy can reset properly */
|
||||
dr_mode = "otg";
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
snps,usb3-phy-reset-quirk;
|
||||
};
|
||||
|
||||
&usbdrd_dwc3_1 {
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <dt-bindings/leds/common.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
#include <dt-bindings/pinctrl/rockchip.h>
|
||||
#include <dt-bindings/usb/pd.h>
|
||||
#include "rk3399.dtsi"
|
||||
#include "rk3399-op1.dtsi"
|
||||
@@ -42,22 +44,38 @@
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
fan: gpio-fan {
|
||||
compatible = "gpio-fan";
|
||||
gpios = <&gpio4 26 GPIO_ACTIVE_HIGH>;
|
||||
/* arbitrary speed values,
|
||||
* actually only full speed and lower speed. */
|
||||
gpio-fan,speed-map = <50 0>,
|
||||
<100 1>;
|
||||
#cooling-cells = <2>;
|
||||
fan-supply = <&vcc5v0_sys>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&gpio_fan_pin>;
|
||||
};
|
||||
|
||||
gpio-leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
pwr-led {
|
||||
gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_HIGH>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
linux,default-trigger = "default-on";
|
||||
retain-state-suspended = <1>;
|
||||
};
|
||||
|
||||
act-led {
|
||||
gpios = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>;
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
linux,default-trigger="mmc0";
|
||||
};
|
||||
|
||||
rsv-led {
|
||||
gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_HIGH>;
|
||||
color = <LED_COLOR_ID_YELLOW>;
|
||||
linux,default-trigger="heartbeat";
|
||||
};
|
||||
};
|
||||
@@ -65,7 +83,7 @@
|
||||
vcc_lcd: vcc-lcd {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vcc_lcd";
|
||||
gpio = <&gpio4 30 GPIO_ACTIVE_HIGH>;
|
||||
gpio = <&gpio4 RK_PD6 GPIO_ACTIVE_HIGH>;
|
||||
startup-delay-us = <20000>;
|
||||
enable-active-high;
|
||||
regulator-min-microvolt = <3300000>;
|
||||
@@ -145,11 +163,20 @@
|
||||
clock-output-names = "xin32k";
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
typec_extcon_bridge: typec-extcon {
|
||||
compatible = "linux,typec-extcon-bridge";
|
||||
usb-role-switch;
|
||||
orientation-switch;
|
||||
mode-switch;
|
||||
svid = /bits/ 16 <0xff01>;
|
||||
};
|
||||
};
|
||||
|
||||
&cdn_dp {
|
||||
status = "okay";
|
||||
extcon = <&fusb0>;
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
phys = <&tcphy0_dp>;
|
||||
};
|
||||
|
||||
&cpu_l0 {
|
||||
@@ -189,7 +216,7 @@
|
||||
phy-mode = "rgmii";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&rgmii_pins>;
|
||||
snps,reset-gpio = <&gpio3 15 GPIO_ACTIVE_LOW>;
|
||||
snps,reset-gpio = <&gpio3 RK_PB7 GPIO_ACTIVE_LOW>;
|
||||
snps,reset-active-low;
|
||||
snps,reset-delays-us = <0 16000 72000>;
|
||||
tx_delay = <0x25>;
|
||||
@@ -439,6 +466,8 @@
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <RK_PA2 IRQ_TYPE_LEVEL_LOW>;
|
||||
vbus-supply = <&vbus_typec>;
|
||||
usb-role-switch = <&typec_extcon_bridge>;
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
status = "okay";
|
||||
|
||||
connector {
|
||||
@@ -448,13 +477,21 @@
|
||||
op-sink-microwatt = <1000000>;
|
||||
power-role = "dual";
|
||||
sink-pdos =
|
||||
<PDO_FIXED(5000, 2500, PDO_FIXED_USB_COMM)>;
|
||||
<PDO_FIXED(5000, 500, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_EXTPOWER)>;
|
||||
source-pdos =
|
||||
<PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>;
|
||||
<PDO_FIXED(5000, 500, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_EXTPOWER)>;
|
||||
try-power-role = "sink";
|
||||
mode-switch = <&typec_extcon_bridge>;
|
||||
orientation-switch = <&typec_extcon_bridge>;
|
||||
|
||||
extcon-cables = <1 2 5 6 9 10 12 44>;
|
||||
typec-altmodes = <0xff01 1 0x001c0000 1>;
|
||||
altmodes {
|
||||
dp {
|
||||
svid = /bits/ 16 <0xff01>;
|
||||
/* what is this? megous uses 0x1c46 for pinebook pro.
|
||||
* dp alt spec is not open so it's based on reverse engineering and guess work */
|
||||
vdo = <0x1c46>;
|
||||
};
|
||||
};
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
@@ -488,10 +525,6 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
vdd_gpu: vdd_gpu@60 {
|
||||
@@ -519,7 +552,6 @@
|
||||
};
|
||||
|
||||
&i2c8 {
|
||||
|
||||
m24c08: m24c08@50 {
|
||||
compatible = "atmel,24c08";
|
||||
reg = <0x50>;
|
||||
@@ -541,7 +573,7 @@
|
||||
};
|
||||
|
||||
&pcie0 {
|
||||
ep-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
|
||||
ep-gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>;
|
||||
num-lanes = <4>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie_clkreqn_cpm>;
|
||||
@@ -594,7 +626,7 @@
|
||||
};
|
||||
|
||||
&tcphy0 {
|
||||
extcon = <&fusb0>;
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
@@ -628,6 +660,8 @@
|
||||
|
||||
&u2phy0 {
|
||||
status = "okay";
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
extcon,ignore-usb; /* u2phy must not handle role switch */
|
||||
|
||||
u2phy0_otg: otg-port {
|
||||
status = "okay";
|
||||
@@ -664,7 +698,10 @@
|
||||
|
||||
&usbdrd_dwc3_0 {
|
||||
status = "okay";
|
||||
dr_mode = "host";
|
||||
/* MUST be otg when DP is used so phy can reset properly */
|
||||
dr_mode = "otg";
|
||||
extcon = <&typec_extcon_bridge>;
|
||||
snps,usb3-phy-reset-quirk;
|
||||
};
|
||||
|
||||
&usbdrd3_1 {
|
||||
@@ -697,12 +734,23 @@
|
||||
};
|
||||
|
||||
&pinctrl {
|
||||
fan {
|
||||
gpio_fan_pin: gpio-fan-pin {
|
||||
rockchip,pins = <4 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>;
|
||||
};
|
||||
};
|
||||
|
||||
fusb30x {
|
||||
fusb0_int: fusb0-int {
|
||||
rockchip,pins = <1 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||
};
|
||||
};
|
||||
|
||||
pmic {
|
||||
pmic_int_l: pmic-int-l {
|
||||
rockchip,pins =
|
||||
<1 18 RK_FUNC_GPIO &pcfg_pull_up>,
|
||||
<0 9 RK_FUNC_GPIO &pcfg_pull_none>; /* GPIO0_B1 */
|
||||
<1 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>,
|
||||
<0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
|
||||
};
|
||||
vsel1_gpio: vsel1-gpio {
|
||||
rockchip,pins =
|
||||
@@ -726,20 +774,6 @@
|
||||
rockchip,pins = <1 RK_PA3 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||
};
|
||||
};
|
||||
|
||||
fusb30x {
|
||||
fusb0_int: fusb0-int {
|
||||
rockchip,pins = <1 2 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&vopl {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&vopl_mmu {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&vopb {
|
||||
@@ -749,3 +783,11 @@
|
||||
&vopb_mmu {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&vopl {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&vopl_mmu {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ondrej Jirman <megi@xff.cz>
|
||||
Date: Tue, 7 May 2024 18:39:12 +0200
|
||||
Subject: Revert "usb: typec: tcpm: unregister existing source caps before
|
||||
re-registration"
|
||||
|
||||
This reverts commit 230ecdf71a644c9c73e0e6735b33173074ae3f94.
|
||||
---
|
||||
drivers/usb/typec/tcpm/tcpm.c | 7 +------
|
||||
1 file changed, 1 insertion(+), 6 deletions(-)
|
||||
|
||||
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/usb/typec/tcpm/tcpm.c
|
||||
+++ b/drivers/usb/typec/tcpm/tcpm.c
|
||||
@@ -3110,7 +3110,7 @@ static int tcpm_register_source_caps(struct tcpm_port *port)
|
||||
{
|
||||
struct usb_power_delivery_desc desc = { port->negotiated_rev };
|
||||
struct usb_power_delivery_capabilities_desc caps = { };
|
||||
- struct usb_power_delivery_capabilities *cap = port->partner_source_caps;
|
||||
+ struct usb_power_delivery_capabilities *cap;
|
||||
|
||||
if (!port->partner_pd)
|
||||
port->partner_pd = usb_power_delivery_register(NULL, &desc);
|
||||
@@ -3120,11 +3120,6 @@ static int tcpm_register_source_caps(struct tcpm_port *port)
|
||||
memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps);
|
||||
caps.role = TYPEC_SOURCE;
|
||||
|
||||
- if (cap) {
|
||||
- usb_power_delivery_unregister_capabilities(cap);
|
||||
- port->partner_source_caps = NULL;
|
||||
- }
|
||||
-
|
||||
cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps);
|
||||
if (IS_ERR(cap))
|
||||
return PTR_ERR(cap);
|
||||
--
|
||||
Armbian
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: hyx0329 <hyx0329@outlook.com>
|
||||
Date: Tue, 3 Jun 2025 12:14:37 +0000
|
||||
Subject: notify typec dp hpd state through extcon
|
||||
|
||||
Signed-off-by: hyx0329 <hyx0329@outlook.com>
|
||||
---
|
||||
drivers/usb/typec/altmodes/displayport.c | 57 ++++++++++
|
||||
1 file changed, 57 insertions(+)
|
||||
|
||||
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/usb/typec/altmodes/displayport.c
|
||||
+++ b/drivers/usb/typec/altmodes/displayport.c
|
||||
@@ -9,8 +9,11 @@
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
+#include <linux/extcon.h>
|
||||
+#include <linux/extcon-provider.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/usb/pd_vdo.h>
|
||||
#include <linux/usb/typec_dp.h>
|
||||
@@ -74,6 +77,57 @@ struct dp_altmode {
|
||||
struct typec_altmode *plug_prime;
|
||||
};
|
||||
|
||||
+/* Notify DP hotplug change via extcon. Current rockchip cdn-dp driver need this signal
|
||||
+ * to reset DP link. */
|
||||
+static void dp_altmode_update_extcon_hpd_status(struct dp_altmode *dp, bool new_hpd) {
|
||||
+ const struct device *dev = &dp->alt->dev;
|
||||
+ struct extcon_dev* edev = NULL;
|
||||
+ int ret;
|
||||
+
|
||||
+ /* skip if hpd is not changed */
|
||||
+ if (dp->hpd == new_hpd)
|
||||
+ return;
|
||||
+
|
||||
+ /* find the extcon associated with cdn-dp
|
||||
+ * an unrelated extcon might be found, but this is a workaround anyway */
|
||||
+ while (dev) {
|
||||
+ /* Case1: dev is the extcon provider */
|
||||
+ edev = extcon_find_edev_by_node(dev->of_node);
|
||||
+ if (!IS_ERR(edev)) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Case2: dev links to one extcon */
|
||||
+ if (of_property_present(dev->of_node, "extcon")) {
|
||||
+ edev = extcon_get_edev_by_phandle(dev, 0);
|
||||
+ if (!IS_ERR(edev)) {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ dev = dev->parent;
|
||||
+ }
|
||||
+
|
||||
+ if (IS_ERR_OR_NULL(edev)) {
|
||||
+ dev_info(&dp->alt->dev,
|
||||
+ "no extcon found for current port, not notifying hpd change");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ret = extcon_set_property_capability(edev, EXTCON_DISP_DP, EXTCON_PROP_DISP_HPD);
|
||||
+ if (ret) {
|
||||
+ dev_info(&dp->alt->dev,
|
||||
+ "updating hpd on target extcon is not supported(%d)\n", ret);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ dev_info(&dp->alt->dev, "dp-hpd=%d (port=%x alt=%x)\n",
|
||||
+ (int)new_hpd, dp->port->vdo, dp->alt->vdo);
|
||||
+ extcon_set_property(edev, EXTCON_DISP_DP, EXTCON_PROP_DISP_HPD,
|
||||
+ (union extcon_property_value)(int)new_hpd);
|
||||
+ extcon_sync(edev, EXTCON_DISP_DP);
|
||||
+}
|
||||
+
|
||||
static int dp_altmode_notify(struct dp_altmode *dp)
|
||||
{
|
||||
unsigned long conf;
|
||||
@@ -167,6 +221,7 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
|
||||
dp->state = dp->plug_prime ? DP_STATE_CONFIGURE_PRIME :
|
||||
DP_STATE_CONFIGURE;
|
||||
if (dp->hpd != hpd) {
|
||||
+ dp_altmode_update_extcon_hpd_status(dp, hpd);
|
||||
dp->hpd = hpd;
|
||||
dp->pending_hpd = true;
|
||||
}
|
||||
@@ -175,6 +230,7 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
|
||||
drm_connector_oob_hotplug_event(dp->connector_fwnode,
|
||||
hpd ? connector_status_connected :
|
||||
connector_status_disconnected);
|
||||
+ dp_altmode_update_extcon_hpd_status(dp, hpd);
|
||||
dp->hpd = hpd;
|
||||
sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
|
||||
}
|
||||
@@ -374,6 +430,7 @@ static int dp_altmode_vdm(struct typec_altmode *alt,
|
||||
if (dp->hpd) {
|
||||
drm_connector_oob_hotplug_event(dp->connector_fwnode,
|
||||
connector_status_disconnected);
|
||||
+ dp_altmode_update_extcon_hpd_status(dp, false);
|
||||
dp->hpd = false;
|
||||
sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
|
||||
}
|
||||
--
|
||||
Armbian
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= <megi@xff.cz>
|
||||
Date: Sun, 30 Jan 2022 23:44:42 +0100
|
||||
Subject: phy: phy-rockchip-inno-usb2: Decrease delay between port init and
|
||||
charger detection
|
||||
|
||||
When used on systems with TCPM, the charger detection via USB2.0 PHY
|
||||
will take too much time during boot, so the fusb302 driver will not
|
||||
be able to get the result, and will set incorrect current_max on
|
||||
its power supply device.
|
||||
|
||||
This will lead to failure to charge when the device is powered up
|
||||
while connected to non-PD charger.
|
||||
|
||||
Decreasing delay of otg_sm_work from 6s to 500ms ensures that
|
||||
port type detection via the PHY will finish well in time for
|
||||
TCPM to be able to use its result when Rp=default is detected
|
||||
via CC pins.
|
||||
|
||||
Signed-off-by: Ondrej Jirman <megi@xff.cz>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||
@@ -536,7 +536,7 @@ static int rockchip_usb2phy_init(struct phy *phy)
|
||||
goto out;
|
||||
|
||||
schedule_delayed_work(&rport->otg_sm_work,
|
||||
- OTG_SCHEDULE_DELAY * 3);
|
||||
+ msecs_to_jiffies(500));
|
||||
} else {
|
||||
/* If OTG works in host only mode, do nothing. */
|
||||
dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode);
|
||||
--
|
||||
Armbian
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= <megi@xff.cz>
|
||||
Date: Fri, 4 Feb 2022 02:25:34 +0100
|
||||
Subject: phy: rockchip-inno-usb2: More robust charger detection extcon updates
|
||||
|
||||
Make sure we always clear the charger detection results. Allow to
|
||||
disable updates of EXTCON_USB state via extcon,ignore-usb DT property.
|
||||
The previous method was smart, but assumed !vbus_branch would
|
||||
always be called.
|
||||
|
||||
Signed-off-by: Ondrej Jirman <megi@xff.cz>
|
||||
---
|
||||
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 39 ++++++----
|
||||
1 file changed, 23 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||
@@ -656,21 +656,27 @@ static const struct phy_ops rockchip_usb2phy_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
+const int rockchip_usb2phy_cable_types[] = {
|
||||
+ EXTCON_CHG_USB_SDP,
|
||||
+ EXTCON_CHG_USB_DCP,
|
||||
+ EXTCON_CHG_USB_CDP,
|
||||
+ EXTCON_CHG_USB_ACA,
|
||||
+};
|
||||
+
|
||||
static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
|
||||
{
|
||||
struct rockchip_usb2phy_port *rport =
|
||||
container_of(work, struct rockchip_usb2phy_port,
|
||||
otg_sm_work.work);
|
||||
struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
|
||||
- static unsigned int cable;
|
||||
+ unsigned int cable = 0, i;
|
||||
unsigned long delay;
|
||||
- bool vbus_attach, sch_work, notify_charger;
|
||||
+ bool vbus_attach, sch_work;
|
||||
|
||||
vbus_attach = property_enabled(rphy->grf,
|
||||
&rport->port_cfg->utmi_bvalid);
|
||||
|
||||
sch_work = false;
|
||||
- notify_charger = false;
|
||||
delay = OTG_SCHEDULE_DELAY;
|
||||
dev_dbg(&rport->phy->dev, "%s otg sm work\n",
|
||||
usb_otg_state_string(rport->state));
|
||||
@@ -699,14 +705,12 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
|
||||
dev_dbg(&rport->phy->dev, "sdp cable is connected\n");
|
||||
rockchip_usb2phy_power_on(rport->phy);
|
||||
rport->state = OTG_STATE_B_PERIPHERAL;
|
||||
- notify_charger = true;
|
||||
sch_work = true;
|
||||
cable = EXTCON_CHG_USB_SDP;
|
||||
break;
|
||||
case POWER_SUPPLY_TYPE_USB_DCP:
|
||||
dev_dbg(&rport->phy->dev, "dcp cable is connected\n");
|
||||
rockchip_usb2phy_power_off(rport->phy);
|
||||
- notify_charger = true;
|
||||
sch_work = true;
|
||||
cable = EXTCON_CHG_USB_DCP;
|
||||
break;
|
||||
@@ -714,7 +718,6 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
|
||||
dev_dbg(&rport->phy->dev, "cdp cable is connected\n");
|
||||
rockchip_usb2phy_power_on(rport->phy);
|
||||
rport->state = OTG_STATE_B_PERIPHERAL;
|
||||
- notify_charger = true;
|
||||
sch_work = true;
|
||||
cable = EXTCON_CHG_USB_CDP;
|
||||
break;
|
||||
@@ -726,22 +729,26 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
- notify_charger = true;
|
||||
rphy->chg_state = USB_CHG_STATE_UNDEFINED;
|
||||
rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
- if (rport->vbus_attached != vbus_attach) {
|
||||
- rport->vbus_attached = vbus_attach;
|
||||
+ if (rphy->edev && extcon_get_state(rphy->edev, cable) != vbus_attach) {
|
||||
+ for (i = 0; i < ARRAY_SIZE(rockchip_usb2phy_cable_types); i++) {
|
||||
+ int type = rockchip_usb2phy_cable_types[i];
|
||||
|
||||
- if (notify_charger && rphy->edev) {
|
||||
- extcon_set_state_sync(rphy->edev,
|
||||
- cable, vbus_attach);
|
||||
- if (cable == EXTCON_CHG_USB_SDP)
|
||||
- extcon_set_state_sync(rphy->edev,
|
||||
- EXTCON_USB,
|
||||
- vbus_attach);
|
||||
+ if (extcon_get_state(rphy->edev, type))
|
||||
+ extcon_set_state_sync(rphy->edev, type, 0);
|
||||
}
|
||||
+
|
||||
+ if (vbus_attach)
|
||||
+ extcon_set_state_sync(rphy->edev, cable, vbus_attach);
|
||||
+
|
||||
+ if ((cable == EXTCON_CHG_USB_SDP || cable == POWER_SUPPLY_TYPE_USB_CDP)
|
||||
+ && extcon_get_state(rphy->edev, EXTCON_USB) != vbus_attach
|
||||
+ && !of_property_read_bool(rphy->dev->of_node, "extcon,ignore-usb"))
|
||||
+ extcon_set_state_sync(rphy->edev,
|
||||
+ EXTCON_USB, vbus_attach);
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
--
|
||||
Armbian
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
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
|
||||
@@ -152,7 +152,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;
|
||||
@@ -191,6 +191,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;
|
||||
@@ -210,12 +211,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
|
||||
@@ -257,6 +289,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);
|
||||
@@ -1844,6 +1877,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;
|
||||
@@ -2438,6 +2473,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;
|
||||
dwc3_gadget_suspend(dwc);
|
||||
@@ -2498,11 +2534,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:
|
||||
@@ -2566,6 +2603,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;
|
||||
@@ -2604,6 +2642,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;
|
||||
@@ -2628,6 +2667,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)
|
||||
@@ -1152,6 +1158,10 @@ struct dwc3_scratchpad_array {
|
||||
* @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
|
||||
@@ -1392,6 +1402,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;
|
||||
}
|
||||
@@ -547,8 +558,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
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ondrej Jirman <megi@xff.cz>
|
||||
Date: Sat, 2 Mar 2024 14:33:21 +0100
|
||||
Subject: usb: dwc3: Extend reset quirk support to include role-switch
|
||||
|
||||
Originally my reset quirk patch only supported extcon mode changes
|
||||
via extcon interface. Support role-switch, too.
|
||||
|
||||
Signed-off-by: Ondrej Jirman <megi@xff.cz>
|
||||
---
|
||||
drivers/usb/dwc3/drd.c | 9 ++++++++-
|
||||
1 file changed, 8 insertions(+), 1 deletion(-)
|
||||
|
||||
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
|
||||
@@ -458,7 +458,7 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
|
||||
enum usb_role role)
|
||||
{
|
||||
struct dwc3 *dwc = usb_role_switch_get_drvdata(sw);
|
||||
- u32 mode;
|
||||
+ u32 mode = DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED;
|
||||
|
||||
switch (role) {
|
||||
case USB_ROLE_HOST:
|
||||
@@ -468,6 +468,8 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
|
||||
mode = DWC3_GCTL_PRTCAP_DEVICE;
|
||||
break;
|
||||
default:
|
||||
+ if (dwc->usb3_phy_reset_quirk)
|
||||
+ break;
|
||||
if (dwc->role_switch_default_mode == USB_DR_MODE_HOST)
|
||||
mode = DWC3_GCTL_PRTCAP_HOST;
|
||||
else
|
||||
@@ -487,6 +489,9 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
switch (dwc->current_dr_role) {
|
||||
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
|
||||
+ role = USB_ROLE_NONE;
|
||||
+ break;
|
||||
case DWC3_GCTL_PRTCAP_HOST:
|
||||
role = USB_ROLE_HOST;
|
||||
break;
|
||||
@@ -518,6 +523,8 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
|
||||
} else {
|
||||
dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL;
|
||||
mode = DWC3_GCTL_PRTCAP_DEVICE;
|
||||
+ if (dwc->usb3_phy_reset_quirk)
|
||||
+ mode = DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED;
|
||||
}
|
||||
dwc3_set_mode(dwc, mode);
|
||||
|
||||
--
|
||||
Armbian
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ondrej Jirman <megi@xff.cz>
|
||||
Date: Sun, 30 Apr 2023 18:54:51 +0200
|
||||
Subject: usb: dwc3: Track the power state of usb3_generic_phy
|
||||
|
||||
We will need to manage power state of this phy inisde set_mode work,
|
||||
without any ability to perform recovery if power on fails, so we'll
|
||||
need to track result of power on separately, to be able to balance
|
||||
the phy on/off calls.
|
||||
|
||||
Signed-off-by: Ondrej Jirman <megi@xff.cz>
|
||||
---
|
||||
drivers/usb/dwc3/core.c | 7 +++++--
|
||||
drivers/usb/dwc3/core.h | 2 ++
|
||||
2 files changed, 7 insertions(+), 2 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
|
||||
@@ -931,6 +931,7 @@ static int dwc3_phy_power_on(struct dwc3 *dwc)
|
||||
goto err_power_off_usb3_phy;
|
||||
}
|
||||
|
||||
+ dwc->usb3_phy_powered = true;
|
||||
return 0;
|
||||
|
||||
err_power_off_usb3_phy:
|
||||
@@ -951,8 +952,10 @@ static void dwc3_phy_power_off(struct dwc3 *dwc)
|
||||
{
|
||||
int i;
|
||||
|
||||
- for (i = 0; i < dwc->num_usb3_ports; i++)
|
||||
- phy_power_off(dwc->usb3_generic_phy[i]);
|
||||
+ if (dwc->usb3_phy_powered)
|
||||
+ for (i = 0; i < dwc->num_usb3_ports; i++)
|
||||
+ phy_power_off(dwc->usb3_generic_phy[i]);
|
||||
+ dwc->usb3_phy_powered = false;
|
||||
|
||||
for (i = 0; i < dwc->num_usb2_ports; i++)
|
||||
phy_power_off(dwc->usb2_generic_phy[i]);
|
||||
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
|
||||
@@ -1224,6 +1224,8 @@ struct dwc3 {
|
||||
u8 num_usb2_ports;
|
||||
u8 num_usb3_ports;
|
||||
|
||||
+ bool usb3_phy_powered;
|
||||
+
|
||||
bool phys_ready;
|
||||
|
||||
struct ulpi *ulpi;
|
||||
--
|
||||
Armbian
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ondrej Jirman <megi@xff.cz>
|
||||
Date: Mon, 5 Sep 2022 00:56:07 +0200
|
||||
Subject: usb: typec: altmodes: displayport: Respect DP_CAP_RECEPTACLE bit
|
||||
|
||||
DP_CAP_RECEPTACLE swaps the meaning of pin assignments. So eg.
|
||||
pin assignments for DFP_D plug are stored in DP_CAP_UFP_D_PIN_ASSIGN
|
||||
bits. Yes, it's slightly confusing. :)
|
||||
|
||||
Make the kernel report lack of match in supported pin configurations
|
||||
between connected ports, so that the user is not confused why their
|
||||
USB-C dock doesn't have working Alt-DP mode, in case the dock returns
|
||||
wrong VDO.
|
||||
|
||||
Signed-off-by: Ondrej Jirman <megi@xff.cz>
|
||||
---
|
||||
drivers/usb/typec/altmodes/displayport.c | 50 +++++++++-
|
||||
1 file changed, 45 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/usb/typec/altmodes/displayport.c
|
||||
+++ b/drivers/usb/typec/altmodes/displayport.c
|
||||
@@ -170,11 +170,29 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
|
||||
/* Account for active cable capabilities */
|
||||
if (dp->plug_prime)
|
||||
pin_assign &= DP_CAP_UFP_D_PIN_ASSIGN(dp->plug_prime->vdo);
|
||||
+
|
||||
+ /*
|
||||
+ * The Display Port Alt mode standard is not publicly available,
|
||||
+ * so this is based on guesswork and real VDOs received from
|
||||
+ * receptacle based and plug based Type-C alt mode supporting
|
||||
+ * docks to make configuration work in practice:
|
||||
+ *
|
||||
+ * Plug (captive cable) based dock: port=c46 alt=c05
|
||||
+ * Recpetacle based dock: port=c46 alt=c0045
|
||||
+ *
|
||||
+ pin_assign = DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo);
|
||||
+ pin_assign &= dp->alt->vdo & DP_CAP_RECEPTACLE ?
|
||||
+ DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) :
|
||||
+ DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo);
|
||||
+ */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+ dev_info(&dp->alt->dev, "con=%d pin_assign=%x (port=%x alt=%x)\n",
|
||||
+ (int)con, (unsigned)pin_assign, dp->port->vdo, dp->alt->vdo);
|
||||
+
|
||||
/* Determining the initial pin assignment. */
|
||||
if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) {
|
||||
/* Is USB together with DP preferred */
|
||||
@@ -787,15 +805,37 @@ int dp_altmode_probe(struct typec_altmode *alt)
|
||||
struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P);
|
||||
struct fwnode_handle *fwnode;
|
||||
struct dp_altmode *dp;
|
||||
+ u32 port_pins, alt_pins;
|
||||
|
||||
/* FIXME: Port can only be DFP_U. */
|
||||
|
||||
- /* Make sure we have compatible pin configurations */
|
||||
- if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) &
|
||||
- DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) &&
|
||||
- !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) &
|
||||
- DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)))
|
||||
+ /*
|
||||
+ * When port is a receptacle DP_CAP_xFP_D_PIN_ASSIGN macros have the
|
||||
+ * regular meaning. When the port is a plug, the meaning is swapped.
|
||||
+ *
|
||||
+ * Check if we have any matching DFP_D<->UFP_D or UFP_D<->DFP_D pin assignment.
|
||||
+ */
|
||||
+ port_pins = port->vdo & DP_CAP_RECEPTACLE ?
|
||||
+ DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) | DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) << 8 :
|
||||
+ DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) | DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) << 8;
|
||||
+
|
||||
+ alt_pins = alt->vdo & DP_CAP_RECEPTACLE ?
|
||||
+ DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo) | DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo) << 8 :
|
||||
+ DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo) | DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo) << 8;
|
||||
+
|
||||
+ /* Can't plug plug into a plug */
|
||||
+ if (!(port->vdo & DP_CAP_RECEPTACLE) && !(alt->vdo & DP_CAP_RECEPTACLE)) {
|
||||
+ dev_warn(&alt->dev, "Our Alt-DP VDO 0x%06x and peer port VDO 0x%06x are not compatible (both are reported as plugs!)\n",
|
||||
+ port->vdo, alt->vdo);
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ /* Make sure we have compatiple pin configurations */
|
||||
+ if (!(port_pins & alt_pins)) {
|
||||
+ dev_warn(&alt->dev, "Our Alt-DP VDO 0x%06x and peer port VDO 0x%06x are not compatible (no shared pinconf %04x<->%04x (UUDD))\n",
|
||||
+ port->vdo, alt->vdo, port_pins, alt_pins);
|
||||
return -ENODEV;
|
||||
+ }
|
||||
|
||||
dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
|
||||
if (!dp)
|
||||
--
|
||||
Armbian
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ondrej Jirman <megi@xff.cz>
|
||||
Date: Sat, 18 Feb 2023 00:38:44 +0100
|
||||
Subject: usb: typec: tcpm: Fix PD devices/capabilities registration
|
||||
|
||||
Unregister caps before registering them. Store NULL to the struct
|
||||
if registration fails, so that next attempt can succeed.
|
||||
|
||||
Fixes "sysfs: cannot create duplicate filename
|
||||
'/devices/virtual/usb_power_delivery/pd1/source-capabilities'" error.
|
||||
|
||||
Signed-off-by: Ondrej Jirman <megi@xff.cz>
|
||||
---
|
||||
drivers/usb/typec/tcpm/tcpm.c | 37 ++++++++--
|
||||
1 file changed, 29 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/usb/typec/tcpm/tcpm.c
|
||||
+++ b/drivers/usb/typec/tcpm/tcpm.c
|
||||
@@ -3111,15 +3111,22 @@ static int tcpm_register_source_caps(struct tcpm_port *port)
|
||||
struct usb_power_delivery_desc desc = { port->negotiated_rev };
|
||||
struct usb_power_delivery_capabilities_desc caps = { };
|
||||
struct usb_power_delivery_capabilities *cap;
|
||||
+ struct usb_power_delivery *partner_pd;
|
||||
+
|
||||
+ if (!port->partner_pd) {
|
||||
+ partner_pd = usb_power_delivery_register(NULL, &desc);
|
||||
+ if (IS_ERR(partner_pd))
|
||||
+ return PTR_ERR(partner_pd);
|
||||
|
||||
- if (!port->partner_pd)
|
||||
- port->partner_pd = usb_power_delivery_register(NULL, &desc);
|
||||
- if (IS_ERR(port->partner_pd))
|
||||
- return PTR_ERR(port->partner_pd);
|
||||
+ port->partner_pd = partner_pd;
|
||||
+ }
|
||||
|
||||
memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps);
|
||||
caps.role = TYPEC_SOURCE;
|
||||
|
||||
+ usb_power_delivery_unregister_capabilities(port->partner_source_caps);
|
||||
+ port->partner_source_caps = NULL;
|
||||
+
|
||||
cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps);
|
||||
if (IS_ERR(cap))
|
||||
return PTR_ERR(cap);
|
||||
@@ -3134,15 +3141,22 @@ static int tcpm_register_sink_caps(struct tcpm_port *port)
|
||||
struct usb_power_delivery_desc desc = { port->negotiated_rev };
|
||||
struct usb_power_delivery_capabilities_desc caps = { };
|
||||
struct usb_power_delivery_capabilities *cap;
|
||||
+ struct usb_power_delivery *partner_pd;
|
||||
+
|
||||
+ if (!port->partner_pd) {
|
||||
+ partner_pd = usb_power_delivery_register(NULL, &desc);
|
||||
+ if (IS_ERR(partner_pd))
|
||||
+ return PTR_ERR(partner_pd);
|
||||
|
||||
- if (!port->partner_pd)
|
||||
- port->partner_pd = usb_power_delivery_register(NULL, &desc);
|
||||
- if (IS_ERR(port->partner_pd))
|
||||
- return PTR_ERR(port->partner_pd);
|
||||
+ port->partner_pd = partner_pd;
|
||||
+ }
|
||||
|
||||
memcpy(caps.pdo, port->sink_caps, sizeof(u32) * port->nr_sink_caps);
|
||||
caps.role = TYPEC_SINK;
|
||||
|
||||
+ usb_power_delivery_unregister_capabilities(port->partner_sink_caps);
|
||||
+ port->partner_sink_caps = NULL;
|
||||
+
|
||||
cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps);
|
||||
if (IS_ERR(cap))
|
||||
return PTR_ERR(cap);
|
||||
@@ -7205,10 +7219,17 @@ static int tcpm_port_register_pd(struct tcpm_port *port)
|
||||
port->pds[i] = usb_power_delivery_register(port->dev, &desc);
|
||||
if (IS_ERR(port->pds[i])) {
|
||||
ret = PTR_ERR(port->pds[i]);
|
||||
+ port->pds[i] = NULL;
|
||||
goto err_unregister;
|
||||
}
|
||||
port->pd_list[i]->pd = port->pds[i];
|
||||
|
||||
+ usb_power_delivery_unregister_capabilities(port->pd_list[i]->source_cap);
|
||||
+ port->pd_list[i]->source_cap = NULL;
|
||||
+
|
||||
+ usb_power_delivery_unregister_capabilities(port->pd_list[i]->sink_cap);
|
||||
+ port->pd_list[i]->sink_cap = NULL;
|
||||
+
|
||||
if (port->pd_list[i]->source_desc.pdo[0]) {
|
||||
cap = usb_power_delivery_register_capabilities(port->pds[i],
|
||||
&port->pd_list[i]->source_desc);
|
||||
--
|
||||
Armbian
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ondrej Jirman <megi@xff.cz>
|
||||
Date: Sun, 14 Aug 2022 16:23:28 +0200
|
||||
Subject: usb: typec: tcpm: Unregister altmodes before registering new ones
|
||||
|
||||
Just in case there are already altmodes registered. Otherwise the
|
||||
kernel will fail with:
|
||||
|
||||
[50554.467172] sysfs: cannot create duplicate filename '/bus/typec/devices/port0-partner.0'
|
||||
[50554.467198] CPU: 2 PID: 897 Comm: 4-0022 Tainted: G C 5.18.15-1-MANJARO-ARM #1
|
||||
[50554.467208] Hardware name: Pine64 PinePhonePro (DT)
|
||||
[50554.467214] Call trace:
|
||||
[50554.467218] dump_backtrace.part.0+0xcc/0xe0
|
||||
[50554.467241] show_stack+0x18/0x6c
|
||||
[50554.467249] dump_stack_lvl+0x64/0x80
|
||||
[50554.467262] dump_stack+0x18/0x34
|
||||
[50554.467270] sysfs_warn_dup+0x60/0x7c
|
||||
[50554.467279] sysfs_do_create_link_sd+0xf0/0x100
|
||||
[50554.467286] sysfs_create_link+0x24/0x44
|
||||
[50554.467292] bus_add_device+0x64/0x120
|
||||
[50554.467304] device_add+0x320/0x890
|
||||
[50554.467312] device_register+0x20/0x30
|
||||
[50554.467318] typec_register_altmode+0x1e4/0x35c
|
||||
[50554.467330] typec_partner_register_altmode+0x10/0x20
|
||||
[50554.467339] tcpm_pd_rx_handler+0x1804/0x18e0
|
||||
[50554.467346] kthread_worker_fn+0xa0/0x180
|
||||
[50554.467357] kthread+0x108/0x10c
|
||||
[50554.467364] ret_from_fork+0x10/0x20
|
||||
[50554.467528] typec port0-partner: failed to register alternate mode (-17)
|
||||
|
||||
Signed-off-by: Ondrej Jirman <megi@xff.cz>
|
||||
---
|
||||
drivers/usb/typec/tcpm/tcpm.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/usb/typec/tcpm/tcpm.c
|
||||
+++ b/drivers/usb/typec/tcpm/tcpm.c
|
||||
@@ -1863,6 +1863,9 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port)
|
||||
return;
|
||||
|
||||
for (i = 0; i < modep->altmodes; i++) {
|
||||
+ typec_unregister_altmode(port->partner_altmode[i]);
|
||||
+ port->partner_altmode[i] = NULL;
|
||||
+
|
||||
altmode = typec_partner_register_altmode(port->partner,
|
||||
&modep->altmode_desc[i]);
|
||||
if (IS_ERR(altmode)) {
|
||||
--
|
||||
Armbian
|
||||
|
||||
@@ -0,0 +1,383 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= <megi@xff.cz>
|
||||
Date: Sun, 7 Nov 2021 19:24:40 +0100
|
||||
Subject: usb: typec: typec-extcon: Add typec -> extcon bridge driver
|
||||
|
||||
This bridge connects standard Type C port interfaces for controling
|
||||
muxes, switches and usb roles to muxes, switches and usb role
|
||||
drivers controlled via extcon interface.
|
||||
|
||||
Signed-off-by: Ondrej Jirman <megi@xff.cz>
|
||||
---
|
||||
drivers/usb/typec/Kconfig | 7 +
|
||||
drivers/usb/typec/Makefile | 1 +
|
||||
drivers/usb/typec/typec-extcon.c | 330 ++++++++++
|
||||
3 files changed, 338 insertions(+)
|
||||
|
||||
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/usb/typec/Kconfig
|
||||
+++ b/drivers/usb/typec/Kconfig
|
||||
@@ -110,6 +110,13 @@ config TYPEC_WUSB3801
|
||||
If you choose to build this driver as a dynamically linked module, the
|
||||
module will be called wusb3801.ko.
|
||||
|
||||
+config TYPEC_EXTCON
|
||||
+ tristate "Type-C switch/mux -> extcon interface bridge driver"
|
||||
+ depends on USB_ROLE_SWITCH
|
||||
+ help
|
||||
+ Say Y or M here if your system needs bridging between typec class
|
||||
+ and extcon interfaces.
|
||||
+
|
||||
source "drivers/usb/typec/mux/Kconfig"
|
||||
|
||||
source "drivers/usb/typec/altmodes/Kconfig"
|
||||
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
|
||||
index 111111111111..222222222222 100644
|
||||
--- a/drivers/usb/typec/Makefile
|
||||
+++ b/drivers/usb/typec/Makefile
|
||||
@@ -11,4 +11,5 @@ obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o
|
||||
obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o
|
||||
obj-$(CONFIG_TYPEC_RT1719) += rt1719.o
|
||||
obj-$(CONFIG_TYPEC_WUSB3801) += wusb3801.o
|
||||
+obj-$(CONFIG_TYPEC_EXTCON) += typec-extcon.o
|
||||
obj-$(CONFIG_TYPEC) += mux/
|
||||
diff --git a/drivers/usb/typec/typec-extcon.c b/drivers/usb/typec/typec-extcon.c
|
||||
new file mode 100644
|
||||
index 000000000000..111111111111
|
||||
--- /dev/null
|
||||
+++ b/drivers/usb/typec/typec-extcon.c
|
||||
@@ -0,0 +1,330 @@
|
||||
+/*
|
||||
+ * typec -> extcon bridge
|
||||
+ * Copyright (c) 2021 Ondřej Jirman <megi@xff.cz>
|
||||
+ *
|
||||
+ * This driver bridges standard type-c interfaces to drivers that
|
||||
+ * expect extcon interface.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/power_supply.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/usb/pd.h>
|
||||
+#include <linux/usb/role.h>
|
||||
+#include <linux/usb/typec.h>
|
||||
+#include <linux/usb/typec_dp.h>
|
||||
+#include <linux/usb/typec_mux.h>
|
||||
+#include <linux/extcon-provider.h>
|
||||
+
|
||||
+struct typec_extcon {
|
||||
+ struct device *dev;
|
||||
+
|
||||
+ /* consumers */
|
||||
+ struct usb_role_switch *role_sw;
|
||||
+ struct typec_switch_dev *sw;
|
||||
+ struct typec_mux_dev *mux;
|
||||
+
|
||||
+ /* providers */
|
||||
+ struct extcon_dev *extcon;
|
||||
+ struct notifier_block extcon_nb;
|
||||
+
|
||||
+ /* cached state from typec controller */
|
||||
+ enum usb_role role;
|
||||
+ enum typec_orientation orientation;
|
||||
+ struct typec_altmode alt;
|
||||
+ unsigned long mode;
|
||||
+ bool has_alt;
|
||||
+ struct mutex lock;
|
||||
+};
|
||||
+
|
||||
+static const unsigned int typec_extcon_cable[] = {
|
||||
+ EXTCON_DISP_DP,
|
||||
+
|
||||
+ EXTCON_USB,
|
||||
+ EXTCON_USB_HOST,
|
||||
+
|
||||
+ EXTCON_CHG_USB_SDP,
|
||||
+ EXTCON_CHG_USB_CDP,
|
||||
+ EXTCON_CHG_USB_DCP,
|
||||
+ EXTCON_CHG_USB_ACA,
|
||||
+
|
||||
+ EXTCON_NONE,
|
||||
+};
|
||||
+
|
||||
+static void typec_extcon_set_cable(struct typec_extcon *tce, int id, bool on,
|
||||
+ union extcon_property_value prop_ss,
|
||||
+ union extcon_property_value prop_or)
|
||||
+{
|
||||
+ union extcon_property_value cur_ss, cur_or;
|
||||
+ bool prop_diff = false;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = extcon_get_property(tce->extcon, id,
|
||||
+ EXTCON_PROP_USB_SS, &cur_ss);
|
||||
+ if (ret || cur_ss.intval != prop_ss.intval)
|
||||
+ prop_diff = true;
|
||||
+
|
||||
+ ret = extcon_get_property(tce->extcon, id,
|
||||
+ EXTCON_PROP_USB_TYPEC_POLARITY, &cur_or);
|
||||
+ if (ret || cur_or.intval != prop_or.intval)
|
||||
+ prop_diff = true;
|
||||
+
|
||||
+ if (!on && extcon_get_state(tce->extcon, id)) {
|
||||
+ extcon_set_state_sync(tce->extcon, id, false);
|
||||
+ } else if (on && (!extcon_get_state(tce->extcon, id) || prop_diff)) {
|
||||
+ extcon_set_state(tce->extcon, id, true);
|
||||
+ extcon_set_property(tce->extcon, id,
|
||||
+ EXTCON_PROP_USB_SS, prop_ss);
|
||||
+ extcon_set_property(tce->extcon, id,
|
||||
+ EXTCON_PROP_USB_TYPEC_POLARITY, prop_or);
|
||||
+ extcon_sync(tce->extcon, id);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int typec_extcon_sync_extcon(struct typec_extcon *tce)
|
||||
+{
|
||||
+ union extcon_property_value prop_ss, prop_or;
|
||||
+ bool has_dp = false;
|
||||
+
|
||||
+ mutex_lock(&tce->lock);
|
||||
+
|
||||
+ /* connector is disconnected */
|
||||
+ if (tce->orientation == TYPEC_ORIENTATION_NONE) {
|
||||
+ typec_extcon_set_cable(tce, EXTCON_USB, false, prop_ss, prop_or);
|
||||
+ typec_extcon_set_cable(tce, EXTCON_USB_HOST, false, prop_ss, prop_or);
|
||||
+ typec_extcon_set_cable(tce, EXTCON_DISP_DP, false, prop_ss, prop_or);
|
||||
+
|
||||
+ goto out_unlock;
|
||||
+ }
|
||||
+
|
||||
+ prop_or.intval = tce->orientation == TYPEC_ORIENTATION_NORMAL ? 0 : 1;
|
||||
+ prop_ss.intval = 0;
|
||||
+
|
||||
+ if (tce->has_alt && tce->alt.svid == USB_TYPEC_DP_SID) {
|
||||
+ switch (tce->mode) {
|
||||
+ case TYPEC_STATE_SAFE:
|
||||
+ break;
|
||||
+ case TYPEC_DP_STATE_C:
|
||||
+ case TYPEC_DP_STATE_E:
|
||||
+ has_dp = true;
|
||||
+ break;
|
||||
+ case TYPEC_DP_STATE_D:
|
||||
+ has_dp = true;
|
||||
+ fallthrough;
|
||||
+ case TYPEC_STATE_USB:
|
||||
+ prop_ss.intval = 1;
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(tce->dev, "unhandled mux mode=%lu\n", tce->mode);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ typec_extcon_set_cable(tce, EXTCON_USB,
|
||||
+ tce->role == USB_ROLE_DEVICE, prop_ss, prop_or);
|
||||
+ typec_extcon_set_cable(tce, EXTCON_USB_HOST,
|
||||
+ tce->role == USB_ROLE_HOST, prop_ss, prop_or);
|
||||
+
|
||||
+ typec_extcon_set_cable(tce, EXTCON_DISP_DP, has_dp, prop_ss, prop_or);
|
||||
+
|
||||
+out_unlock:
|
||||
+ mutex_unlock(&tce->lock);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int typec_extcon_sw_set(struct typec_switch_dev *sw,
|
||||
+ enum typec_orientation orientation)
|
||||
+{
|
||||
+ struct typec_extcon *tce = typec_switch_get_drvdata(sw);
|
||||
+
|
||||
+ dev_dbg(tce->dev, "SW SET: orientation=%d\n", orientation);
|
||||
+
|
||||
+ mutex_lock(&tce->lock);
|
||||
+ tce->orientation = orientation;
|
||||
+ mutex_unlock(&tce->lock);
|
||||
+
|
||||
+ typec_extcon_sync_extcon(tce);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int typec_extcon_mux_set(struct typec_mux_dev *mux,
|
||||
+ struct typec_mux_state *state)
|
||||
+{
|
||||
+ struct typec_extcon *tce = typec_mux_get_drvdata(mux);
|
||||
+ struct typec_altmode *alt = state->alt;
|
||||
+
|
||||
+ dev_dbg(tce->dev, "MUX SET: state->mode=%lu\n", state->mode);
|
||||
+ if (alt)
|
||||
+ dev_dbg(tce->dev, " ...alt: svid=%04hx mode=%d vdo=%08x active=%u\n",
|
||||
+ alt->svid, alt->mode, alt->vdo, alt->active);
|
||||
+
|
||||
+ mutex_lock(&tce->lock);
|
||||
+ tce->mode = state->mode;
|
||||
+ tce->has_alt = alt != NULL;
|
||||
+ if (alt)
|
||||
+ tce->alt = *alt;
|
||||
+ mutex_unlock(&tce->lock);
|
||||
+
|
||||
+ typec_extcon_sync_extcon(tce);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int typec_extcon_usb_set_role(struct usb_role_switch *sw,
|
||||
+ enum usb_role role)
|
||||
+{
|
||||
+ struct typec_extcon *tce = usb_role_switch_get_drvdata(sw);
|
||||
+
|
||||
+ dev_dbg(tce->dev, "ROLE SET: role=%d\n", role);
|
||||
+
|
||||
+ mutex_lock(&tce->lock);
|
||||
+ tce->role = role;
|
||||
+ mutex_unlock(&tce->lock);
|
||||
+
|
||||
+ typec_extcon_sync_extcon(tce);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int typec_extcon_notifier(struct notifier_block *nb,
|
||||
+ unsigned long action, void *data)
|
||||
+{
|
||||
+ struct typec_extcon *tce = container_of(nb, struct typec_extcon, extcon_nb);
|
||||
+
|
||||
+ bool sdp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_SDP);
|
||||
+ bool cdp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_CDP);
|
||||
+ bool dcp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_DCP);
|
||||
+ bool usb = extcon_get_state(tce->extcon, EXTCON_USB);
|
||||
+ bool usb_host = extcon_get_state(tce->extcon, EXTCON_USB_HOST);
|
||||
+ bool dp = extcon_get_state(tce->extcon, EXTCON_DISP_DP);
|
||||
+
|
||||
+ dev_info(tce->dev, "extcon changed sdp=%d cdp=%d dcp=%d usb=%d usb_host=%d dp=%d\n",
|
||||
+ sdp, cdp, dcp, usb, usb_host, dp);
|
||||
+
|
||||
+ return NOTIFY_OK;
|
||||
+}
|
||||
+
|
||||
+static int typec_extcon_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct typec_switch_desc sw_desc = { };
|
||||
+ struct typec_mux_desc mux_desc = { };
|
||||
+ struct usb_role_switch_desc role_desc = { };
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ struct typec_extcon *tce;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ tce = devm_kzalloc(dev, sizeof(*tce), GFP_KERNEL);
|
||||
+ if (!tce)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ tce->dev = &pdev->dev;
|
||||
+ mutex_init(&tce->lock);
|
||||
+ tce->mode = TYPEC_STATE_SAFE;
|
||||
+
|
||||
+ sw_desc.drvdata = tce;
|
||||
+ sw_desc.fwnode = dev->fwnode;
|
||||
+ sw_desc.set = typec_extcon_sw_set;
|
||||
+
|
||||
+ tce->sw = typec_switch_register(dev, &sw_desc);
|
||||
+ if (IS_ERR(tce->sw))
|
||||
+ return dev_err_probe(dev, PTR_ERR(tce->sw),
|
||||
+ "Error registering typec switch\n");
|
||||
+
|
||||
+ mux_desc.drvdata = tce;
|
||||
+ mux_desc.fwnode = dev->fwnode;
|
||||
+ mux_desc.set = typec_extcon_mux_set;
|
||||
+
|
||||
+ tce->mux = typec_mux_register(dev, &mux_desc);
|
||||
+ if (IS_ERR(tce->mux)) {
|
||||
+ ret = dev_err_probe(dev, PTR_ERR(tce->mux),
|
||||
+ "Error registering typec mux\n");
|
||||
+ goto err_sw;
|
||||
+ }
|
||||
+
|
||||
+ role_desc.driver_data = tce;
|
||||
+ role_desc.fwnode = dev->fwnode;
|
||||
+ role_desc.name = fwnode_get_name(dev->fwnode);
|
||||
+ role_desc.set = typec_extcon_usb_set_role;
|
||||
+
|
||||
+ tce->role_sw = usb_role_switch_register(dev, &role_desc);
|
||||
+ if (IS_ERR(tce->role_sw)) {
|
||||
+ ret = dev_err_probe(dev, PTR_ERR(tce->role_sw),
|
||||
+ "Error registering USB role switch\n");
|
||||
+ goto err_mux;
|
||||
+ }
|
||||
+
|
||||
+ tce->extcon = devm_extcon_dev_allocate(dev, typec_extcon_cable);
|
||||
+ if (IS_ERR(tce->extcon)) {
|
||||
+ ret = PTR_ERR(tce->extcon);
|
||||
+ goto err_role;
|
||||
+ }
|
||||
+
|
||||
+ ret = devm_extcon_dev_register(dev, tce->extcon);
|
||||
+ if (ret) {
|
||||
+ ret = dev_err_probe(dev, ret, "failed to register extcon device\n");
|
||||
+ goto err_role;
|
||||
+ }
|
||||
+
|
||||
+ extcon_set_property_capability(tce->extcon, EXTCON_USB,
|
||||
+ EXTCON_PROP_USB_SS);
|
||||
+ extcon_set_property_capability(tce->extcon, EXTCON_USB,
|
||||
+ EXTCON_PROP_USB_TYPEC_POLARITY);
|
||||
+ extcon_set_property_capability(tce->extcon, EXTCON_USB_HOST,
|
||||
+ EXTCON_PROP_USB_SS);
|
||||
+ extcon_set_property_capability(tce->extcon, EXTCON_USB_HOST,
|
||||
+ EXTCON_PROP_USB_TYPEC_POLARITY);
|
||||
+ extcon_set_property_capability(tce->extcon, EXTCON_DISP_DP,
|
||||
+ EXTCON_PROP_USB_SS);
|
||||
+ extcon_set_property_capability(tce->extcon, EXTCON_DISP_DP,
|
||||
+ EXTCON_PROP_USB_TYPEC_POLARITY);
|
||||
+
|
||||
+ tce->extcon_nb.notifier_call = typec_extcon_notifier;
|
||||
+ ret = devm_extcon_register_notifier_all(dev, tce->extcon, &tce->extcon_nb);
|
||||
+ if (ret) {
|
||||
+ dev_err_probe(dev, ret, "Failed to register extcon notifier\n");
|
||||
+ goto err_role;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_role:
|
||||
+ usb_role_switch_unregister(tce->role_sw);
|
||||
+err_mux:
|
||||
+ typec_mux_unregister(tce->mux);
|
||||
+err_sw:
|
||||
+ typec_switch_unregister(tce->sw);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void typec_extcon_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct typec_extcon *tce = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ usb_role_switch_unregister(tce->role_sw);
|
||||
+ typec_mux_unregister(tce->mux);
|
||||
+ typec_switch_unregister(tce->sw);
|
||||
+}
|
||||
+
|
||||
+static struct of_device_id typec_extcon_of_match_table[] = {
|
||||
+ { .compatible = "linux,typec-extcon-bridge" },
|
||||
+ { },
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, typec_extcon_of_match_table);
|
||||
+
|
||||
+static struct platform_driver typec_extcon_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "typec-extcon",
|
||||
+ .of_match_table = typec_extcon_of_match_table,
|
||||
+ },
|
||||
+ .probe = typec_extcon_probe,
|
||||
+ .remove = typec_extcon_remove,
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(typec_extcon_driver);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("Ondrej Jirman <megi@xff.cz>");
|
||||
+MODULE_DESCRIPTION("typec -> extcon bridge driver");
|
||||
--
|
||||
Armbian
|
||||
|
||||
Reference in New Issue
Block a user