From patchwork Fri Aug 26 08:39:58 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Badhri Jagan Sridharan X-Patchwork-Id: 12955644 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A6996ECAAA3 for ; Fri, 26 Aug 2022 08:41:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245404AbiHZIlY (ORCPT ); Fri, 26 Aug 2022 04:41:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58278 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343698AbiHZIki (ORCPT ); Fri, 26 Aug 2022 04:40:38 -0400 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CB79628E08 for ; Fri, 26 Aug 2022 01:40:07 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-3363b1dffa0so14841197b3.23 for ; Fri, 26 Aug 2022 01:40:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:mime-version:date:from:to:cc; bh=5Nio9wW+lrrk0jgd+Y112TI5asoBTwAgUJzWnWe5Nhw=; b=lc/DEvA28YWRszUXEzeZiZBVU33lgrVMfzjNRLDZdc2lRvjAZToaPDsCieBTLyyGQa mIUmjfEOYpO9/mMQoZ79PsRbPjy1ugF6JXOpcEuFXrFulmpYBwn/FqAZXZUrfh+duRae hL/Wmrw1HNOfwcYmlIJpvDyqNAaMQrx0t9zsr1HfhQLVlU4FVFFLtP3C2ENDiABYjH/l GRHPTZjMj2399RnPkmhjxwvqv0nAwkX3VpfFfUCYl5soSOD3Sqc95t+1tBhve/6OG9sJ 3eapzduIfdIMZsDiMWWF9lVrASgwhRR9UNKj4mVd1y5aYLFAPOhnnimMTcMASf3KYtr/ AgDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:mime-version:date:x-gm-message-state :from:to:cc; bh=5Nio9wW+lrrk0jgd+Y112TI5asoBTwAgUJzWnWe5Nhw=; b=caBqdXw4gsCL/NA/m2ub+d1czjHI4roSQMtVp3ykJ/ibNLyPntXmKAf8L4UYY55wTi v77xU7V1VlPutz9JSfd5iq3Sgwc71Dms2MPYYJrf4Yrr8PLuMribBxQdO/A/a2/xj9XC gGlctv5At8U1B8rha4GgX+UeMi65F6Qf1ePEZid9UwWHlOlYXNchuM902RzocIPYbu5Z udzfRLfcReU+CvWp/dzOpXD+WVsK3Ws0Xu16wRO/H49n6Lnkgq5kkiYu94hv+RLjA//D 0vd45XU2heTY+MUJwCoLq+y4QCEN1SWJvdHvC+Qwl4XcPRiNNtbOE4GSa9LAtBTvE9DD jNyg== X-Gm-Message-State: ACgBeo1y4STySecqdbedbnI7JFGJYBT2CQNk/eaRMVpI8c/YzacXYRQi Y1pLpajXBOKx+2tt3ECqzczV5H3TCYM= X-Google-Smtp-Source: AA6agR4Uav3ReYTOQOwjr3GgXzhPHy3miFnlZKIUaO7TrO0USxQIECRWCkEtZY8fF0mFzaNA3fs9N3qEZlM= X-Received: from badhri.mtv.corp.google.com ([2620:15c:211:201:9c82:622c:af33:39f2]) (user=badhri job=sendgmr) by 2002:a81:594:0:b0:33d:a498:167c with SMTP id 142-20020a810594000000b0033da498167cmr7809363ywf.59.1661503206669; Fri, 26 Aug 2022 01:40:06 -0700 (PDT) Date: Fri, 26 Aug 2022 01:39:58 -0700 Mime-Version: 1.0 X-Mailer: git-send-email 2.37.2.672.g94769d06f0-goog Message-ID: <20220826084001.3341215-1-badhri@google.com> Subject: [PATCH v1 1/4] usb: typec: tcpm: Add callbacks to mitigate wakeups due to contaminant From: Badhri Jagan Sridharan To: Guenter Roeck , Heikki Krogerus , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Kyle Tso , Badhri Jagan Sridharan Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org On some of the TCPC implementations, when the Type-C port is exposed to contaminants, such as water, TCPC stops toggling while reporting OPEN either by the time TCPM reads CC pin status or during CC debounce window. This causes TCPM to be stuck in TOGGLING state. If TCPM is made to restart toggling, the behavior recurs causing redundant CPU wakeups till the USB-C port is free of contaminant. [206199.287817] CC1: 0 -> 0, CC2: 0 -> 0 [state TOGGLING, polarity 0, disconnected] [206199.640337] CC1: 0 -> 0, CC2: 0 -> 0 [state TOGGLING, polarity 0, disconnected] [206199.985789] CC1: 0 -> 0, CC2: 0 -> 0 [state TOGGLING, polarity 0, disconnected] ... To mitigate redundant TCPM wakeups, TCPCs which do have the needed hardware can implement the check_contaminant callback which is invoked by TCPM to evaluate for presence of contaminant. Lower level TCPC driver can restart toggling through TCPM_PORT_CLEAN event when the driver detects that USB-C port is free of contaminant. check_contaminant callback also passes the disconnect_while_debounce flag which when true denotes that the CC pins transitioned to OPEN state during the CC debounce window. Signed-off-by: Badhri Jagan Sridharan --- drivers/usb/typec/tcpm/tcpm.c | 59 +++++++++++++++++++++++++++++++++-- include/linux/usb/tcpm.h | 7 +++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index ea5a917c51b1..072c5a2817d0 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -36,6 +36,7 @@ #define FOREACH_STATE(S) \ S(INVALID_STATE), \ S(TOGGLING), \ + S(CHECK_CONTAMINANT), \ S(SRC_UNATTACHED), \ S(SRC_ATTACH_WAIT), \ S(SRC_ATTACHED), \ @@ -249,6 +250,7 @@ enum frs_typec_current { #define TCPM_RESET_EVENT BIT(2) #define TCPM_FRS_EVENT BIT(3) #define TCPM_SOURCING_VBUS BIT(4) +#define TCPM_PORT_CLEAN BIT(5) #define LOG_BUFFER_ENTRIES 1024 #define LOG_BUFFER_ENTRY_SIZE 128 @@ -483,6 +485,13 @@ struct tcpm_port { * SNK_READY for non-pd link. */ bool slow_charger_loop; + + /* + * When true indicates CC pins transitioned to OPEN state while + * debouncing(tCCDebounce). + */ + bool disconnect_while_debouncing; + #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -3663,6 +3672,7 @@ static int tcpm_src_attach(struct tcpm_port *port) port->partner = NULL; port->attached = true; + port->disconnect_while_debouncing = false; port->send_discover = true; return 0; @@ -3797,6 +3807,7 @@ static int tcpm_snk_attach(struct tcpm_port *port) port->partner = NULL; port->attached = true; + port->disconnect_while_debouncing = false; port->send_discover = true; return 0; @@ -3824,6 +3835,7 @@ static int tcpm_acc_attach(struct tcpm_port *port) tcpm_typec_connect(port); port->attached = true; + port->disconnect_while_debouncing = false; return 0; } @@ -3908,11 +3920,22 @@ static void run_state_machine(struct tcpm_port *port) switch (port->state) { case TOGGLING: break; + case CHECK_CONTAMINANT: + port->tcpc->check_contaminant(port->tcpc, port->disconnect_while_debouncing); + port->disconnect_while_debouncing = false; + break; /* SRC states */ case SRC_UNATTACHED: if (!port->non_pd_role_swap) tcpm_swap_complete(port, -ENOTCONN); tcpm_src_detach(port); + if (port->disconnect_while_debouncing) { + /* Check for contaminant when port reports disconnected while debouncing */ + if (port->tcpc->check_contaminant) { + tcpm_set_state(port, CHECK_CONTAMINANT, 0); + break; + } + } if (tcpm_start_toggling(port, tcpm_rp_cc(port))) { tcpm_set_state(port, TOGGLING, 0); break; @@ -3922,6 +3945,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->disconnect_while_debouncing = true; if (tcpm_port_is_debug(port)) tcpm_set_state(port, DEBUG_ACC_ATTACHED, PD_T_CC_DEBOUNCE); @@ -3936,6 +3960,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_TRY: + port->disconnect_while_debouncing = false; port->try_snk_count++; /* * Requirements: @@ -4150,6 +4175,13 @@ 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->disconnect_while_debouncing) { + port->disconnect_while_debouncing = false; + if (port->tcpc->check_contaminant) { + tcpm_set_state(port, CHECK_CONTAMINANT, 0); + break; + } + } if (tcpm_start_toggling(port, TYPEC_CC_RD)) { tcpm_set_state(port, TOGGLING, 0); break; @@ -4159,6 +4191,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->disconnect_while_debouncing = true; if ((port->cc1 == TYPEC_CC_OPEN && port->cc2 != TYPEC_CC_OPEN) || (port->cc1 != TYPEC_CC_OPEN && @@ -4170,14 +4203,16 @@ 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); + port->disconnect_while_debouncing = false; + } break; case SRC_TRY: port->try_src_count++; @@ -4925,6 +4960,12 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, tcpm_set_state(port, SRC_ATTACH_WAIT, 0); else if (tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_ATTACH_WAIT, 0); + /* Check for contaminant when port reports disconnected while toggling */ + else if (tcpm_port_is_disconnected(port) && port->tcpc->check_contaminant) + tcpm_set_state(port, CHECK_CONTAMINANT, 0); + break; + case CHECK_CONTAMINANT: + /* Wait for Toggling to be resumed */ break; case SRC_UNATTACHED: case ACC_UNATTACHED: @@ -5225,6 +5266,7 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) case SNK_ATTACH_WAIT: case SNK_DEBOUNCED: /* Do nothing, as TCPM is still waiting for vbus to reaach VSAFE5V to connect */ + port->disconnect_while_debouncing = false; break; case SNK_NEGOTIATE_CAPABILITIES: @@ -5425,6 +5467,10 @@ static void tcpm_pd_event_handler(struct kthread_work *work) port->vbus_source = true; _tcpm_pd_vbus_on(port); } + if (events & TCPM_PORT_CLEAN) { + tcpm_log(port, "port clean"); + tcpm_set_state(port, TOGGLING, 0); + } spin_lock(&port->pd_event_lock); } @@ -5477,6 +5523,15 @@ void tcpm_sourcing_vbus(struct tcpm_port *port) } EXPORT_SYMBOL_GPL(tcpm_sourcing_vbus); +void tcpm_port_clean(struct tcpm_port *port) +{ + spin_lock(&port->pd_event_lock); + port->pd_events |= TCPM_PORT_CLEAN; + spin_unlock(&port->pd_event_lock); + kthread_queue_work(port->wq, &port->event_work); +} +EXPORT_SYMBOL_GPL(tcpm_port_clean); + static void tcpm_enable_frs_work(struct kthread_work *work) { struct tcpm_port *port = container_of(work, struct tcpm_port, enable_frs); diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index bffc8d3e14ad..436563d91a49 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -114,6 +114,11 @@ enum tcpm_transmit_type { * Optional; The USB Communications Capable bit indicates if port * partner is capable of communication over the USB data lines * (e.g. D+/- or SS Tx/Rx). Called to notify the status of the bit. + * @check_contaminant: + * Optional; The callback is called when CC pins report open status + * at the end of the deboumce period or when the port is still + * toggling. Chip level drivers are expected to check for contaminant + * and call tcpm_clean_port when the port is clean. */ struct tcpc_dev { struct fwnode_handle *fwnode; @@ -148,6 +153,7 @@ struct tcpc_dev { bool pps_active, u32 requested_vbus_voltage); bool (*is_vbus_vsafe0v)(struct tcpc_dev *dev); void (*set_partner_usb_comm_capable)(struct tcpc_dev *dev, bool enable); + void (*check_contaminant)(struct tcpc_dev *dev, bool disconnect_while_debouncing); }; struct tcpm_port; @@ -165,5 +171,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); +void tcpm_port_clean(struct tcpm_port *port); #endif /* __LINUX_USB_TCPM_H */ From patchwork Fri Aug 26 08:39:59 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Badhri Jagan Sridharan X-Patchwork-Id: 12955645 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B1CECECAAA3 for ; Fri, 26 Aug 2022 08:41:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245438AbiHZIl2 (ORCPT ); Fri, 26 Aug 2022 04:41:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58906 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343705AbiHZIkj (ORCPT ); Fri, 26 Aug 2022 04:40:39 -0400 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5606E24963 for ; Fri, 26 Aug 2022 01:40:11 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-33dd097f993so15675077b3.10 for ; Fri, 26 Aug 2022 01:40:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc; bh=RLmxwVwtne7Ris2nr8dSsVg7os+UaP8tQY3U04GCx/Q=; b=bUWPHNUvCcT+uV6k7KJHdeY5B+TXt9tNnJE7NjCcjfnWH9S5GYXggIwBwIPylOO2Zg 8xZmT4s/Q/73gUdWXlzpv9vTZm2ZFGl+kC4vEjUva2NNG9Tebr87+w175hJVn4IcTWUw wuZBDMDfMNeuEcIhIUQJmzsPWI7rZ69942HHPnkyC3RV56Nv+k6p/vm82de5pHQLQ2+K vxVx/Sh+Bgt+9EvFX3+Gga6YEl0NIClbj61smG9Ygn9xvdH4obcbeW3F3JanL1CLeEfu qPOpF0H9O/Px4CsoqMzDu68YvyEbZXVr2z5PYOqhmohrJ0vWDRukGHIjPYBTOez1WowK Lp6g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc; bh=RLmxwVwtne7Ris2nr8dSsVg7os+UaP8tQY3U04GCx/Q=; b=4IubFzzNPJ7U9sITLZ9s+ps6ZiCarUcFSEnZnEt7myJU0WckOO8P4UsyNjYKKqo4Cv p5Of8je7nlNaNoyuuQ5f56V4NmjjOWHKaBH5Mo8JzCmw0+ngI1yRcycMHG7K/YbXAc80 1ANh3kKkbUgjZIfGqy7+62jK8smN/S+BvHJpB1cbA+HlOECkHIprXasu4QSdUlvn18w+ YdLZvM59QNi7SNoiI7GyDdEWIuSIGHzPbq+FDXKTZJXGI5/EypLG8zjkTUNybnSr3xps zh7f3dYfmjdGnXAJn0ho28KoolR4JYj9rOgJ5K94vvl14ZKntox09nJNLfMPKVG0a31a CHCg== X-Gm-Message-State: ACgBeo1BYf4anFJM5QpwSPZZUOEZvporbsY2i21ZbBCmhIR4dy9/Kdo5 kwLO5ZIZMQQCO1yIUF4IG2jvzS/8TMw= X-Google-Smtp-Source: AA6agR4KLKITQOlDJ02mlSvMKGIjTT/6g02Bqj3cS1aQg1rBbh1nvTiFY+LvtnmVm7uVG74q+zc0+lIAL2M= X-Received: from badhri.mtv.corp.google.com ([2620:15c:211:201:9c82:622c:af33:39f2]) (user=badhri job=sendgmr) by 2002:a05:6902:722:b0:695:62a2:f22c with SMTP id l2-20020a056902072200b0069562a2f22cmr7071146ybt.272.1661503210277; Fri, 26 Aug 2022 01:40:10 -0700 (PDT) Date: Fri, 26 Aug 2022 01:39:59 -0700 In-Reply-To: <20220826084001.3341215-1-badhri@google.com> Mime-Version: 1.0 References: <20220826084001.3341215-1-badhri@google.com> X-Mailer: git-send-email 2.37.2.672.g94769d06f0-goog Message-ID: <20220826084001.3341215-2-badhri@google.com> Subject: [PATCH v1 2/4] usb: typec: tcpci: Add callback for evaluating contaminant presence From: Badhri Jagan Sridharan To: Guenter Roeck , Heikki Krogerus , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Kyle Tso , Badhri Jagan Sridharan Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org This change adds callback to evaluate presence of contaminant in the TCPCI layer. Signed-off-by: Badhri Jagan Sridharan --- drivers/usb/typec/tcpm/tcpci.c | 9 +++++++++ include/linux/usb/tcpci.h | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 50674ecf430d..53f6fad61781 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -403,6 +403,14 @@ static void tcpci_frs_sourcing_vbus(struct tcpc_dev *dev) tcpci->data->frs_sourcing_vbus(tcpci, tcpci->data); } +static void tcpci_check_contaminant(struct tcpc_dev *dev, bool disconnect_while_debouncing) +{ + struct tcpci *tcpci = tcpc_to_tcpci(dev); + + if (tcpci->data->check_contaminant) + tcpci->data->check_contaminant(tcpci, tcpci->data, disconnect_while_debouncing); +} + static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable) { struct tcpci *tcpci = tcpc_to_tcpci(tcpc); @@ -777,6 +785,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) tcpci->tcpc.enable_frs = tcpci_enable_frs; tcpci->tcpc.frs_sourcing_vbus = tcpci_frs_sourcing_vbus; tcpci->tcpc.set_partner_usb_comm_capable = tcpci_set_partner_usb_comm_capable; + tcpci->tcpc.check_contaminant = tcpci_check_contaminant; if (tcpci->data->auto_discharge_disconnect) { tcpci->tcpc.enable_auto_vbus_discharge = tcpci_enable_auto_vbus_discharge; diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h index 17657451c762..250b34ccbf9c 100644 --- a/include/linux/usb/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -188,6 +188,11 @@ struct tcpci; * Optional; The USB Communications Capable bit indicates if port * partner is capable of communication over the USB data lines * (e.g. D+/- or SS Tx/Rx). Called to notify the status of the bit. + * @check_contaminant: + * Optional;The callback is called when CC pins report open status + * at the end of the deboumce period or when the port is still + * toggling. Chip level drivers are expected to check for contaminant + * and call tcpm_clean_port when the port is clean. */ struct tcpci_data { struct regmap *regmap; @@ -204,6 +209,8 @@ struct tcpci_data { void (*frs_sourcing_vbus)(struct tcpci *tcpci, struct tcpci_data *data); void (*set_partner_usb_comm_capable)(struct tcpci *tcpci, struct tcpci_data *data, bool capable); + void (*check_contaminant)(struct tcpci *tcpci, struct tcpci_data *data, + bool disconnect_while_debouncing); }; struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data); From patchwork Fri Aug 26 08:40:00 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Badhri Jagan Sridharan X-Patchwork-Id: 12955646 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 44BD2ECAAD6 for ; Fri, 26 Aug 2022 08:41:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234424AbiHZIl3 (ORCPT ); Fri, 26 Aug 2022 04:41:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34616 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343713AbiHZIkj (ORCPT ); Fri, 26 Aug 2022 04:40:39 -0400 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 875473ECDC for ; Fri, 26 Aug 2022 01:40:13 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-33dd097f993so15676477b3.10 for ; Fri, 26 Aug 2022 01:40:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc; bh=1BCRKzVhOtBrKG/VR6oCKwA2rCN8yts14gxEEpWzTCs=; b=LpDUdywQAhULaMYLRcfvjEFdyqICCSbvUhMa1DwrNNBTXZ98htp1qCZwkXePp0+qCP fjWkp3GQpIlRivOEyhsVHLsN6eU/KWRz9XFPvbwOinyj0d3mfcx9j2h9IvBFz3Cax1dx D+6trm1GmP3wzvlvqtVNSb/Zh1UtxY9xcfwoJ5/Z5nUEZNPNZHtV2V/7VzGIvP8BVwSv wLi74SKWq17U0iEorxRpjRSmMMIAGSy47nHgvX2KCsjCfjZjhu9pFdF1ancQdj4wBoLR vKVy7pYQN320eHO3HAsMf1AEOpRSt++WrxHCnZDOsPV1hBoLjM5R1WBoNIHhyi+ndYRj nn7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc; bh=1BCRKzVhOtBrKG/VR6oCKwA2rCN8yts14gxEEpWzTCs=; b=aOnZtO+q3qqhu5XGiCuXopwFcK+vIo5OCKVwRgq/k4jjYFOs3MWPh7AgidYda2/AIa Y/b7dlrUMscvlgdofd3lmefRXwTrVZpHLSZzX7qK22PzLO/qVUG0lkO+PizoMo6QgzI5 gWyTkGgU+2g5nEjTVOK3Nt0IZK4xNZvNutxJZKhAX6JyF6vcZAkgxgr84ecK1jMz4EON IAMTfzZtlkKIB10xjjsHpa3KDfSSD8ki3AXQvnhXb8ofHAGHT8iUhXfp5P8LZ3N3DhDi pcVTAeRdk4xItXjt1iTD6QV+4XjLGDAd1Ej0CKaVwtNI08psOsC7mmnc7SN2rEg6CUSh RNdQ== X-Gm-Message-State: ACgBeo3ENeA1dm/ocT5PSTDLmrjCyzWOsBa+AngQm4f1aByZsy+MXlzA poI/9NDJB0uHeeDflPArs3UyoplUlLU= X-Google-Smtp-Source: AA6agR6wjESRw5TzQK2Y6xr9dLNT27OzE76bMAt4poVX5d//5QWPL/VdEtum0Cvn2yNc0Zzee+GEEfFEqGs= X-Received: from badhri.mtv.corp.google.com ([2620:15c:211:201:9c82:622c:af33:39f2]) (user=badhri job=sendgmr) by 2002:a0d:da83:0:b0:329:9c04:fe6d with SMTP id c125-20020a0dda83000000b003299c04fe6dmr7700379ywe.196.1661503212667; Fri, 26 Aug 2022 01:40:12 -0700 (PDT) Date: Fri, 26 Aug 2022 01:40:00 -0700 In-Reply-To: <20220826084001.3341215-1-badhri@google.com> Mime-Version: 1.0 References: <20220826084001.3341215-1-badhri@google.com> X-Mailer: git-send-email 2.37.2.672.g94769d06f0-goog Message-ID: <20220826084001.3341215-3-badhri@google.com> Subject: [PATCH v1 3/4] usb: typec: tcpci_maxim: Export helper function for register reads From: Badhri Jagan Sridharan To: Guenter Roeck , Heikki Krogerus , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Kyle Tso , Badhri Jagan Sridharan Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Signed-off-by: Badhri Jagan Sridharan --- drivers/usb/typec/tcpm/tcpci_maxim.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c index 4b6705f3d7b7..af3a7ecab23b 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim.c @@ -68,25 +68,29 @@ static struct max_tcpci_chip *tdata_to_max_tcpci(struct tcpci_data *tdata) return container_of(tdata, struct max_tcpci_chip, data); } -static int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val) +inline int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val) { return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u16)); } +EXPORT_SYMBOL_GPL(max_tcpci_read16); -static int max_tcpci_write16(struct max_tcpci_chip *chip, unsigned int reg, u16 val) +inline int max_tcpci_write16(struct max_tcpci_chip *chip, unsigned int reg, u16 val) { return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u16)); } +EXPORT_SYMBOL_GPL(max_tcpci_write16); -static int max_tcpci_read8(struct max_tcpci_chip *chip, unsigned int reg, u8 *val) +inline int max_tcpci_read8(struct max_tcpci_chip *chip, unsigned int reg, u8 *val) { return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u8)); } +EXPORT_SYMBOL_GPL(max_tcpci_read8); -static int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg, u8 val) +inline int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg, u8 val) { return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8)); } +EXPORT_SYMBOL_GPL(max_tcpci_write8); static void max_tcpci_init_regs(struct max_tcpci_chip *chip) { From patchwork Fri Aug 26 08:40:01 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Badhri Jagan Sridharan X-Patchwork-Id: 12955647 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BBBC1ECAAA3 for ; Fri, 26 Aug 2022 08:41:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243479AbiHZIlc (ORCPT ); Fri, 26 Aug 2022 04:41:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34632 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343716AbiHZIkj (ORCPT ); Fri, 26 Aug 2022 04:40:39 -0400 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EDCC25A2E1 for ; Fri, 26 Aug 2022 01:40:15 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-32a115757b6so15463917b3.13 for ; Fri, 26 Aug 2022 01:40:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc; bh=1o9bLsaDoarTaZ8Yap5yLvZNAQOBhwz/I+jv/M+m85M=; b=ch+RSQDlIz7YkxO6QT4II9AcEpZoxJFhXMQUup5ghL9GkIsKA5m+KrwH9Xa0OdNEr4 cBU7g0tGvhxASza/Xtt2WoCExWLdcdjZhnFbUvYVT07Bc2DDojmF+vNr4UtRFGYRI9kf 4HN8piMbkfg2HHSkd1WezyJXQPHC7eFpnE9hvcPNeXa8zpsRxXi1JjKLX5Dkr5k2yp/I eiBNJ+ldiLLdyVWHc6eK6PdlDXATvN+PfyXqyGBdQ3sHcTrp5H4jlg97bNuS2yac+t34 YzvaR5XBQSXpL8FjSR94Xt/SLvRoWSSReeB0UK50yiKqMpbPQva9k8zTj3Xs9y3AafNj y4Aw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc; bh=1o9bLsaDoarTaZ8Yap5yLvZNAQOBhwz/I+jv/M+m85M=; b=22dG3wOBfnWNnKNdyy+SuzPi7qECetothqT5Dlk0Q3JaSVGxqGWk0erUiUGc34MVRd 5MBf64yI2iGQhbUq9XtCu3hpxzS7PrRm7+HXWGc/zm7O9IyGcpHilGme5sPM+PMYVAzP XjeoO4ebNoIBVCvl2ms3GMkKPHIUOGTbdqPxaCmKKy7bjoTK/ZLVp4r3aFVrEgoqnZd3 gJVFhq1MJPA9p0rwAap7U0FzTps8ZDCKL8ZDWYVWE8nN/YDfBloebkxDgJ2VSEhiYf9Y O+0A/jUvQsjtrgawCDi0nA1KWRHiyeSgUYVmqYPeIw8Ckj8HlDgqfQoXI4yvn3vttCjo vd1w== X-Gm-Message-State: ACgBeo3c4QtmX9Rh9WZeRWd09twMWz+rwTg1Gh2HGhHFTZbIgIXBu4hg 9VUryFF33mqA4M+Ao3A55aPbnNuPW84= X-Google-Smtp-Source: AA6agR5vBVXFlu8BZUIiYNbWUNTx/kmyxZMQ9o0jVDK1YkwkrpSNiTotTi0Ot4AsP4h5O01QKkil/wIfaPY= X-Received: from badhri.mtv.corp.google.com ([2620:15c:211:201:9c82:622c:af33:39f2]) (user=badhri job=sendgmr) by 2002:a25:6611:0:b0:67b:e0c2:3239 with SMTP id a17-20020a256611000000b0067be0c23239mr6417578ybc.18.1661503215249; Fri, 26 Aug 2022 01:40:15 -0700 (PDT) Date: Fri, 26 Aug 2022 01:40:01 -0700 In-Reply-To: <20220826084001.3341215-1-badhri@google.com> Mime-Version: 1.0 References: <20220826084001.3341215-1-badhri@google.com> X-Mailer: git-send-email 2.37.2.672.g94769d06f0-goog Message-ID: <20220826084001.3341215-4-badhri@google.com> Subject: [PATCH v1 4/4] usb: typec: maxim_contaminant: Implement check_contaminant callback From: Badhri Jagan Sridharan To: Guenter Roeck , Heikki Krogerus , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Kyle Tso , Badhri Jagan Sridharan Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Maxim TCPC has additional ADCs and low current(1ua) current source to measure the impedance of CC and SBU pins. When tcpm invokes the check_contaminant callback, Maxim TCPC measures the impedance of the CC & SBU pins and when the impedance measured is less than 1MOhm, it is assumed that USB-C port is contaminated. CC comparators are also checked to differentiate between presence of sink and contaminant. Once USB-C is deemed to be contaminated, MAXIM TCPC has additional hardware to disable normal DRP toggling cycle and enable 1ua on CC pins once every 2.4secs/4.8secs. Maxim TCPC interrupts AP once the impedance on the CC pin is above the 1MOhm threshold. The Maxim tcpc driver then signals TCPM_PORT_CLEAN to restart toggling. Signed-off-by: Badhri Jagan Sridharan --- drivers/usb/typec/tcpm/Makefile | 2 +- drivers/usb/typec/tcpm/maxim_contaminant.c | 338 +++++++++++++++++++++ drivers/usb/typec/tcpm/tcpci_maxim.c | 34 ++- drivers/usb/typec/tcpm/tcpci_maxim.h | 68 +++++ 4 files changed, 429 insertions(+), 13 deletions(-) create mode 100644 drivers/usb/typec/tcpm/maxim_contaminant.c create mode 100644 drivers/usb/typec/tcpm/tcpci_maxim.h diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile index 906d9dced8e7..81e4e9421fa0 100644 --- a/drivers/usb/typec/tcpm/Makefile +++ b/drivers/usb/typec/tcpm/Makefile @@ -7,4 +7,4 @@ obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o obj-$(CONFIG_TYPEC_MT6360) += tcpci_mt6360.o obj-$(CONFIG_TYPEC_TCPCI_MT6370) += tcpci_mt6370.o -obj-$(CONFIG_TYPEC_TCPCI_MAXIM) += tcpci_maxim.o +obj-$(CONFIG_TYPEC_TCPCI_MAXIM) += tcpci_maxim.o maxim_contaminant.o diff --git a/drivers/usb/typec/tcpm/maxim_contaminant.c b/drivers/usb/typec/tcpm/maxim_contaminant.c new file mode 100644 index 000000000000..8bb2a509a00d --- /dev/null +++ b/drivers/usb/typec/tcpm/maxim_contaminant.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022 Google, Inc + * + * USB-C module to reduce wakeups due to contaminants. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tcpci_maxim.h" + +enum fladc_select { + CC1_SCALE1 = 1, + CC1_SCALE2, + CC2_SCALE1, + CC2_SCALE2, + SBU1, + SBU2, +}; + +#define FLADC_1uA_LSB_MV 25 +/* High range CC */ +#define FLADC_CC_HIGH_RANGE_LSB_MV 208 +/* Low range CC */ +#define FLADC_CC_LOW_RANGE_LSB_MV 126 + +/* 1uA current source */ +#define FLADC_CC_SCALE1 1 +/* 5 uA current source */ +#define FLADC_CC_SCALE2 5 + +#define FLADC_1uA_CC_OFFSET_MV 300 +#define FLADC_CC_HIGH_RANGE_OFFSET_MV 624 +#define FLADC_CC_LOW_RANGE_OFFSET_MV 378 + +#define CONTAMINANT_THRESHOLD_SBU_K 1000 +#define CONTAMINANT_THRESHOLD_CC_K 1000 + +#define READ1_SLEEP_MS 10 +#define READ2_SLEEP_MS 5 + +#define STATUS_CHECK(reg, mask, val) (((reg) & (mask)) == (val)) + +#define IS_CC_OPEN(cc_status) \ + (STATUS_CHECK((cc_status), TCPC_CC_STATUS_CC1_MASK << TCPC_CC_STATUS_CC1_SHIFT, \ + TCPC_CC_STATE_SRC_OPEN) && STATUS_CHECK((cc_status), \ + TCPC_CC_STATUS_CC2_MASK << \ + TCPC_CC_STATUS_CC2_SHIFT, \ + TCPC_CC_STATE_SRC_OPEN)) + +static int adc_to_mv(struct max_tcpci_chip *chip, enum fladc_select channel, bool ua_src, u8 fladc) +{ + /* SBU channels only have 1 scale with 1uA. */ + if ((ua_src && (channel == CC1_SCALE2 || channel == CC2_SCALE2 || channel == SBU1 || + channel == SBU2))) + /* Mean of range */ + return FLADC_1uA_CC_OFFSET_MV + (fladc * FLADC_1uA_LSB_MV); + else if (!ua_src && (channel == CC1_SCALE1 || channel == CC2_SCALE1)) + return FLADC_CC_HIGH_RANGE_OFFSET_MV + (fladc * FLADC_CC_HIGH_RANGE_LSB_MV); + else if (!ua_src && (channel == CC1_SCALE2 || channel == CC2_SCALE2)) + return FLADC_CC_LOW_RANGE_OFFSET_MV + (fladc * FLADC_CC_LOW_RANGE_LSB_MV); + + dev_err(chip->dev, "ADC ERROR: SCALE UNKNOWN"); + + return -EINVAL; +} + +static int read_adc_mv(struct max_tcpci_chip *chip, enum fladc_select channel, int sleep_msec, + bool raw, bool ua_src) +{ + struct regmap *regmap = chip->data.regmap; + u8 fladc; + int ret; + + /* Channel & scale select */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK, + channel << ADC_CHANNEL_OFFSET); + if (ret < 0) + return ret; + + /* Enable ADC */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCEN, ADCEN); + if (ret < 0) + return ret; + + usleep_range(sleep_msec * 1000, (sleep_msec + 1) * 1000); + ret = max_tcpci_read8(chip, TCPC_VENDOR_FLADC_STATUS, &fladc); + if (ret < 0) + return ret; + + /* Disable ADC */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCEN, 0); + if (ret < 0) + return ret; + + ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK, 0); + if (ret < 0) + return ret; + + if (!raw) + return adc_to_mv(chip, channel, ua_src, fladc); + else + return fladc; +} + +static int read_resistance_kohm(struct max_tcpci_chip *chip, enum fladc_select channel, + int sleep_msec, bool raw) +{ + struct regmap *regmap = chip->data.regmap; + int mv; + int ret; + + if (channel == CC1_SCALE1 || channel == CC2_SCALE1 || channel == CC1_SCALE2 || + channel == CC2_SCALE2) { + /* Enable 1uA current source */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK, + ULTRA_LOW_POWER_MODE); + if (ret < 0) + return ret; + + /* Enable 1uA current source */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_1_SRC); + if (ret < 0) + return ret; + + /* OVP disable */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCOVPDIS, CCOVPDIS); + if (ret < 0) + return ret; + + mv = read_adc_mv(chip, channel, sleep_msec, raw, true); + /* OVP enable */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCOVPDIS, 0); + if (ret < 0) + return ret; + /* returns KOhm as 1uA source is used. */ + return mv; + } + + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, SBUOVPDIS, SBUOVPDIS); + if (ret < 0) + return ret; + + /* SBU switches auto configure when channel is selected. */ + /* Enable 1ua current source */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, SBURPCTRL, SBURPCTRL); + if (ret < 0) + return ret; + + mv = read_adc_mv(chip, channel, sleep_msec, raw, true); + /* Disable current source */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, SBURPCTRL, 0); + if (ret < 0) + return ret; + + /* OVP disable */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, SBUOVPDIS, 0); + if (ret < 0) + return ret; + + return mv; +} + +static void read_comparators(struct max_tcpci_chip *chip, u8 *vendor_cc_status2_cc1, + u8 *vendor_cc_status2_cc2) +{ + struct regmap *regmap = chip->data.regmap; + int ret; + + /* Enable 80uA source */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_80_SRC); + if (ret < 0) + return; + + /* Enable comparators */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL1, CCCOMPEN, CCCOMPEN); + if (ret < 0) + return; + + /* Sleep to allow comparators settle */ + usleep_range(5000, 6000); + ret = regmap_update_bits(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_ORIENTATION, PLUG_ORNT_CC1); + if (ret < 0) + return; + + usleep_range(5000, 6000); + ret = max_tcpci_read8(chip, VENDOR_CC_STATUS2, vendor_cc_status2_cc1); + if (ret < 0) + return; + + ret = regmap_update_bits(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_ORIENTATION, PLUG_ORNT_CC2); + if (ret < 0) + return; + + usleep_range(5000, 6000); + ret = max_tcpci_read8(chip, VENDOR_CC_STATUS2, vendor_cc_status2_cc2); + if (ret < 0) + return; + + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL1, CCCOMPEN, 0); + if (ret < 0) + return; + regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, 0); +} + +static int detect_contaminant(struct max_tcpci_chip *chip) +{ + int cc1_k, cc2_k, sbu1_k, sbu2_k; + u8 vendor_cc_status2_cc1 = 0xff, vendor_cc_status2_cc2 = 0xff; + u8 role_ctrl = 0, role_ctrl_backup = 0; + int inferred_state = NOT_DETECTED; + + max_tcpci_read8(chip, TCPC_ROLE_CTRL, &role_ctrl); + role_ctrl_backup = role_ctrl; + role_ctrl = 0x0F; + max_tcpci_write8(chip, TCPC_ROLE_CTRL, role_ctrl); + + cc1_k = read_resistance_kohm(chip, CC1_SCALE2, READ1_SLEEP_MS, false); + cc2_k = read_resistance_kohm(chip, CC2_SCALE2, READ2_SLEEP_MS, false); + + sbu1_k = read_resistance_kohm(chip, SBU1, READ1_SLEEP_MS, false); + sbu2_k = read_resistance_kohm(chip, SBU2, READ2_SLEEP_MS, false); + read_comparators(chip, &vendor_cc_status2_cc1, &vendor_cc_status2_cc2); + + if ((!(CC1_VUFP_RD0P5 & vendor_cc_status2_cc1) || + !(CC2_VUFP_RD0P5 & vendor_cc_status2_cc2)) && + !(CC1_VUFP_RD0P5 & vendor_cc_status2_cc1 && CC2_VUFP_RD0P5 & vendor_cc_status2_cc2)) + inferred_state = SINK; + else if ((cc1_k < CONTAMINANT_THRESHOLD_CC_K || cc2_k < CONTAMINANT_THRESHOLD_CC_K) && + (sbu1_k < CONTAMINANT_THRESHOLD_SBU_K || sbu2_k < CONTAMINANT_THRESHOLD_SBU_K)) + inferred_state = DETECTED; + + if (inferred_state == NOT_DETECTED) + max_tcpci_write8(chip, TCPC_ROLE_CTRL, role_ctrl_backup); + else + max_tcpci_write8(chip, TCPC_ROLE_CTRL, (TCPC_ROLE_CTRL_DRP | 0xA)); + + return inferred_state; +} + +static int enable_dry_detection(struct max_tcpci_chip *chip) +{ + struct regmap *regmap = chip->data.regmap; + u8 temp; + int ret; + + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL3, CCWTRDEB_MASK | CCWTRSEL_MASK + | WTRCYCLE_MASK, CCWTRDEB_1MS << CCWTRDEB_SHIFT | + CCWTRSEL_1V << CCWTRSEL_SHIFT | WTRCYCLE_4_8_S << + WTRCYCLE_SHIFT); + if (ret < 0) + return ret; + + ret = regmap_update_bits(regmap, TCPC_ROLE_CTRL, TCPC_ROLE_CTRL_DRP, TCPC_ROLE_CTRL_DRP); + if (ret < 0) + return ret; + + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL1, CCCONNDRY, CCCONNDRY); + if (ret < 0) + return ret; + ret = max_tcpci_read8(chip, TCPC_VENDOR_CC_CTRL1, &temp); + if (ret < 0) + return ret; + + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK, + ULTRA_LOW_POWER_MODE); + if (ret < 0) + return ret; + ret = max_tcpci_read8(chip, TCPC_VENDOR_CC_CTRL2, &temp); + if (ret < 0) + return ret; + + /* Enable Look4Connection before sending the command */ + ret = regmap_update_bits(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_EN_LK4CONN_ALRT, + TCPC_TCPC_CTRL_EN_LK4CONN_ALRT); + if (ret < 0) + return ret; + + ret = max_tcpci_write8(chip, TCPC_COMMAND, TCPC_CMD_LOOK4CONNECTION); + if (ret < 0) + return ret; + return 0; +} + +bool is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce) +{ + u8 cc_status, pwr_cntl; + enum contamiant_state state; + + max_tcpci_read8(chip, TCPC_CC_STATUS, &cc_status); + max_tcpci_read8(chip, TCPC_POWER_CTRL, &pwr_cntl); + + if (chip->contaminant_state == NOT_DETECTED || chip->contaminant_state == SINK) { + if (!disconnect_while_debounce) + msleep(100); + + max_tcpci_read8(chip, TCPC_CC_STATUS, &cc_status); + if (IS_CC_OPEN(cc_status)) { + u8 role_ctrl, role_ctrl_backup; + + max_tcpci_read8(chip, TCPC_ROLE_CTRL, &role_ctrl); + role_ctrl_backup = role_ctrl; + role_ctrl |= 0x0F; + role_ctrl &= ~(TCPC_ROLE_CTRL_DRP); + max_tcpci_write8(chip, TCPC_ROLE_CTRL, role_ctrl); + + chip->contaminant_state = detect_contaminant(chip); + + max_tcpci_write8(chip, TCPC_ROLE_CTRL, role_ctrl_backup); + if (state == DETECTED) { + enable_dry_detection(chip); + return true; + } + } + return false; + } else if (chip->contaminant_state == DETECTED) { + if (STATUS_CHECK(cc_status, TCPC_CC_STATUS_TOGGLING, 0)) { + chip->contaminant_state = detect_contaminant(chip); + if (state == DETECTED) { + enable_dry_detection(chip); + return true; + } + } + } + + return false; +} +EXPORT_SYMBOL_GPL(is_contaminant); + +MODULE_DESCRIPTION("MAXIM TCPC CONTAMINANT Module"); +MODULE_AUTHOR("Badhri Jagan Sridharan "); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c index af3a7ecab23b..0b75d987cdb2 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim.c @@ -1,6 +1,6 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2020, Google LLC + * Copyright (C) 2020 - 2022, Google LLC * * MAXIM TCPCI based TCPC driver */ @@ -15,6 +15,8 @@ #include #include +#include "tcpci_maxim.h" + #define PD_ACTIVITY_TIMEOUT_MS 10000 #define TCPC_VENDOR_ALERT 0x80 @@ -39,14 +41,6 @@ #define MAX_BUCK_BOOST_SOURCE 0xa #define MAX_BUCK_BOOST_SINK 0x5 -struct max_tcpci_chip { - struct tcpci_data data; - struct tcpci *tcpci; - struct device *dev; - struct i2c_client *client; - struct tcpm_port *port; -}; - static const struct regmap_range max_tcpci_tcpci_range[] = { regmap_reg_range(0x00, 0x95) }; @@ -352,8 +346,14 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status) if (status & TCPC_ALERT_VBUS_DISCNCT) tcpm_vbus_change(chip->port); - if (status & TCPC_ALERT_CC_STATUS) - tcpm_cc_change(chip->port); + if (status & TCPC_ALERT_CC_STATUS) { + if (chip->contaminant_state == DETECTED) { + if (!is_contaminant(chip, false)) + tcpm_port_clean(chip->port); + } else { + tcpm_cc_change(chip->port); + } + } if (status & TCPC_ALERT_POWER_STATUS) process_power_status(chip); @@ -442,6 +442,15 @@ static int tcpci_init(struct tcpci *tcpci, struct tcpci_data *data) return -1; } +static void max_tcpci_check_contaminant(struct tcpci *tcpci, struct tcpci_data *tdata, + bool disconnect_while_debounce) +{ + struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata); + + if (!is_contaminant(chip, disconnect_while_debounce)) + tcpm_port_clean(chip->port); +} + static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { int ret; @@ -475,6 +484,7 @@ static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id chip->data.auto_discharge_disconnect = true; chip->data.vbus_vsafe0v = true; chip->data.set_partner_usb_comm_capable = max_tcpci_set_partner_usb_comm_capable; + chip->data.check_contaminant = max_tcpci_check_contaminant; max_tcpci_init_regs(chip); chip->tcpci = tcpci_register_port(chip->dev, &chip->data); diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.h b/drivers/usb/typec/tcpm/tcpci_maxim.h new file mode 100644 index 000000000000..47e685cb212c --- /dev/null +++ b/drivers/usb/typec/tcpm/tcpci_maxim.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022 Google, Inc + * + * MAXIM TCPC header file. + */ +#ifndef TCPCI_MAXIM_H_ +#define TCPCI_MAXIM_H_ + +#define VENDOR_CC_STATUS2 0x85 +#define CC1_VUFP_RD0P5 BIT(1) +#define CC2_VUFP_RD0P5 BIT(5) +#define TCPC_VENDOR_FLADC_STATUS 0x89 + +#define TCPC_VENDOR_CC_CTRL1 0x8c +#define CCCONNDRY BIT(7) +#define CCCOMPEN BIT(5) + +#define TCPC_VENDOR_CC_CTRL2 0x8d +#define SBUOVPDIS BIT(7) +#define CCOVPDIS BIT(6) +#define SBURPCTRL BIT(5) +#define CCLPMODESEL_MASK GENMASK(4, 3) +#define ULTRA_LOW_POWER_MODE BIT(3) +#define CCRPCTRL_MASK GENMASK(2, 0) +#define UA_1_SRC 1 +#define UA_80_SRC 3 + +#define TCPC_VENDOR_CC_CTRL3 0x8e +#define CCWTRDEB_MASK GENMASK(7, 6) +#define CCWTRDEB_SHIFT 6 +#define CCWTRDEB_1MS 1 +#define CCWTRSEL_MASK GENMASK(5, 3) +#define CCWTRSEL_SHIFT 3 +#define CCWTRSEL_1V 0x4 +#define CCLADDERDIS BIT(2) +#define WTRCYCLE_MASK BIT(0) +#define WTRCYCLE_SHIFT 0 +#define WTRCYCLE_2_4_S 0 +#define WTRCYCLE_4_8_S 1 + +#define TCPC_VENDOR_ADC_CTRL1 0x91 +#define ADCINSEL_MASK GENMASK(7, 5) +#define ADC_CHANNEL_OFFSET 5 +#define ADCEN BIT(0) + +enum contamiant_state { + NOT_DETECTED, + DETECTED, + SINK, +}; + +struct max_tcpci_chip { + struct tcpci_data data; + struct tcpci *tcpci; + struct device *dev; + struct i2c_client *client; + struct tcpm_port *port; + enum contamiant_state contaminant_state; +}; + +int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val); +int max_tcpci_write16(struct max_tcpci_chip *chip, unsigned int reg, u16 val); +int max_tcpci_read8(struct max_tcpci_chip *chip, unsigned int reg, u8 *val); +int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg, u8 val); +bool is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce); + +#endif // TCPCI_MAXIM_H_