Received: by 2002:a05:6358:d09b:b0:dc:cd0c:909e with SMTP id jc27csp3876950rwb; Fri, 9 Dec 2022 23:01:28 -0800 (PST) X-Google-Smtp-Source: AA0mqf4PaRcYbBI+QksjGZsvgAhNR3VwTh6pHQcXBtex1WLSI1fUHbOfFON26TQO3DNZaH7M5zEk X-Received: by 2002:a17:906:a15a:b0:7ae:7c4e:195b with SMTP id bu26-20020a170906a15a00b007ae7c4e195bmr7265642ejb.22.1670655688617; Fri, 09 Dec 2022 23:01:28 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1670655688; cv=none; d=google.com; s=arc-20160816; b=ZpEUM1OY9KMm1uHomwKqTraKb95+5KhQ+FwLc/8vVfU3qQ7zIlufZmJAXl3GesM7rf /GNPQQmQXPxlxa579GO4Ck5QDYWmGUcdZ8XXT00thvj9KD263VmpuT4N+ZJGrGkjXiHx vCWA+h+GOHBQjYgwNcRXczyiPGEvTqWTlfymO5ei4Ev+5WzQqKvETWiKbs8zABrdSMCv B9tcV/kEEiml7z5Ue4/QMUwDTjkhD+kDwhH1317t2WBUxKctTUeOUDSpiDRZlg2CX/+7 RWKZQnMg4celhOfKRm3CzPCE52X4lv9DEBBxFsh0Sy4dfPDBmY2H3wYt51lCWX9U7ukA zigg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:dkim-signature; bh=av8F+m9aXNHF1XGoT6JkaOLr+DsrL9gzQC12crOuVHM=; b=EQK5xmLE7PDArBxQ7kpmT9O8gD53adXDEiPhD5e5YILk9yr2SnO2F1zZvHD7n97Npi lBfgIt50gtAd4lGOEr1xgheL3mfDbV6yujRlBfRkPQmu3HIbPNJi1h8aOLQyXqVnmIfh A467tuCizRFn9s3UwfDfpcsCV97q3h9ZNrYbhLKrOnotVc3Ca0UJL08gvYrW5+Q7+PQT hK6M89xCFYpGS67dLoAt7MNJh6YBmVFg0iY3o0V6X9N6iWT6hOveCybjxgTQe7uVQRax 0TfnBuRj6leorcLTBvfIR6AeSuak3JgIyoOTDgoltdaFkm7FB7ei+mQ2RsdEZ8u92NI/ hmmQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20210112 header.b=XOQhxxTR; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id sd5-20020a1709076e0500b0078772382b35si1584181ejc.91.2022.12.09.23.01.09; Fri, 09 Dec 2022 23:01:28 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20210112 header.b=XOQhxxTR; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229863AbiLJGPc (ORCPT + 74 others); Sat, 10 Dec 2022 01:15:32 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33584 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229793AbiLJGPY (ORCPT ); Sat, 10 Dec 2022 01:15:24 -0500 Received: from mail-pj1-x102b.google.com (mail-pj1-x102b.google.com [IPv6:2607:f8b0:4864:20::102b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3F62E51C3C for ; Fri, 9 Dec 2022 22:15:21 -0800 (PST) Received: by mail-pj1-x102b.google.com with SMTP id q17-20020a17090aa01100b002194cba32e9so10380896pjp.1 for ; Fri, 09 Dec 2022 22:15:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=av8F+m9aXNHF1XGoT6JkaOLr+DsrL9gzQC12crOuVHM=; b=XOQhxxTRvAodKn/UVggpurWs/+/C/Ms30JypY5eig9l0gd+pW6Kxr8BhHMFBT7v9ze HZ646zjqwDp5L+GrxpE4h62hkbw18/bPvSn/a+raSbn2OqayOZepK02JsL4KZ3WPTmnp 6LzH5TzbDvqTM9BZHTZroQVU/nrgwXx8WXjTDm+xI4trOthwudWPkELtjM5xtNRlmrbd eFMMXyB8WEyvE49TSQnupS7Clgy8GXTOoDLB+H1mDQYfRGi1AO79n5vaCHgrSxcUEuQi nZbvSxD9PALc+45uBarSpLKLovA9RvRUuyZHBb2UR1MU3pmtVIeWrKejJdvs1PBe5ktV 3hwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=av8F+m9aXNHF1XGoT6JkaOLr+DsrL9gzQC12crOuVHM=; b=jN3S9Yi02Yf2B/yw9OcN0uDhNgyeXNVX4+q4xK/hQ7z7xK90icbYqmo5H1lqaTQ5rh JNed9MwCTUiJJ1zydYV2Rs9hkBHPIfcRa5i6EFMnFg+xSMEMOdckM9AJGH1CCTkFQTNm MOxVl0Diw1AI2/E5703RtV9CifV8CmS44R1YQ9SDsta72skyW7eEOOR6VrUEkQn+t8+N vjn+eo0vVZ6ibNNx27gYsathp2+VJCYoIWU36IwBZcWMh8ZTnZwnTwKAcqcp4gsd0vFz ADdqYvg+mgtBGPpbdxPcdXYe6/fBh/8nffne/8fZSjKAlvDUyv0ZaG0+GEmepvflRUBV Iyog== X-Gm-Message-State: ANoB5pmUef7FIHFdEuNqI9VYxCFiUgGYjbtfiGGD7rVuB+WSlRkKkznj EcSW6cNZfCyCTFRMAYI99hwp/+wtqVBj1P9e+CK4zA== X-Received: by 2002:a17:90a:4d48:b0:219:4487:f763 with SMTP id l8-20020a17090a4d4800b002194487f763mr49989913pjh.201.1670652920210; Fri, 09 Dec 2022 22:15:20 -0800 (PST) MIME-Version: 1.0 References: <20221210060119.944931-1-badhri@google.com> <20221210060119.944931-3-badhri@google.com> In-Reply-To: <20221210060119.944931-3-badhri@google.com> From: Badhri Jagan Sridharan Date: Fri, 9 Dec 2022 22:14:43 -0800 Message-ID: Subject: Re: [PATCH v7 3/3] usb: typec: maxim_contaminant: Implement check_contaminant callback To: Guenter Roeck , Heikki Krogerus , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Kyle Tso Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-17.6 required=5.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, ENV_AND_HDR_SPF_MATCH,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS, USER_IN_DEF_DKIM_WL,USER_IN_DEF_SPF_WL autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Please disregard. Messed up versions. Version 9 is what it is. On Fri, Dec 9, 2022 at 10:01 PM Badhri Jagan Sridharan wrote: > > 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. > > Renaming tcpci_maxim.c to tcpci_maxim_core.c and moving reg read/write > helper functions to the tcpci_maxim.h header file. > > Signed-off-by: Badhri Jagan Sridharan > --- > Changes since v6: > * Refactored logic for check_contaminant and removed > * is_potential_contaminant. > Changes since v5: > * None > Changes since v4: > * Missed committing local changes to fix ktest robot warnings. > Updated the patch in v5. > Changes since v3: > * Renamed functions and removed the unecessary EXPORT. > * Fixed ktest robot warning. > Changes since v2: > * Implemented is_potential_contaminant to offload > * disconnect_while_debouncing > Changes since v1: > * Renamed tcpci_maxim.c to tcpci_maxim_core.c and compiling > tcpci_maxim_core.o with maxim_contaminant.o as a single module > as suggested by Guenter Roeck > * Got rid of exporting symbols for reg read/write helper functions > and moved them to header as suggested by Heikki Krogerus > * Sqashed the commit which exposed the max_tcpci_read helper functions > into this one. > --- > drivers/usb/typec/tcpm/Makefile | 1 + > drivers/usb/typec/tcpm/maxim_contaminant.c | 337 ++++++++++++++++++ > drivers/usb/typec/tcpm/tcpci_maxim.h | 89 +++++ > .../{tcpci_maxim.c => tcpci_maxim_core.c} | 53 ++- > 4 files changed, 448 insertions(+), 32 deletions(-) > create mode 100644 drivers/usb/typec/tcpm/maxim_contaminant.c > create mode 100644 drivers/usb/typec/tcpm/tcpci_maxim.h > rename drivers/usb/typec/tcpm/{tcpci_maxim.c => tcpci_maxim_core.c} (93%) > > diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile > index 906d9dced8e7..08e57bb499cb 100644 > --- a/drivers/usb/typec/tcpm/Makefile > +++ b/drivers/usb/typec/tcpm/Makefile > @@ -8,3 +8,4 @@ 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 > +tcpci_maxim-y += tcpci_maxim_core.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..23b5ed65cba8 > --- /dev/null > +++ b/drivers/usb/typec/tcpm/maxim_contaminant.c > @@ -0,0 +1,337 @@ > +// 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 max_contaminant_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 max_contaminant_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 max_contaminant_adc_to_mv(chip, channel, ua_src, fladc); > + else > + return fladc; > +} > + > +static int max_contaminant_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 = max_contaminant_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 = max_contaminant_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 max_contaminant_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 max_contaminant_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 = max_contaminant_read_resistance_kohm(chip, CC1_SCALE2, READ1_SLEEP_MS, false); > + cc2_k = max_contaminant_read_resistance_kohm(chip, CC2_SCALE2, READ2_SLEEP_MS, false); > + > + sbu1_k = max_contaminant_read_resistance_kohm(chip, SBU1, READ1_SLEEP_MS, false); > + sbu2_k = max_contaminant_read_resistance_kohm(chip, SBU2, READ2_SLEEP_MS, false); > + max_contaminant_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 max_contaminant_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 max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce) > +{ > + u8 cc_status, pwr_cntl; > + > + 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 = max_contaminant_detect_contaminant(chip); > + > + max_tcpci_write8(chip, TCPC_ROLE_CTRL, role_ctrl_backup); > + if (chip->contaminant_state == DETECTED) { > + max_contaminant_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 = max_contaminant_detect_contaminant(chip); > + if (chip->contaminant_state == DETECTED) { > + max_contaminant_enable_dry_detection(chip); > + return true; > + } > + } > + } > + > + return false; > +} > + > +MODULE_DESCRIPTION("MAXIM TCPC CONTAMINANT Module"); > +MODULE_AUTHOR("Badhri Jagan Sridharan "); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.h b/drivers/usb/typec/tcpm/tcpci_maxim.h > new file mode 100644 > index 000000000000..2c1c4d161b0d > --- /dev/null > +++ b/drivers/usb/typec/tcpm/tcpci_maxim.h > @@ -0,0 +1,89 @@ > +/* 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, > +}; > + > +/* > + * @potential_contaminant: > + * Last returned result to tcpm indicating whether the TCPM port > + * has potential contaminant. > + */ > +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; > +}; > + > +static 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)); > +} > + > +static 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)); > +} > + > +static 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)); > +} > + > +static 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)); > +} > + > +bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce); > + > +#endif // TCPCI_MAXIM_H_ > diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c > similarity index 93% > rename from drivers/usb/typec/tcpm/tcpci_maxim.c > rename to drivers/usb/typec/tcpm/tcpci_maxim_core.c > index 83e140ffcc3e..f32cda2a5e3a 100644 > --- a/drivers/usb/typec/tcpm/tcpci_maxim.c > +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.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) > }; > @@ -68,26 +62,6 @@ 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) > -{ > - return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u16)); > -} > - > -static 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)); > -} > - > -static 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)); > -} > - > -static 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)); > -} > - > static void max_tcpci_init_regs(struct max_tcpci_chip *chip) > { > u16 alert_mask = 0; > @@ -348,8 +322,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 || tcpm_port_is_toggling(chip->port)) { > + if (!max_contaminant_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); > @@ -438,6 +418,14 @@ 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) > +{ > + struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata); > + > + if (!max_contaminant_is_contaminant(chip, true)) > + tcpm_port_clean(chip->port); > +} > + > static int max_tcpci_probe(struct i2c_client *client) > { > int ret; > @@ -471,6 +459,7 @@ static int max_tcpci_probe(struct i2c_client *client) > 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); > -- > 2.39.0.rc1.256.g54fd8350bd-goog >