bool attached;
bool connected;
enum typec_port_type port_type;
+
+ /*
+ * Set to true when vbus is greater than VSAFE5V min.
+ * Set to false when vbus falls below vSinkDisconnect max threshold.
+ */
bool vbus_present;
+
+ /*
+ * Set to true when vbus is less than VSAFE0V max.
+ * Set to false when vbus is greater than VSAFE0V max.
+ */
+ bool vbus_vsafe0v;
+
bool vbus_never_low;
bool vbus_source;
bool vbus_charge;
else if (tcpm_port_is_audio(port))
tcpm_set_state(port, AUDIO_ACC_ATTACHED,
PD_T_CC_DEBOUNCE);
- else if (tcpm_port_is_source(port))
+ else if (tcpm_port_is_source(port) && port->vbus_vsafe0v)
tcpm_set_state(port,
tcpm_try_snk(port) ? SNK_TRY
: SRC_ATTACHED,
{
tcpm_log_force(port, "VBUS on");
port->vbus_present = true;
+ /*
+ * When vbus_present is true i.e. Voltage at VBUS is greater than VSAFE5V implicitly
+ * states that vbus is not at VSAFE0V, hence clear the vbus_vsafe0v flag here.
+ */
+ port->vbus_vsafe0v = false;
+
switch (port->state) {
case SNK_TRANSITION_SINK_VBUS:
port->explicit_contract = true;
case SNK_HARD_RESET_SINK_OFF:
tcpm_set_state(port, SNK_HARD_RESET_WAIT_VBUS, 0);
break;
- case SRC_HARD_RESET_VBUS_OFF:
- /*
- * After establishing the vSafe0V voltage condition on VBUS, the Source Shall wait
- * tSrcRecover before re-applying VCONN and restoring VBUS to vSafe5V.
- */
- tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER);
- break;
case HARD_RESET_SEND:
break;
-
case SNK_TRY:
/* Do nothing, waiting for timeout */
break;
}
}
+static void _tcpm_pd_vbus_vsafe0v(struct tcpm_port *port)
+{
+ tcpm_log_force(port, "VBUS VSAFE0V");
+ port->vbus_vsafe0v = true;
+ switch (port->state) {
+ case SRC_HARD_RESET_VBUS_OFF:
+ /*
+ * After establishing the vSafe0V voltage condition on VBUS, the Source Shall wait
+ * tSrcRecover before re-applying VCONN and restoring VBUS to vSafe5V.
+ */
+ tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER);
+ break;
+ case SRC_ATTACH_WAIT:
+ if (tcpm_port_is_source(port))
+ tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED,
+ PD_T_CC_DEBOUNCE);
+ break;
+ default:
+ break;
+ }
+}
+
static void _tcpm_pd_hard_reset(struct tcpm_port *port)
{
tcpm_log_force(port, "Received hard reset");
bool vbus;
vbus = port->tcpc->get_vbus(port->tcpc);
- if (vbus)
+ if (vbus) {
_tcpm_pd_vbus_on(port);
- else
+ } else {
_tcpm_pd_vbus_off(port);
+ /*
+ * When TCPC does not support detecting vsafe0v voltage level,
+ * treat vbus absent as vsafe0v. Else invoke is_vbus_vsafe0v
+ * to see if vbus has discharge to VSAFE0V.
+ */
+ if (!port->tcpc->is_vbus_vsafe0v ||
+ port->tcpc->is_vbus_vsafe0v(port->tcpc))
+ _tcpm_pd_vbus_vsafe0v(port);
+ }
}
if (events & TCPM_CC_EVENT) {
enum typec_cc_status cc1, cc2;
* will be turned on. requested_vbus_voltage is set to 0 when vbus
* is going to disappear knowingly i.e. during PR_SWAP and
* HARD_RESET etc.
+ * @is_vbus_vsafe0v:
+ * Optional; TCPCI spec based TCPC implementations are expected to
+ * detect VSAFE0V voltage level at vbus. When detection of VSAFE0V
+ * is supported by TCPC, set this callback for TCPM to query
+ * whether vbus is at VSAFE0V when needed.
+ * Returns true when vbus is at VSAFE0V, false otherwise.
*/
struct tcpc_dev {
struct fwnode_handle *fwnode;
int (*enable_auto_vbus_discharge)(struct tcpc_dev *dev, bool enable);
int (*set_auto_vbus_discharge_threshold)(struct tcpc_dev *dev, enum typec_pwr_opmode mode,
bool pps_active, u32 requested_vbus_voltage);
+ bool (*is_vbus_vsafe0v)(struct tcpc_dev *dev);
};
struct tcpm_port;