From 1f16d824a8bdc832d591373d4981fa7fe50b23e3 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 14 Sep 2020 15:18:33 -0700 Subject: [PATCH] ANDROID: usb: typec: tcpm: Add chip level callback to check for contaminant TCPC validation revealed that when the Type-C port is exposed to water, TCPC stops toggling while reporting Rp or Rd in CC pins. However, At the end of debounce period, CC pins report OPEN state which causes TCPM to re-enable toggling. The loop continues to happen and keeps the cpu busy and burns power while wasting cpu cycles. To overcome this issue, When TCPM detects CC open at the end of debounce, call chip level callbacks to check for contaminant in the connector. Bug: 168544734 Signed-off-by: Badhri Jagan Sridharan Change-Id: If28027bdacfdf8511be0ea54766970fb73bc3d96 --- drivers/usb/typec/tcpm/tcpm.c | 52 +++++++++++++++++++++++++++++++++-- include/linux/usb/tcpm.h | 10 +++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index a48e3f90d196..b372f8ea6d25 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -330,6 +330,9 @@ struct tcpm_port { /* port belongs to a self powered device */ bool self_powered; + /* Port is still in tCCDebounce */ + bool debouncing; + #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -713,6 +716,21 @@ static int tcpm_set_vconn(struct tcpm_port *port, bool enable) return ret; } +bool tcpm_is_debouncing(struct tcpm_port *port) +{ + bool debounce; + + if (!port) + return false; + + mutex_lock(&port->lock); + debounce = port->debouncing; + mutex_unlock(&port->lock); + + return debounce; +} +EXPORT_SYMBOL_GPL(tcpm_is_debouncing); + static u32 tcpm_get_current_limit(struct tcpm_port *port) { enum typec_cc_status cc; @@ -2722,6 +2740,7 @@ static int tcpm_src_attach(struct tcpm_port *port) port->partner = NULL; port->attached = true; + port->debouncing = false; port->send_discover = true; return 0; @@ -2830,6 +2849,7 @@ static int tcpm_snk_attach(struct tcpm_port *port) port->partner = NULL; port->attached = true; + port->debouncing = false; port->send_discover = true; return 0; @@ -2857,6 +2877,7 @@ static int tcpm_acc_attach(struct tcpm_port *port) tcpm_typec_connect(port); port->attached = true; + port->debouncing = false; return 0; } @@ -2940,6 +2961,15 @@ static void run_state_machine(struct tcpm_port *port) if (!port->non_pd_role_swap) tcpm_swap_complete(port, -ENOTCONN); tcpm_src_detach(port); + if (port->debouncing) { + port->debouncing = false; + if (port->tcpc->check_contaminant && + port->tcpc->check_contaminant(port->tcpc)) { + /* Contaminant detection would handle toggling */ + tcpm_set_state(port, TOGGLING, 0); + break; + } + } if (tcpm_start_toggling(port, tcpm_rp_cc(port))) { tcpm_set_state(port, TOGGLING, 0); break; @@ -2949,6 +2979,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SNK_UNATTACHED, PD_T_DRP_SNK); break; case SRC_ATTACH_WAIT: + port->debouncing = true; if (tcpm_port_is_debug(port)) tcpm_set_state(port, DEBUG_ACC_ATTACHED, PD_T_CC_DEBOUNCE); @@ -2963,6 +2994,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_TRY: + port->debouncing = false; port->try_snk_count++; /* * Requirements: @@ -3139,6 +3171,15 @@ static void run_state_machine(struct tcpm_port *port) tcpm_swap_complete(port, -ENOTCONN); tcpm_pps_complete(port, -ENOTCONN); tcpm_snk_detach(port); + if (port->debouncing) { + port->debouncing = false; + if (port->tcpc->check_contaminant && + port->tcpc->check_contaminant(port->tcpc)) { + /* Contaminant detection would handle toggling */ + tcpm_set_state(port, TOGGLING, 0); + break; + } + } if (tcpm_start_toggling(port, TYPEC_CC_RD)) { tcpm_set_state(port, TOGGLING, 0); break; @@ -3148,6 +3189,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_UNATTACHED, PD_T_DRP_SRC); break; case SNK_ATTACH_WAIT: + port->debouncing = true; if ((port->cc1 == TYPEC_CC_OPEN && port->cc2 != TYPEC_CC_OPEN) || (port->cc1 != TYPEC_CC_OPEN && @@ -3159,17 +3201,20 @@ static void run_state_machine(struct tcpm_port *port) PD_T_PD_DEBOUNCE); break; case SNK_DEBOUNCED: - if (tcpm_port_is_disconnected(port)) + if (tcpm_port_is_disconnected(port)) { tcpm_set_state(port, SNK_UNATTACHED, PD_T_PD_DEBOUNCE); - else if (port->vbus_present) + } else if (port->vbus_present) { tcpm_set_state(port, tcpm_try_src(port) ? SRC_TRY : SNK_ATTACHED, 0); - else + port->debouncing = false; + } else { /* Wait for VBUS, but not forever */ tcpm_set_state(port, PORT_RESET, PD_T_PS_SOURCE_ON); + port->debouncing = false; + } break; case SRC_TRY: @@ -3987,6 +4032,7 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) case SNK_TRYWAIT_DEBOUNCE: break; case SNK_ATTACH_WAIT: + port->debouncing = false; tcpm_set_state(port, SNK_UNATTACHED, 0); break; diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 89f58760cf48..9c2cc201f0af 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -80,6 +80,14 @@ enum tcpm_transmit_type { * @pd_transmit:Called to transmit PD message * @mux: Pointer to multiplexer data * @set_bist_data: Turn on/off bist data mode for compliance testing + * @check_contaminant: + * Optional; The callback is called when CC pins report open status + * at the end of the toggling period. Chip level drivers are + * expected to check for contaminant and re-enable toggling if + * needed. When 0 is not returned, check_contaminant is expected to + * restart toggling after checking the connector for contaminant. + * This forces the TCPM state machine to tranistion to TOGGLING state + * without calling start_toggling callback. */ struct tcpc_dev { struct fwnode_handle *fwnode; @@ -105,6 +113,7 @@ struct tcpc_dev { int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type, const struct pd_message *msg); int (*set_bist_data)(struct tcpc_dev *dev, bool on); + int (*check_contaminant)(struct tcpc_dev *dev); }; struct tcpm_port; @@ -120,5 +129,6 @@ void tcpm_pd_transmit_complete(struct tcpm_port *port, enum tcpm_transmit_status status); void tcpm_pd_hard_reset(struct tcpm_port *port); void tcpm_tcpc_reset(struct tcpm_port *port); +bool tcpm_is_debouncing(struct tcpm_port *tcpm); #endif /* __LINUX_USB_TCPM_H */