Received: by 2002:a05:6358:d09b:b0:dc:cd0c:909e with SMTP id jc27csp3850034rwb; Fri, 9 Dec 2022 22:26:07 -0800 (PST) X-Google-Smtp-Source: AA0mqf5YTctr3DC573giO1vmvHG+l8eSfpDxFiJfEbwa2jUd4bsDyxWhAMtBmR6KnW7qOu4x498L X-Received: by 2002:a50:cbcd:0:b0:46a:331:8e72 with SMTP id l13-20020a50cbcd000000b0046a03318e72mr7176544edi.37.1670653566826; Fri, 09 Dec 2022 22:26:06 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1670653566; cv=none; d=google.com; s=arc-20160816; b=KqzDa9KQQn9nQSvD2D/H2KbOFCetxPpDl4X7ke3IZMjTNeeltAAmZKEksDrdNDOran cJ8hBVrNojCYKKWRK9TXIxH9aXFRWoQUmikh7dr3jh5iRA/2uQNgsMq5NcmPp5NZh1Zh sNuD4TXNo2RRF9vEwL4Bo4OvntmA2gEgBLFiK/ApMHvmIatgqhJxDiQV5ZHkQjFal6qK Oc0JuEbBeh8tdCgtXjKJxPAJN5C0xT83t8n69toOizJMq7yfC0TySQfD1WYDF4hTTcdC v0vj83Ij3C5ukIYlukmNY612N+uYPKw+ELntQwiMxBprUHqxB6O7Xd2dWe/7APGi80oN eMFg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:dkim-signature; bh=Mfn79o6mmJB23c9+pXARsLNaK/U6ZRm9SKcMJfTEAI8=; b=Z2KNsjw1LxdaAZT72w4T+QUybDVeuFcc1wmmXCUi6WZuU/46SaDsF4IT7tkqwG0nk+ ePX0LPKIsuHzBpU/gXD0XK01e7JeImQDw319vMrGS2oOx/wrlXAW+kNMQhYgYkn6sfO2 pGtoC7CjYj8C92tRkJpBDw7R0ulnSdqkGPiMIby4YJzyqmymOWl7IccG72Zvib0yUflg F+C5hzgDt+L46wdrQS0P0lA/dOIWVj0fvt3jBjKqBOmhndY5cNOTsWt680LfmJgYl0of 4dk24rk6O9XVo+5NwqxepbMEwZSFv2BbGphNuuEdGYVjrvG8aMg6xv+wmM3XKaqmrXrd XCyQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20210112 header.b=rnspyn7L; 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 m16-20020a056402431000b00461c9bb5205si3589254edc.152.2022.12.09.22.25.47; Fri, 09 Dec 2022 22:26:06 -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=rnspyn7L; 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 S229637AbiLJGQs (ORCPT + 73 others); Sat, 10 Dec 2022 01:16:48 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34182 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229677AbiLJGQg (ORCPT ); Sat, 10 Dec 2022 01:16:36 -0500 Received: from mail-pg1-x549.google.com (mail-pg1-x549.google.com [IPv6:2607:f8b0:4864:20::549]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4D22B48757 for ; Fri, 9 Dec 2022 22:16:34 -0800 (PST) Received: by mail-pg1-x549.google.com with SMTP id s16-20020a632c10000000b0047084b16f23so4319106pgs.7 for ; Fri, 09 Dec 2022 22:16:34 -0800 (PST) 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:subject:date:message-id:reply-to; bh=Mfn79o6mmJB23c9+pXARsLNaK/U6ZRm9SKcMJfTEAI8=; b=rnspyn7LlVe/OhL/eUtQ7updhGTp0cdWY2fD6FVqP21GXipdpzAzet04iyrolkzBv7 yaAX7UrVJbHK6gPuoY/sMhF8oEOsGTAFJdKiyXQ9NAohFR/AyFlaJrxl5nAcFLD24KXH HA2tIbnRw2hsgTY3iCfI5BozlvpvjOkQM+X9gxNvYlVrRZiGmkwjdzPctSeJeSp0FsyS ZKur2omlX1txzKaFKJKidsXKKD9co8xLSbHOFbBGGu69s9MTmnPkS15yEvNI3FpLIfdV 9SrQj+1ucPaxUB+Nh6VNYfvzK7Krd4lc9IOCPANp3mRYl5VqPPuXcYF5PappEWg9+Scg 7M3g== 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:subject:date:message-id:reply-to; bh=Mfn79o6mmJB23c9+pXARsLNaK/U6ZRm9SKcMJfTEAI8=; b=fECFcZAv6oFknby5iZZDOWe8wFrVKhZ+P2d0brUc+Wk3glk5kq8n2Hbi5KOpdc9Bkx 6o3yOg8yNLHmT5xzCWiLLkDDEOnsg+rm9+DM6y0xRfM90tagIpIFrZO53CD0RmJ/wr+z tHf8Xbcy1ZGfK7cEO65zsgBv4JtP8/trORTbeBaS2y67LpgdRS8Z7weYjTxk9JzXM2br sJsKmV0Y0O2YLkVGTYnNYvItNtSMNxJkWrHAKe83teL49oFvTgJfW3leXyu33eosyzBc qoLdt1uu36S8Jppay7IXalEPTEi2P8Ya0tXH1SV2jYYbJLQhkPlQek8zRhgSJvLqbai9 Vxwg== X-Gm-Message-State: ANoB5pkvDfkk3oDVo0Ays01G9amryLYUzvpbao+H8TtN2y5Zr7j2YfzI DXznqShbG+1MYmnYoe3ynQLgqkeZpU4= X-Received: from badhri.mtv.corp.google.com ([2620:15c:211:201:bcc6:f018:88f8:a199]) (user=badhri job=sendgmr) by 2002:a17:90a:b38e:b0:218:79d8:1a2d with SMTP id e14-20020a17090ab38e00b0021879d81a2dmr99786829pjr.229.1670652993815; Fri, 09 Dec 2022 22:16:33 -0800 (PST) Date: Fri, 9 Dec 2022 22:16:26 -0800 In-Reply-To: <20221210061626.948426-1-badhri@google.com> Mime-Version: 1.0 References: <20221210061626.948426-1-badhri@google.com> X-Mailer: git-send-email 2.39.0.rc1.256.g54fd8350bd-goog Message-ID: <20221210061626.948426-3-badhri@google.com> Subject: [PATCH v9 3/3] 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 Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-9.6 required=5.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,USER_IN_DEF_DKIM_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 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 V7: * None. Skipped versions by mistake. 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