TCPCI spec allows TCPC hardware to autonomously discharge the vbus
capacitance upon disconnect. The expectation is that the TCPM enables
AutoDischargeDisconnect while entering SNK/SRC_ATTACHED states. Hardware
then automously discharges vbus when the vbus falls below a certain
threshold i.e. VBUS_SINK_DISCONNECT_THRESHOLD.
Apart from enabling the vbus discharge circuit, AutoDischargeDisconnect
is also used a flag to move TCPCI based TCPC implementations into
Attached.Snk/Attached.Src state as mentioned in
Figure 4-15. TCPC State Diagram before a Connection of the
USB Type-C Port Controller Interface Specification.
In such TCPC implementations, setting AutoDischargeDisconnect would
prevent TCPC into entering "Connection_Invalid" state as well.
Signed-off-by: Badhri Jagan Sridharan
Change-Id: I09c407eb228d69eb1259008eeb14c429b0fda765
---
drivers/usb/typec/tcpm/tcpm.c | 80 +--
include/linux/usb/tcpm.h | 16 +++
2 files changed, 92 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 6ea4613af905..fe58cf908144 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -1660,6 +1660,25 @@ static void tcpm_handle_alert(struct tcpm_port *port,
const __le32 *payload,
}
}
+static int tcpm_set_auto_vbus_discharge_threshold(struct tcpm_port *port, enum
typec_role port_role,
+ enum typec_pwr_opmode mode,
bool pps_active,
+ u32 requested_vbus_voltage)
+{
+ int ret;
+
+ if (!port->tcpc->set_auto_vbus_discharge_threshold)
+ return 0;
+
+ ret = port->tcpc->set_auto_vbus_discharge_threshold(port->tcpc,
port_role, mode, pps_active,
+
requested_vbus_voltage);
+ tcpm_log_force(port,
+ "set_auto_vbus_discharge_threshold pwr_role:%s mode:%d
pps_active:%c vbus:%u ret:%d",
+ port_role == TYPEC_SINK ? "sink" : "source", mode,
pps_active ? 'y' : 'n',
+ requested_vbus_voltage, ret);
+
+ return ret;
+}
+
static void tcpm_pd_data_request(struct tcpm_port *port,
const struct pd_message *msg)
{
@@ -1829,6 +1848,10 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
port->current_limit,
port->supply_voltage);
port->explicit_contract = true;
+ tcpm_set_auto_vbus_discharge_threshold(port,
TYPEC_SINK,
+
TYPEC_PWR_MODE_PD,
+
port->pps_data.active,
+
port->supply_voltage);
tcpm_set_state(port, SNK_READY, 0);
} else {
/*
@@ -2743,8 +2766,14 @@ static int tcpm_src_attach(struct tcpm_port *port)
if (ret < 0)
return ret;
- ret = tcpm_set_roles(port, true, TYPEC_SOURCE,
-tcpm_data_role_for_source(port));
+ if (port->tcpc->enable_auto_vbus_discharge) {
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_SOURCE,
TYPEC_PWR_MODE_USB,
+ false, VSAFE5V);
+ ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true);
+ tcpm_log_force(port, "enable vbus discharge ret:%d", ret);
+ }
+
+ ret = tcpm_set_roles(port, true, TYPEC_SOURCE,
tcpm_data_role_for_sink(port));
if (ret < 0)
return ret;
@@ -2811,6 +2840,12 @@ static void tcpm_unregister_altmodes(struct tcpm_port
*port)
static void tcpm_reset_port(struct tcpm_port *port)
{
+ int ret;
+
+ if (port->tcpc->enable_auto_vbus_discharge) {
+ ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, false);
+ tcpm_log_force(port, "Disable vbus discharge ret:%d", ret);
+ }
tcpm_unregister_altmodes(port);
tcpm_typec_disconnect(port);
port->attached = false;
@@ -2875,8 +2910,14 @@ static int tcpm_snk_attach(struct tcpm_port *port)
if (ret < 0)
return ret;
- ret = tcpm_set_roles(port, true, TYPEC_SINK,
-tcpm_data_role_for_sink(port));
+ if (port->tcpc->enable_auto_vbus_discharge) {
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_SINK,
TYPEC_PWR_MODE_USB, false,
+ VSAFE5V);
+ ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, true);
+ tcpm_log_force(port, "enable vbus