From patchwork Mon Oct 12 06:23:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peng Fan X-Patchwork-Id: 317608 Delivered-To: patch@linaro.org Received: by 2002:a92:d603:0:0:0:0:0 with SMTP id w3csp4324247ilm; Sun, 11 Oct 2020 22:57:38 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzc2scIv75jkvWt7opNpL5UiN7EQd7krG/xtLHMkLYrMVjWjD5gORIvO84Du6Cj/Ogj4/Au X-Received: by 2002:a17:907:4365:: with SMTP id nd5mr27203943ejb.56.1602482258336; Sun, 11 Oct 2020 22:57:38 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1602482258; cv=pass; d=google.com; s=arc-20160816; b=qGNR/vt+tuU23jLMKBifYQEu6FvMcTlVTjdO0VDIuhoy4YEc64bSb4MYpGHUImZdvk 9Bgrq1G+GWY7DDE6JWmeevQoddMGdz1ReB/CQNkTtfXa1lC1d+mnHdepmBvEQkNZBxVK /vStJUwyEYUlhHg6MJhTuwjD3hyHi1zHuuRUpp2Vcs6ibtwjD2IQ83LBQcsLKuOYqG7H AQcr9gzyz+/Zc4Y/QkoOvJE9SYwHq45IgSOJQLl/kLi78m/mnev/tciuQRilFFixZUGz JJ5IBaPKWmzYX5bnylTyajG0ISPQZsbzWYD9WakcjTyATMj1pvnbeBQbD8IIZYoigMFy KLZQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:mime-version :content-transfer-encoding:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=D2ZlP0XTAtdi5EfC6i086Ftopf+P5iQNzLsce3CDmxM=; b=GKIMIywyeG++LJdZwef4gEtoNZ4RKyOeLRhOnkkm/eTawPcYqZ4uG12RQ6yJJ4PWbW Xf5zbICadJJMw3dGM4AXUMvLrThvJV9Chi6FaeF/761zz4iTNgbFbajrpnr5me2Os4C6 xlTxCcHm61s5uYm4OaZC7oUaNg8fyUtDFIPvtmj7qyuIyw33nD7cXqvvpyR2LhJOnm0A 5NEQDIs9IaJ2dmdjWQai0BI08iSHUaBszAp8YeJo8yvwvTWW+FdMC3t91kLpzvHfGyJD f+fyeJ1HQSoLfhBmyZaXJd1wuFdkRHUhrste+Jwlhf7wp4KS1i5EGAzzfqmsqXKb79+K C2JQ== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@nxp.com header.s=selector2 header.b=gR9gm5zf; arc=pass (i=1 spf=pass spfdomain=nxp.com dkim=pass dkdomain=nxp.com dmarc=pass fromdomain=nxp.com); spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=nxp.com Return-Path: Received: from phobos.denx.de (phobos.denx.de. [85.214.62.61]) by mx.google.com with ESMTPS id x13si8069713edq.569.2020.10.11.22.57.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 11 Oct 2020 22:57:38 -0700 (PDT) Received-SPF: pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 as permitted sender) client-ip=85.214.62.61; Authentication-Results: mx.google.com; dkim=pass header.i=@nxp.com header.s=selector2 header.b=gR9gm5zf; arc=pass (i=1 spf=pass spfdomain=nxp.com dkim=pass dkdomain=nxp.com dmarc=pass fromdomain=nxp.com); spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=nxp.com Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 728F38247C; Mon, 12 Oct 2020 07:57:26 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.b="gR9gm5zf"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 9C2998247D; Mon, 12 Oct 2020 07:57:22 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-0.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FORGED_SPF_HELO,MSGID_FROM_MTA_HEADER, SPF_HELO_PASS,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.2 Received: from EUR01-DB5-obe.outbound.protection.outlook.com (mail-db5eur01on0600.outbound.protection.outlook.com [IPv6:2a01:111:f400:fe02::600]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 0BBBD8246B for ; Mon, 12 Oct 2020 07:57:18 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=peng.fan@nxp.com ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=ZoGH6fxBhITALkiOnyZEj/xPkKiOgoassbKsXLsrqWJbQF5OSZAZ3QPK2DohYKAEk24rjCSHJQgoQ9vz/jyRMw9M8YSjA1G6vEteDP2nbbxtplTrCov3OjsPQ8u8V8+dzhouG/pYZ2cAnPVlV2NJ0Kjlap/gWxrts/MTghG4KAF2sJvL6GZvYVpixnJjPvJsEf29Ikb4Y7miyOGuOHpgpitYKT/2yp3eOvQRpCXAeCNJbv1Tic2n8KwhsvDg6q89Wf/E4xskiv06V9BggRDZVCnTR+6m6yE/RlXf0GOEH8DSH154tSsmHi/8BPbBriSOFRsDaU0sn3EryG+urhRc4A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=D2ZlP0XTAtdi5EfC6i086Ftopf+P5iQNzLsce3CDmxM=; b=F01YQCIQMqS2V0oZVwC5W5zfi5wPEamCfXhkJXwtKMq3lxnwAi5TXS1JFCQ8Qtm7Drv2nguD/OSdPUu++GbkG/pxxZwHG8lamhmYz5KkdcdsDjGGO064EcPdorwIiAi5z+2mbNxJM5I9YAybg+LFDY2KHNVTxRUB0HIIg5XIjPffS2gU+S2tQ7Nahb9nuPZcD+RnvFYAABHMoW559B55wv55EMW5QWpTXcmGXEjltzBHCjRq5fMwFNpOhglBeSC9rJQ3RdPs3SeCGPFpsDUkoKoQrqUP574t9Fmaff7zy8hlRDxfZ3fPC+dXHoM8S86hF5RwycKp2k/zGjakIbCu3A== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=D2ZlP0XTAtdi5EfC6i086Ftopf+P5iQNzLsce3CDmxM=; b=gR9gm5zfXLGyRcLa3H2CrG/+jrR+7ITI0efAklw9LjFwaDjf4QDu/JEVMsjpO47dCnj2HRiEpTl8PxP5g2fLcal8UOzhRVp7gTVWJzB9oG7Qke31tDPW4D5qQ9TIgCi+u1mlOundTUPKYvVKNB+kZ3wOuyyx53XcvJ0dNtljtcA= Authentication-Results: denx.de; dkim=none (message not signed) header.d=none;denx.de; dmarc=none action=none header.from=nxp.com; Received: from DB6PR0402MB2760.eurprd04.prod.outlook.com (2603:10a6:4:a1::14) by DB7PR04MB4235.eurprd04.prod.outlook.com (2603:10a6:5:22::25) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3455.26; Mon, 12 Oct 2020 05:57:16 +0000 Received: from DB6PR0402MB2760.eurprd04.prod.outlook.com ([fe80::35c5:8c71:91f3:6bc6]) by DB6PR0402MB2760.eurprd04.prod.outlook.com ([fe80::35c5:8c71:91f3:6bc6%12]) with mapi id 15.20.3455.029; Mon, 12 Oct 2020 05:57:16 +0000 From: Peng Fan To: sbabic@denx.de, festevam@gmail.com, marex@denx.de, lukma@denx.de Cc: uboot-imx@nxp.com, u-boot@lists.denx.de, Ye Li , Li Jun , Peng Fan Subject: [PATCH 1/8] tcpc: Add driver for USB typec port controller (TCPC) Date: Mon, 12 Oct 2020 14:23:47 +0800 Message-Id: <20201012062354.3743-2-peng.fan@nxp.com> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20201012062354.3743-1-peng.fan@nxp.com> References: <20201012062354.3743-1-peng.fan@nxp.com> X-Originating-IP: [119.31.174.71] X-ClientProxiedBy: SG2PR01CA0105.apcprd01.prod.exchangelabs.com (2603:1096:3:15::31) To DB6PR0402MB2760.eurprd04.prod.outlook.com (2603:10a6:4:a1::14) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from linux-1xn6.ap.freescale.net (119.31.174.71) by SG2PR01CA0105.apcprd01.prod.exchangelabs.com (2603:1096:3:15::31) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3455.22 via Frontend Transport; Mon, 12 Oct 2020 05:57:12 +0000 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-HT: Tenant X-MS-Office365-Filtering-Correlation-Id: 7ec51cc5-e920-40e6-6139-08d86e73abd2 X-MS-TrafficTypeDiagnostic: DB7PR04MB4235: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:370; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 0MVq2ClmRR/f5MmPOSDNLWh6t8IWPMAF2aGIKmOBTeudWCIcyHU50vaMTczHc6qO2QMBc47M1WhyFSrcLBwZGPw6PemAFJjxqkiZ1NBlLuIpUP6yMAGqQkmbYufnKvZ6PMWVYra/zO3jqT7VJKq0sCQxNhidTJm7uGC2rtmflIJIyrUfEdgzSeD4yFnIaq1pu97/x8MrIPi+67nJTkzY+5zx9MbjxQSx0N3jQnv3ubvPE/Cr2BOIOBUtq60BtxXvBl2Xgv4bEdG/ukFyEdC9uwsAOE5j9vRk6mnw/6gHvtbK3EzOZKPwCIO1chEQ0PTATlO5xouCiVxmRFpLEwRlBTCyqub3N9uckn4hfwPisqUz0SpuDUZactk3sX76E+d/ X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:DB6PR0402MB2760.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(4636009)(396003)(39860400002)(376002)(346002)(366004)(136003)(52116002)(83380400001)(8936002)(6506007)(4326008)(8676002)(44832011)(30864003)(6512007)(1076003)(6486002)(86362001)(5660300002)(6666004)(66476007)(66946007)(66556008)(316002)(36756003)(54906003)(2616005)(956004)(2906002)(186003)(26005)(478600001)(16526019)(21314003)(579004); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData: 2Iryetzo0Hho+HKCWX53wBw3iVLPwwDmGpKwmZOqL0DwQAhSNnu9DNAq/nLTOBrdq5sSbAJOzlWmw2P85vyOu7zAz6jFGgtr9My7w69svL6DM6ytwSaZJyf2bk0hZtXQVL1muJvw011/avbnjIMVLxhhxsl89goMd5DxQBJwnMpe8ssiyxS6X+DcThSlTij177egivdf86ch52qkFY54h8JBGexF+bii3WfSGU0vI44AmyEmUkGU1RCGEbCuyHqhsD8YW8H2B2aDkw6GZ54N8lDGMuN01ctnDRrM4yR4q5AAdEUnqeZCkxbivVKcSyTPsgX2mkIucHOuI15KhFhQ9xMFGiIlg5O34RX6JCqXzr9TzlfUFP2wYZY3X3+rdZd9YsFjplxbWb31XZyTXUGlogsd3jDOfZD0/lc3/7s8OJCZG2aCEqB+w6jbyjSad1FY9VPi9BUG7Gejdg2ismKD+uA3/eyWYngI9RSpzvgLtGO5KOGrvpORnWQToxPPTIonFPjO7R28MdK6tLqHuRT+cVaId0LnB93yEizOGEeRdBDY3r1xxh4BACI6U2KI+lkabTV18VPDcR0TSiijO7oZwmUvxeffwQZoqMVR7RMIo48sDtOo/JDIp02wUPMwBpqBFIdhP7L9ZnPXs7L0rFSlrw== X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 7ec51cc5-e920-40e6-6139-08d86e73abd2 X-MS-Exchange-CrossTenant-AuthSource: DB6PR0402MB2760.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Oct 2020 05:57:16.1148 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: fQPZYCJxVBSOfryy38iMWeMRMOJRWErTfqkVKq3dljZNCZHoBx/w76UXiDifhjz4WkkwbCx1LO1+CElCXt046g== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB7PR04MB4235 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.3 at phobos.denx.de X-Virus-Status: Clean From: Ye Li The functionalities in this driver include: 1. USB power delivery support at dead battery 2. Support configure to UFP or DFP mode 3. Support callback to setup external PD switch. When PD process is enabled, we call this function only when SINK_VBUS is enabled to avoid system power shut down. Signed-off-by: Li Jun Signed-off-by: Ye Li Signed-off-by: Peng Fan --- board/freescale/common/Kconfig | 6 + board/freescale/common/Makefile | 4 + board/freescale/common/tcpc.c | 1018 +++++++++++++++++++++++++++++++ board/freescale/common/tcpc.h | 469 ++++++++++++++ 4 files changed, 1497 insertions(+) create mode 100644 board/freescale/common/tcpc.c create mode 100644 board/freescale/common/tcpc.h -- 2.28.0 diff --git a/board/freescale/common/Kconfig b/board/freescale/common/Kconfig index 1b1fd69cb2..eccfc12bfc 100644 --- a/board/freescale/common/Kconfig +++ b/board/freescale/common/Kconfig @@ -36,3 +36,9 @@ config VOL_MONITOR_LTC3882_SET help This option enables LTC3882 voltage monitor set functionality. It is used by common VID driver. + +config USB_TCPC + bool "USB Typec port controller simple driver" + default n + help + Enable USB type-c port controller (TCPC) driver diff --git a/board/freescale/common/Makefile b/board/freescale/common/Makefile index 04e04a6358..8f1ce67f66 100644 --- a/board/freescale/common/Makefile +++ b/board/freescale/common/Makefile @@ -79,4 +79,8 @@ obj-$(CONFIG_CMD_ESBC_VALIDATE) += fsl_validate.o cmd_esbc_validate.o endif obj-$(CONFIG_CHAIN_OF_TRUST) += fsl_chain_of_trust.o +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_USB_TCPC) += tcpc.o +endif + endif diff --git a/board/freescale/common/tcpc.c b/board/freescale/common/tcpc.c new file mode 100644 index 0000000000..fb532a3d87 --- /dev/null +++ b/board/freescale/common/tcpc.c @@ -0,0 +1,1018 @@ +/* + * Copyright 2017,2019 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include "tcpc.h" + +#ifdef DEBUG +#define tcpc_debug_log(port, fmt, args...) tcpc_log(port, fmt, ##args) +#else +#define tcpc_debug_log(port, fmt, args...) +#endif + +static int tcpc_log(struct tcpc_port *port, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vscnprintf(port->log_p, port->log_size, fmt, args); + va_end(args); + + port->log_size -= i; + port->log_p += i; + + return i; +} + +int tcpc_set_cc_to_source(struct tcpc_port *port) +{ + uint8_t valb; + int err; + + if (port == NULL) + return -EINVAL; + + valb = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | + (TCPC_ROLE_CTRL_RP_VAL_DEF << + TCPC_ROLE_CTRL_RP_VAL_SHIFT) | TCPC_ROLE_CTRL_DRP; + + err = dm_i2c_write(port->i2c_dev, TCPC_ROLE_CTRL, &valb, 1); + if (err) + tcpc_log(port, "%s dm_i2c_write failed, err %d\n", __func__, err); + return err; +} + +int tcpc_set_cc_to_sink(struct tcpc_port *port) +{ + uint8_t valb; + int err; + + if (port == NULL) + return -EINVAL; + + valb = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT) | TCPC_ROLE_CTRL_DRP; + + err = dm_i2c_write(port->i2c_dev, TCPC_ROLE_CTRL, &valb, 1); + if (err) + tcpc_log(port, "%s dm_i2c_write failed, err %d\n", __func__, err); + return err; +} + + +int tcpc_set_plug_orientation(struct tcpc_port *port, enum typec_cc_polarity polarity) +{ + uint8_t valb; + int err; + + if (port == NULL) + return -EINVAL; + + err = dm_i2c_read(port->i2c_dev, TCPC_TCPC_CTRL, &valb, 1); + if (err) { + tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, err); + return -EIO; + } + + if (polarity == TYPEC_POLARITY_CC2) + valb |= TCPC_TCPC_CTRL_ORIENTATION; + else + valb &= ~TCPC_TCPC_CTRL_ORIENTATION; + + err = dm_i2c_write(port->i2c_dev, TCPC_TCPC_CTRL, &valb, 1); + if (err) { + tcpc_log(port, "%s dm_i2c_write failed, err %d\n", __func__, err); + return -EIO; + } + + return 0; +} + +int tcpc_get_cc_status(struct tcpc_port *port, enum typec_cc_polarity *polarity, enum typec_cc_state *state) +{ + + uint8_t valb_cc, cc2, cc1; + int err; + + if (port == NULL || polarity == NULL || state == NULL) + return -EINVAL; + + err = dm_i2c_read(port->i2c_dev, TCPC_CC_STATUS, (uint8_t *)&valb_cc, 1); + if (err) { + tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, err); + return -EIO; + } + + tcpc_debug_log(port, "cc status 0x%x\n", valb_cc); + + cc2 = (valb_cc >> TCPC_CC_STATUS_CC2_SHIFT) & TCPC_CC_STATUS_CC2_MASK; + cc1 = (valb_cc >> TCPC_CC_STATUS_CC1_SHIFT) & TCPC_CC_STATUS_CC1_MASK; + + if (valb_cc & TCPC_CC_STATUS_LOOK4CONN) + return -EFAULT; + + *state = TYPEC_STATE_OPEN; + + if (valb_cc & TCPC_CC_STATUS_TERM) { + if (cc2) { + *polarity = TYPEC_POLARITY_CC2; + + switch (cc2) { + case 0x1: + *state = TYPEC_STATE_SNK_DEFAULT; + tcpc_log(port, "SNK.Default on CC2\n"); + break; + case 0x2: + *state = TYPEC_STATE_SNK_POWER15; + tcpc_log(port, "SNK.Power1.5 on CC2\n"); + break; + case 0x3: + *state = TYPEC_STATE_SNK_POWER30; + tcpc_log(port, "SNK.Power3.0 on CC2\n"); + break; + } + } else if (cc1) { + *polarity = TYPEC_POLARITY_CC1; + + switch (cc1) { + case 0x1: + *state = TYPEC_STATE_SNK_DEFAULT; + tcpc_log(port, "SNK.Default on CC1\n"); + break; + case 0x2: + *state = TYPEC_STATE_SNK_POWER15; + tcpc_log(port, "SNK.Power1.5 on CC1\n"); + break; + case 0x3: + *state = TYPEC_STATE_SNK_POWER30; + tcpc_log(port, "SNK.Power3.0 on CC1\n"); + break; + } + } else { + *state = TYPEC_STATE_OPEN; + return -EPERM; + } + + } else { + if (cc2) { + *polarity = TYPEC_POLARITY_CC2; + + switch (cc2) { + case 0x1: + if (cc1 == 0x1) { + *state = TYPEC_STATE_SRC_BOTH_RA; + tcpc_log(port, "SRC.Ra on both CC1 and CC2\n"); + } else if (cc1 == 0x2) { + *state = TYPEC_STATE_SRC_RD_RA; + tcpc_log(port, "SRC.Ra on CC2, SRC.Rd on CC1\n"); + } else if (cc1 == 0x0) { + tcpc_log(port, "SRC.Ra only on CC2\n"); + return -EFAULT; + } else + return -EFAULT; + break; + case 0x2: + if (cc1 == 0x1) { + *state = TYPEC_STATE_SRC_RD_RA; + tcpc_log(port, "SRC.Ra on CC1, SRC.Rd on CC2\n"); + } else if (cc1 == 0x0) { + *state = TYPEC_STATE_SRC_RD; + tcpc_log(port, "SRC.Rd on CC2\n"); + } else + return -EFAULT; + break; + case 0x3: + *state = TYPEC_STATE_SRC_RESERVED; + return -EFAULT; + } + } else if (cc1) { + *polarity = TYPEC_POLARITY_CC1; + + switch (cc1) { + case 0x1: + tcpc_log(port, "SRC.Ra only on CC1\n"); + return -EFAULT; + case 0x2: + *state = TYPEC_STATE_SRC_RD; + tcpc_log(port, "SRC.Rd on CC1\n"); + break; + case 0x3: + *state = TYPEC_STATE_SRC_RESERVED; + return -EFAULT; + } + } else { + *state = TYPEC_STATE_OPEN; + return -EPERM; + } + } + + return 0; +} + +int tcpc_clear_alert(struct tcpc_port *port, uint16_t clear_mask) +{ + int err; + + if (port == NULL) + return -EINVAL; + + err = dm_i2c_write(port->i2c_dev, TCPC_ALERT, (const uint8_t *)&clear_mask, 2); + if (err) { + tcpc_log(port, "%s dm_i2c_write failed, err %d\n", __func__, err); + return -EIO; + } + + return 0; +} + +int tcpc_send_command(struct tcpc_port *port, uint8_t command) +{ + int err; + + if (port == NULL) + return -EINVAL; + + err = dm_i2c_write(port->i2c_dev, TCPC_COMMAND, (const uint8_t *)&command, 1); + if (err) { + tcpc_log(port, "%s dm_i2c_write failed, err %d\n", __func__, err); + return -EIO; + } + + return 0; +} + +int tcpc_polling_reg(struct tcpc_port *port, uint8_t reg, + uint8_t reg_width, uint16_t mask, uint16_t value, ulong timeout_ms) +{ + uint16_t val = 0; + int err; + ulong start; + + if (port == NULL) + return -EINVAL; + + tcpc_debug_log(port, "%s reg 0x%x, mask 0x%x, value 0x%x\n", __func__, reg, mask, value); + + /* TCPC registers is 8 bits or 16 bits */ + if (reg_width != 1 && reg_width != 2) + return -EINVAL; + + start = get_timer(0); /* Get current timestamp */ + do { + err = dm_i2c_read(port->i2c_dev, reg, (uint8_t *)&val, reg_width); + if (err) + return -EIO; + + if ((val & mask) == value) + return 0; + } while (get_timer(0) < (start + timeout_ms)); + + return -ETIME; +} + +void tcpc_print_log(struct tcpc_port *port) +{ + if (port == NULL) + return; + + if (port->log_print == port->log_p) /*nothing to output*/ + return; + + printf("%s", port->log_print); + + port->log_print = port->log_p; +} + +int tcpc_setup_dfp_mode(struct tcpc_port *port) +{ + enum typec_cc_polarity pol; + enum typec_cc_state state; + int ret; + + if ((port == NULL) || (port->i2c_dev == NULL)) + return -EINVAL; + + if (tcpc_pd_sink_check_charging(port)) { + tcpc_log(port, "%s: Can't apply DFP mode when PD is charging\n", + __func__); + return -EPERM; + } + + tcpc_set_cc_to_source(port); + + ret = tcpc_send_command(port, TCPC_CMD_LOOK4CONNECTION); + if (ret) + return ret; + + /* At least wait tCcStatusDelay + tTCPCFilter + tCcTCPCSampleRate (max) = 200us + 500us + ?ms + * PTN5110 datasheet does not contain the sample rate value, according other productions, + * the sample rate is at ms level, about 2 ms -10ms. So wait 100ms should be enough. + */ + mdelay(100); + + ret = tcpc_polling_reg(port, TCPC_ALERT, 2, TCPC_ALERT_CC_STATUS, TCPC_ALERT_CC_STATUS, 100); + if (ret) { + tcpc_log(port, "%s: Polling ALERT register, TCPC_ALERT_CC_STATUS bit failed, ret = %d\n", + __func__, ret); + return ret; + } + + ret = tcpc_get_cc_status(port, &pol, &state); + tcpc_clear_alert(port, TCPC_ALERT_CC_STATUS); + + if (!ret) { + /* If presenting as Rd/audio mode/open, return */ + if (state != TYPEC_STATE_SRC_RD_RA && state != TYPEC_STATE_SRC_RD) + return -EPERM; + + if (pol == TYPEC_POLARITY_CC1) + tcpc_debug_log(port, "polarity cc1\n"); + else + tcpc_debug_log(port, "polarity cc2\n"); + + if (port->ss_sel_func) + port->ss_sel_func(pol); + + ret = tcpc_set_plug_orientation(port, pol); + if (ret) + return ret; + + /* Enable source vbus default voltage */ + ret = tcpc_send_command(port, TCPC_CMD_SRC_VBUS_DEFAULT); + if (ret) + return ret; + + /* The max vbus on time is 200ms, we add margin 100ms */ + mdelay(300); + + } + + return 0; +} + +int tcpc_setup_ufp_mode(struct tcpc_port *port) +{ + enum typec_cc_polarity pol; + enum typec_cc_state state; + int ret; + + if ((port == NULL) || (port->i2c_dev == NULL)) + return -EINVAL; + + /* Check if the PD charge is working. If not, need to configure CC role for UFP */ + if (!tcpc_pd_sink_check_charging(port)) { + + /* Disable the source vbus once it is enabled by DFP mode */ + tcpc_disable_src_vbus(port); + + tcpc_set_cc_to_sink(port); + + ret = tcpc_send_command(port, TCPC_CMD_LOOK4CONNECTION); + if (ret) + return ret; + + /* At least wait tCcStatusDelay + tTCPCFilter + tCcTCPCSampleRate (max) = 200us + 500us + ?ms + * PTN5110 datasheet does not contain the sample rate value, according other productions, + * the sample rate is at ms level, about 2 ms -10ms. So wait 100ms should be enough. + */ + mdelay(100); + + ret = tcpc_polling_reg(port, TCPC_ALERT, 2, TCPC_ALERT_CC_STATUS, TCPC_ALERT_CC_STATUS, 100); + if (ret) { + tcpc_log(port, "%s: Polling ALERT register, TCPC_ALERT_CC_STATUS bit failed, ret = %d\n", + __func__, ret); + return ret; + } + + ret = tcpc_get_cc_status(port, &pol, &state); + tcpc_clear_alert(port, TCPC_ALERT_CC_STATUS); + + } else { + ret = tcpc_get_cc_status(port, &pol, &state); + } + + if (!ret) { + /* If presenting not as sink, then return */ + if (state != TYPEC_STATE_SNK_DEFAULT && state != TYPEC_STATE_SNK_POWER15 && + state != TYPEC_STATE_SNK_POWER30) + return -EPERM; + + if (pol == TYPEC_POLARITY_CC1) + tcpc_debug_log(port, "polarity cc1\n"); + else + tcpc_debug_log(port, "polarity cc2\n"); + + if (port->ss_sel_func) + port->ss_sel_func(pol); + + ret = tcpc_set_plug_orientation(port, pol); + if (ret) + return ret; + } + + return 0; +} + +int tcpc_disable_src_vbus(struct tcpc_port *port) +{ + int ret; + + if (port == NULL) + return -EINVAL; + + /* Disable VBUS*/ + ret = tcpc_send_command(port, TCPC_CMD_DISABLE_SRC_VBUS); + if (ret) + return ret; + + /* The max vbus off time is 0.5ms, we add margin 0.5 ms */ + mdelay(1); + + return 0; +} + +int tcpc_disable_sink_vbus(struct tcpc_port *port) +{ + int ret; + + if (port == NULL) + return -EINVAL; + + /* Disable SINK VBUS*/ + ret = tcpc_send_command(port, TCPC_CMD_DISABLE_SINK_VBUS); + if (ret) + return ret; + + /* The max vbus off time is 0.5ms, we add margin 0.5 ms */ + mdelay(1); + + return 0; +} + + +static int tcpc_pd_receive_message(struct tcpc_port *port, struct pd_message *msg) +{ + int ret; + uint8_t cnt; + uint16_t val; + + if (port == NULL) + return -EINVAL; + + /* Generally the max tSenderResponse is 30ms, max tTypeCSendSourceCap is 200ms, we set the timeout to 500ms */ + ret = tcpc_polling_reg(port, TCPC_ALERT, 2, TCPC_ALERT_RX_STATUS, TCPC_ALERT_RX_STATUS, 500); + if (ret) { + tcpc_log(port, "%s: Polling ALERT register, TCPC_ALERT_RX_STATUS bit failed, ret = %d\n", + __func__, ret); + return ret; + } + + cnt = 0; + ret = dm_i2c_read(port->i2c_dev, TCPC_RX_BYTE_CNT, (uint8_t *)&cnt, 1); + if (ret) + return -EIO; + + if (cnt > 0) { + ret = dm_i2c_read(port->i2c_dev, TCPC_RX_BUF_FRAME_TYPE, (uint8_t *)msg, cnt); + if (ret) + return -EIO; + + /* Clear RX status alert bit */ + val = TCPC_ALERT_RX_STATUS; + ret = dm_i2c_write(port->i2c_dev, TCPC_ALERT, (const uint8_t *)&val, 2); + if (ret) + return -EIO; + } + + return cnt; +} + +static int tcpc_pd_transmit_message(struct tcpc_port *port, struct pd_message *msg_p, uint8_t bytes) +{ + int ret; + uint8_t valb; + uint16_t val; + + if (port == NULL) + return -EINVAL; + + if (msg_p == NULL || bytes <= 0) + return -EINVAL; + + ret = dm_i2c_write(port->i2c_dev, TCPC_TX_BYTE_CNT, (const uint8_t *)&bytes, 1); + if (ret) + return -EIO; + + ret = dm_i2c_write(port->i2c_dev, TCPC_TX_HDR, (const uint8_t *)&(msg_p->header), bytes); + if (ret) + return -EIO; + + valb = (3 << TCPC_TRANSMIT_RETRY_SHIFT) | (TCPC_TX_SOP << TCPC_TRANSMIT_TYPE_SHIFT); + ret = dm_i2c_write(port->i2c_dev, TCPC_TRANSMIT, (const uint8_t *)&valb, 1); + if (ret) + return -EIO; + + /* Max tReceive is 1.1ms, we set to 5ms timeout */ + ret = tcpc_polling_reg(port, TCPC_ALERT, 2, TCPC_ALERT_TX_SUCCESS, TCPC_ALERT_TX_SUCCESS, 5); + if (ret) { + if (ret == -ETIME) { + ret = dm_i2c_read(port->i2c_dev, TCPC_ALERT, (uint8_t *)&val, 2); + if (ret) + return -EIO; + + if (val & TCPC_ALERT_TX_FAILED) + tcpc_log(port, "%s: PD TX FAILED, ALERT = 0x%x\n", __func__, val); + + if (val & TCPC_ALERT_TX_DISCARDED) + tcpc_log(port, "%s: PD TX DISCARDED, ALERT = 0x%x\n", __func__, val); + + } else { + tcpc_log(port, "%s: Polling ALERT register, TCPC_ALERT_TX_SUCCESS bit failed, ret = %d\n", + __func__, ret); + } + } else { + port->tx_msg_id = (port->tx_msg_id + 1) & PD_HEADER_ID_MASK; + } + + /* Clear ALERT status */ + val &= (TCPC_ALERT_TX_FAILED | TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_TX_SUCCESS); + ret = dm_i2c_write(port->i2c_dev, TCPC_ALERT, (const uint8_t *)&val, 2); + if (ret) + return -EIO; + + return ret; +} + +static void tcpc_log_source_caps(struct tcpc_port *port, struct pd_message *msg, unsigned int capcount) +{ + int i; + + for (i = 0; i < capcount; i++) { + u32 pdo = msg->payload[i]; + enum pd_pdo_type type = pdo_type(pdo); + + tcpc_log(port, "PDO %d: type %d, ", + i, type); + + switch (type) { + case PDO_TYPE_FIXED: + tcpc_log(port, "%u mV, %u mA [%s%s%s%s%s%s]\n", + pdo_fixed_voltage(pdo), + pdo_max_current(pdo), + (pdo & PDO_FIXED_DUAL_ROLE) ? + "R" : "", + (pdo & PDO_FIXED_SUSPEND) ? + "S" : "", + (pdo & PDO_FIXED_HIGHER_CAP) ? + "H" : "", + (pdo & PDO_FIXED_USB_COMM) ? + "U" : "", + (pdo & PDO_FIXED_DATA_SWAP) ? + "D" : "", + (pdo & PDO_FIXED_EXTPOWER) ? + "E" : ""); + break; + case PDO_TYPE_VAR: + tcpc_log(port, "%u-%u mV, %u mA\n", + pdo_min_voltage(pdo), + pdo_max_voltage(pdo), + pdo_max_current(pdo)); + break; + case PDO_TYPE_BATT: + tcpc_log(port, "%u-%u mV, %u mW\n", + pdo_min_voltage(pdo), + pdo_max_voltage(pdo), + pdo_max_power(pdo)); + break; + default: + tcpc_log(port, "undefined\n"); + break; + } + } +} + +static int tcpc_pd_select_pdo(struct pd_message *msg, uint32_t capcount, uint32_t max_snk_mv, uint32_t max_snk_ma) +{ + unsigned int i, max_mw = 0, max_mv = 0; + int ret = -EINVAL; + + /* + * Select the source PDO providing the most power while staying within + * the board's voltage limits. Prefer PDO providing exp + */ + for (i = 0; i < capcount; i++) { + u32 pdo = msg->payload[i]; + enum pd_pdo_type type = pdo_type(pdo); + unsigned int mv, ma, mw; + + if (type == PDO_TYPE_FIXED) + mv = pdo_fixed_voltage(pdo); + else + mv = pdo_min_voltage(pdo); + + if (type == PDO_TYPE_BATT) { + mw = pdo_max_power(pdo); + } else { + ma = min(pdo_max_current(pdo), + max_snk_ma); + mw = ma * mv / 1000; + } + + /* Perfer higher voltages if available */ + if ((mw > max_mw || (mw == max_mw && mv > max_mv)) && + mv <= max_snk_mv) { + ret = i; + max_mw = mw; + max_mv = mv; + } + } + + return ret; +} + +static int tcpc_pd_build_request(struct tcpc_port *port, + struct pd_message *msg, + uint32_t capcount, + uint32_t max_snk_mv, + uint32_t max_snk_ma, + uint32_t max_snk_mw, + uint32_t operating_snk_mw, + uint32_t *rdo) +{ + unsigned int mv, ma, mw, flags; + unsigned int max_ma, max_mw; + enum pd_pdo_type type; + int index; + u32 pdo; + + index = tcpc_pd_select_pdo(msg, capcount, max_snk_mv, max_snk_ma); + if (index < 0) + return -EINVAL; + + pdo = msg->payload[index]; + type = pdo_type(pdo); + + if (type == PDO_TYPE_FIXED) + mv = pdo_fixed_voltage(pdo); + else + mv = pdo_min_voltage(pdo); + + /* Select maximum available current within the board's power limit */ + if (type == PDO_TYPE_BATT) { + mw = pdo_max_power(pdo); + ma = 1000 * min(mw, max_snk_mw) / mv; + } else { + ma = min(pdo_max_current(pdo), + 1000 * max_snk_mw / mv); + } + ma = min(ma, max_snk_ma); + + /* XXX: Any other flags need to be set? */ + flags = 0; + + /* Set mismatch bit if offered power is less than operating power */ + mw = ma * mv / 1000; + max_ma = ma; + max_mw = mw; + if (mw < operating_snk_mw) { + flags |= RDO_CAP_MISMATCH; + max_mw = operating_snk_mw; + max_ma = max_mw * 1000 / mv; + } + + if (type == PDO_TYPE_BATT) { + *rdo = RDO_BATT(index + 1, mw, max_mw, flags); + + tcpc_log(port, "Requesting PDO %d: %u mV, %u mW%s\n", + index, mv, mw, + flags & RDO_CAP_MISMATCH ? " [mismatch]" : ""); + } else { + *rdo = RDO_FIXED(index + 1, ma, max_ma, flags); + + tcpc_log(port, "Requesting PDO %d: %u mV, %u mA%s\n", + index, mv, ma, + flags & RDO_CAP_MISMATCH ? " [mismatch]" : ""); + } + + return 0; +} + +static void tcpc_pd_sink_process(struct tcpc_port *port) +{ + int ret; + uint8_t msgtype; + uint32_t objcnt; + struct pd_message msg; + enum pd_sink_state pd_state = WAIT_SOURCE_CAP; + + while (tcpc_pd_receive_message(port, &msg) > 0) { + + msgtype = pd_header_type(msg.header); + objcnt = pd_header_cnt_le(msg.header); + + tcpc_debug_log(port, "get msg, type %d, cnt %d\n", msgtype, objcnt); + + switch (pd_state) { + case WAIT_SOURCE_CAP: + case SINK_READY: + if (msgtype != PD_DATA_SOURCE_CAP) + continue; + + uint32_t rdo = 0; + + tcpc_log_source_caps(port, &msg, objcnt); + + tcpc_pd_build_request(port, &msg, objcnt, + port->cfg.max_snk_mv, port->cfg.max_snk_ma, + port->cfg.max_snk_mw, port->cfg.op_snk_mv, + &rdo); + + memset(&msg, 0, sizeof(msg)); + msg.header = PD_HEADER(PD_DATA_REQUEST, 0, 0, port->tx_msg_id, 1); /* power sink, data device, id 0, len 1 */ + msg.payload[0] = rdo; + + ret = tcpc_pd_transmit_message(port, &msg, 6); + if (ret) + tcpc_log(port, "send request failed\n"); + else + pd_state = WAIT_SOURCE_ACCEPT; + + break; + case WAIT_SOURCE_ACCEPT: + if (objcnt > 0) /* Should be ctrl message */ + continue; + + if (msgtype == PD_CTRL_ACCEPT) { + pd_state = WAIT_SOURCE_READY; + tcpc_log(port, "Source accept request\n"); + } else if (msgtype == PD_CTRL_REJECT) { + tcpc_log(port, "Source reject request\n"); + return; + } + + break; + case WAIT_SOURCE_READY: + if (objcnt > 0) /* Should be ctrl message */ + continue; + + if (msgtype == PD_CTRL_PS_RDY) { + tcpc_log(port, "PD source ready!\n"); + pd_state = SINK_READY; + } + + break; + default: + tcpc_log(port, "unexpect status: %u\n", pd_state); + break; + } + } +} + +bool tcpc_pd_sink_check_charging(struct tcpc_port *port) +{ + uint8_t valb; + int err; + enum typec_cc_polarity pol; + enum typec_cc_state state; + + if (port == NULL) + return false; + + /* Check the CC status, must be sink */ + err = tcpc_get_cc_status(port, &pol, &state); + if (err || (state != TYPEC_STATE_SNK_POWER15 + && state != TYPEC_STATE_SNK_POWER30 + && state != TYPEC_STATE_SNK_DEFAULT)) { + tcpc_debug_log(port, "TCPC wrong state for PD charging, err = %d, CC = 0x%x\n", + err, state); + return false; + } + + /* Check the VBUS PRES and SINK VBUS for dead battery */ + err = dm_i2c_read(port->i2c_dev, TCPC_POWER_STATUS, &valb, 1); + if (err) { + tcpc_debug_log(port, "%s dm_i2c_read failed, err %d\n", __func__, err); + return false; + } + + if (!(valb & TCPC_POWER_STATUS_VBUS_PRES)) { + tcpc_debug_log(port, "VBUS NOT PRES \n"); + return false; + } + + if (!(valb & TCPC_POWER_STATUS_SINKING_VBUS)) { + tcpc_debug_log(port, "SINK VBUS is not enabled for dead battery\n"); + return false; + } + + return true; +} + +static int tcpc_pd_sink_disable(struct tcpc_port *port) +{ + uint8_t valb; + int err; + + if (port == NULL) + return -EINVAL; + + port->pd_state = UNATTACH; + + /* Check the VBUS PRES and SINK VBUS for dead battery */ + err = dm_i2c_read(port->i2c_dev, TCPC_POWER_STATUS, &valb, 1); + if (err) { + tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, err); + return -EIO; + } + + if ((valb & TCPC_POWER_STATUS_VBUS_PRES) && (valb & TCPC_POWER_STATUS_SINKING_VBUS)) { + dm_i2c_read(port->i2c_dev, TCPC_POWER_CTRL, (uint8_t *)&valb, 1); + valb &= ~TCPC_POWER_CTRL_AUTO_DISCH_DISCO; /* disable AutoDischargeDisconnect */ + dm_i2c_write(port->i2c_dev, TCPC_POWER_CTRL, (const uint8_t *)&valb, 1); + + tcpc_disable_sink_vbus(port); + } + + if (port->cfg.switch_setup_func) + port->cfg.switch_setup_func(port); + + return 0; +} + +static int tcpc_pd_sink_init(struct tcpc_port *port) +{ + uint8_t valb; + uint16_t val; + int err; + enum typec_cc_polarity pol; + enum typec_cc_state state; + + if (port == NULL) + return -EINVAL; + + port->pd_state = UNATTACH; + + /* Check the VBUS PRES and SINK VBUS for dead battery */ + err = dm_i2c_read(port->i2c_dev, TCPC_POWER_STATUS, &valb, 1); + if (err) { + tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, err); + return -EIO; + } + + if (!(valb & TCPC_POWER_STATUS_VBUS_PRES)) { + tcpc_debug_log(port, "VBUS NOT PRES \n"); + return -EPERM; + } + + if (!(valb & TCPC_POWER_STATUS_SINKING_VBUS)) { + tcpc_debug_log(port, "SINK VBUS is not enabled for dead battery\n"); + return -EPERM; + } + + err = dm_i2c_read(port->i2c_dev, TCPC_ALERT, (uint8_t *)&val, 2); + if (err) { + tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, err); + return -EIO; + } + + if (!(val & TCPC_ALERT_CC_STATUS)) { + tcpc_debug_log(port, "CC STATUS not detected for dead battery\n"); + return -EPERM; + } + + err = tcpc_get_cc_status(port, &pol, &state); + if (err || (state != TYPEC_STATE_SNK_POWER15 + && state != TYPEC_STATE_SNK_POWER30 + && state != TYPEC_STATE_SNK_DEFAULT)) { + tcpc_log(port, "TCPC wrong state for dead battery, err = %d, CC = 0x%x\n", + err, state); + return -EPERM; + } else { + err = tcpc_set_plug_orientation(port, pol); + if (err) { + tcpc_log(port, "TCPC set plug orientation failed, err = %d\n", err); + return err; + } + port->pd_state = ATTACHED; + } + + dm_i2c_read(port->i2c_dev, TCPC_POWER_CTRL, (uint8_t *)&valb, 1); + valb &= ~TCPC_POWER_CTRL_AUTO_DISCH_DISCO; /* disable AutoDischargeDisconnect */ + dm_i2c_write(port->i2c_dev, TCPC_POWER_CTRL, (const uint8_t *)&valb, 1); + + if (port->cfg.switch_setup_func) + port->cfg.switch_setup_func(port); + + /* As sink role */ + valb = 0x00; + err = dm_i2c_write(port->i2c_dev, TCPC_MSG_HDR_INFO, (const uint8_t *)&valb, 1); + if (err) { + tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, err); + return -EIO; + } + + /* Enable rx */ + valb = TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET; + err = dm_i2c_write(port->i2c_dev, TCPC_RX_DETECT, (const uint8_t *)&valb, 1); + if (err) { + tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, err); + return -EIO; + } + + tcpc_pd_sink_process(port); + + return 0; +} + +int tcpc_init(struct tcpc_port *port, struct tcpc_port_config config, ss_mux_sel ss_sel_func) +{ + int ret; + uint8_t valb; + uint16_t vid, pid; + struct udevice *bus; + struct udevice *i2c_dev = NULL; + + memset(port, 0, sizeof(struct tcpc_port)); + + if (port == NULL) + return -EINVAL; + + port->cfg = config; + port->tx_msg_id = 0; + port->ss_sel_func = ss_sel_func; + port->log_p = (char *)&(port->logbuffer); + port->log_size = TCPC_LOG_BUFFER_SIZE; + port->log_print = port->log_p; + memset(&(port->logbuffer), 0, TCPC_LOG_BUFFER_SIZE); + + ret = uclass_get_device_by_seq(UCLASS_I2C, port->cfg.i2c_bus, &bus); + if (ret) { + printf("%s: Can't find bus\n", __func__); + return -EINVAL; + } + + ret = dm_i2c_probe(bus, port->cfg.addr, 0, &i2c_dev); + if (ret) { + printf("%s: Can't find device id=0x%x\n", + __func__, config.addr); + return -ENODEV; + } + + port->i2c_dev = i2c_dev; + + /* Check the Initialization Status bit in 1s */ + ret = tcpc_polling_reg(port, TCPC_POWER_STATUS, 1, TCPC_POWER_STATUS_UNINIT, 0, 1000); + if (ret) { + tcpc_log(port, "%s: Polling TCPC POWER STATUS Initialization Status bit failed, ret = %d\n", + __func__, ret); + return ret; + } + + dm_i2c_read(port->i2c_dev, TCPC_POWER_STATUS, &valb, 1); + tcpc_debug_log(port, "POWER STATUS: 0x%x\n", valb); + + /* Clear AllRegistersResetToDefault */ + valb = 0x80; + ret = dm_i2c_write(port->i2c_dev, TCPC_FAULT_STATUS, (const uint8_t *)&valb, 1); + if (ret) { + tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, ret); + return -EIO; + } + + /* Read Vendor ID and Product ID */ + ret = dm_i2c_read(port->i2c_dev, TCPC_VENDOR_ID, (uint8_t *)&vid, 2); + if (ret) { + tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, ret); + return -EIO; + } + + ret = dm_i2c_read(port->i2c_dev, TCPC_PRODUCT_ID, (uint8_t *)&pid, 2); + if (ret) { + tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, ret); + return -EIO; + } + + tcpc_log(port, "TCPC: Vendor ID [0x%x], Product ID [0x%x], Addr [I2C%u 0x%x]\n", + vid, pid, port->cfg.i2c_bus, port->cfg.addr); + + if (!port->cfg.disable_pd) { + if (port->cfg.port_type == TYPEC_PORT_UFP + || port->cfg.port_type == TYPEC_PORT_DRP) + tcpc_pd_sink_init(port); + } else { + tcpc_pd_sink_disable(port); + } + + tcpc_clear_alert(port, 0xffff); + + tcpc_print_log(port); + + return 0; +} diff --git a/board/freescale/common/tcpc.h b/board/freescale/common/tcpc.h new file mode 100644 index 0000000000..ff749518a8 --- /dev/null +++ b/board/freescale/common/tcpc.h @@ -0,0 +1,469 @@ +/* + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __TCPCI_H +#define __TCPCI_H + +#include + +#define TCPC_VENDOR_ID 0x0 +#define TCPC_PRODUCT_ID 0x2 + +#define TCPC_ALERT 0x10 +#define TCPC_ALERT_VBUS_DISCNCT BIT(11) +#define TCPC_ALERT_RX_BUF_OVF BIT(10) +#define TCPC_ALERT_FAULT BIT(9) +#define TCPC_ALERT_V_ALARM_LO BIT(8) +#define TCPC_ALERT_V_ALARM_HI BIT(7) +#define TCPC_ALERT_TX_SUCCESS BIT(6) +#define TCPC_ALERT_TX_DISCARDED BIT(5) +#define TCPC_ALERT_TX_FAILED BIT(4) +#define TCPC_ALERT_RX_HARD_RST BIT(3) +#define TCPC_ALERT_RX_STATUS BIT(2) +#define TCPC_ALERT_POWER_STATUS BIT(1) +#define TCPC_ALERT_CC_STATUS BIT(0) + +#define TCPC_TCPC_CTRL 0x19 +#define TCPC_TCPC_CTRL_BIST_MODE BIT(1) +#define TCPC_TCPC_CTRL_ORIENTATION BIT(0) + +#define TCPC_ROLE_CTRL 0x1a +#define TCPC_ROLE_CTRL_DRP BIT(6) +#define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4 +#define TCPC_ROLE_CTRL_RP_VAL_MASK 0x3 +#define TCPC_ROLE_CTRL_RP_VAL_DEF 0x0 +#define TCPC_ROLE_CTRL_RP_VAL_1_5 0x1 +#define TCPC_ROLE_CTRL_RP_VAL_3_0 0x2 +#define TCPC_ROLE_CTRL_CC2_SHIFT 2 +#define TCPC_ROLE_CTRL_CC2_MASK 0x3 +#define TCPC_ROLE_CTRL_CC1_SHIFT 0 +#define TCPC_ROLE_CTRL_CC1_MASK 0x3 +#define TCPC_ROLE_CTRL_CC_RA 0x0 +#define TCPC_ROLE_CTRL_CC_RP 0x1 +#define TCPC_ROLE_CTRL_CC_RD 0x2 +#define TCPC_ROLE_CTRL_CC_OPEN 0x3 + +#define TCPC_POWER_CTRL 0x1c +#define TCPC_POWER_CTRL_EN_VCONN BIT(0) +#define TCPC_POWER_CTRL_VCONN_POWER BIT(1) +#define TCPC_POWER_CTRL_FORCE_DISCH BIT(2) +#define TCPC_POWER_CTRL_EN_BLEED_CH BIT(3) +#define TCPC_POWER_CTRL_AUTO_DISCH_DISCO BIT(4) +#define TCPC_POWER_CTRL_DIS_V_ALARMS BIT(5) +#define TCPC_POWER_CTRL_VBUS_V_MONITOR BIT(6) + +#define TCPC_CC_STATUS 0x1d +#define TCPC_CC_STATUS_LOOK4CONN BIT(5) +#define TCPC_CC_STATUS_TERM BIT(4) +#define TCPC_CC_STATUS_CC2_SHIFT 2 +#define TCPC_CC_STATUS_CC2_MASK 0x3 +#define TCPC_CC_STATUS_CC1_SHIFT 0 +#define TCPC_CC_STATUS_CC1_MASK 0x3 + +#define TCPC_POWER_STATUS 0x1e +#define TCPC_POWER_STATUS_UNINIT BIT(6) +#define TCPC_POWER_STATUS_VBUS_DET BIT(3) +#define TCPC_POWER_STATUS_VBUS_PRES BIT(2) +#define TCPC_POWER_STATUS_SINKING_VBUS BIT(0) + +#define TCPC_FAULT_STATUS 0x1f + +#define TCPC_COMMAND 0x23 +#define TCPC_CMD_WAKE_I2C 0x11 +#define TCPC_CMD_DISABLE_VBUS_DETECT 0x22 +#define TCPC_CMD_ENABLE_VBUS_DETECT 0x33 +#define TCPC_CMD_DISABLE_SINK_VBUS 0x44 +#define TCPC_CMD_SINK_VBUS 0x55 +#define TCPC_CMD_DISABLE_SRC_VBUS 0x66 +#define TCPC_CMD_SRC_VBUS_DEFAULT 0x77 +#define TCPC_CMD_SRC_VBUS_HIGH 0x88 +#define TCPC_CMD_LOOK4CONNECTION 0x99 +#define TCPC_CMD_RXONEMORE 0xAA +#define TCPC_CMD_I2C_IDLE 0xFF + +#define TCPC_DEV_CAP_1 0x24 +#define TCPC_DEV_CAP_2 0x26 +#define TCPC_STD_INPUT_CAP 0x28 +#define TCPC_STD_OUTPUT_CAP 0x29 + +#define TCPC_MSG_HDR_INFO 0x2e +#define TCPC_MSG_HDR_INFO_DATA_ROLE BIT(3) +#define TCPC_MSG_HDR_INFO_PWR_ROLE BIT(0) +#define TCPC_MSG_HDR_INFO_REV_SHIFT 1 +#define TCPC_MSG_HDR_INFO_REV_MASK 0x3 + +#define TCPC_RX_DETECT 0x2f +#define TCPC_RX_DETECT_HARD_RESET BIT(5) +#define TCPC_RX_DETECT_SOP BIT(0) + +#define TCPC_RX_BYTE_CNT 0x30 +#define TCPC_RX_BUF_FRAME_TYPE 0x31 +#define TCPC_RX_HDR 0x32 +#define TCPC_RX_DATA 0x34 /* through 0x4f */ + +#define TCPC_TRANSMIT 0x50 +#define TCPC_TRANSMIT_RETRY_SHIFT 4 +#define TCPC_TRANSMIT_RETRY_MASK 0x3 +#define TCPC_TRANSMIT_TYPE_SHIFT 0 +#define TCPC_TRANSMIT_TYPE_MASK 0x7 + +#define TCPC_TX_BYTE_CNT 0x51 +#define TCPC_TX_HDR 0x52 +#define TCPC_TX_DATA 0x54 /* through 0x6f */ + +#define TCPC_VBUS_VOLTAGE 0x70 +#define TCPC_VBUS_VOL_MASK 0x3ff +#define TCPC_VBUS_VOL_SCALE_FACTOR_MASK 0xc00 +#define TCPC_VBUS_VOL_SCALE_FACTOR_SHIFT 10 +#define TCPC_VBUS_VOL_MV_UNIT 25 + +#define TCPC_VBUS_SINK_DISCONNECT_THRESH 0x72 +#define TCPC_VBUS_STOP_DISCHARGE_THRESH 0x74 +#define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76 +#define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78 + +enum typec_role { + TYPEC_SINK, + TYPEC_SOURCE, + TYPEC_ROLE_UNKNOWN, +}; + +enum typec_data_role { + TYPEC_DEVICE, + TYPEC_HOST, +}; + +enum typec_cc_polarity { + TYPEC_POLARITY_CC1, + TYPEC_POLARITY_CC2, +}; + +enum typec_cc_state { + TYPEC_STATE_OPEN, + TYPEC_STATE_SRC_BOTH_RA, + TYPEC_STATE_SRC_RD_RA, + TYPEC_STATE_SRC_RD, + TYPEC_STATE_SRC_RESERVED, + TYPEC_STATE_SNK_DEFAULT, + TYPEC_STATE_SNK_POWER15, + TYPEC_STATE_SNK_POWER30, +}; + + +/* USB PD Messages */ +enum pd_ctrl_msg_type { + /* 0 Reserved */ + PD_CTRL_GOOD_CRC = 1, + PD_CTRL_GOTO_MIN = 2, + PD_CTRL_ACCEPT = 3, + PD_CTRL_REJECT = 4, + PD_CTRL_PING = 5, + PD_CTRL_PS_RDY = 6, + PD_CTRL_GET_SOURCE_CAP = 7, + PD_CTRL_GET_SINK_CAP = 8, + PD_CTRL_DR_SWAP = 9, + PD_CTRL_PR_SWAP = 10, + PD_CTRL_VCONN_SWAP = 11, + PD_CTRL_WAIT = 12, + PD_CTRL_SOFT_RESET = 13, + /* 14-15 Reserved */ +}; + +enum pd_data_msg_type { + /* 0 Reserved */ + PD_DATA_SOURCE_CAP = 1, + PD_DATA_REQUEST = 2, + PD_DATA_BIST = 3, + PD_DATA_SINK_CAP = 4, + /* 5-14 Reserved */ + PD_DATA_VENDOR_DEF = 15, +}; + +enum tcpc_transmit_type { + TCPC_TX_SOP = 0, + TCPC_TX_SOP_PRIME = 1, + TCPC_TX_SOP_PRIME_PRIME = 2, + TCPC_TX_SOP_DEBUG_PRIME = 3, + TCPC_TX_SOP_DEBUG_PRIME_PRIME = 4, + TCPC_TX_HARD_RESET = 5, + TCPC_TX_CABLE_RESET = 6, + TCPC_TX_BIST_MODE_2 = 7 +}; + +enum pd_sink_state{ + UNATTACH = 0, + ATTACHED, + WAIT_SOURCE_CAP, + WAIT_SOURCE_ACCEPT, + WAIT_SOURCE_READY, + SINK_READY, +}; + + +#define PD_REV10 0x0 +#define PD_REV20 0x1 + +#define PD_HEADER_CNT_SHIFT 12 +#define PD_HEADER_CNT_MASK 0x7 +#define PD_HEADER_ID_SHIFT 9 +#define PD_HEADER_ID_MASK 0x7 +#define PD_HEADER_PWR_ROLE BIT(8) +#define PD_HEADER_REV_SHIFT 6 +#define PD_HEADER_REV_MASK 0x3 +#define PD_HEADER_DATA_ROLE BIT(5) +#define PD_HEADER_TYPE_SHIFT 0 +#define PD_HEADER_TYPE_MASK 0xf + +#define PD_HEADER(type, pwr, data, id, cnt) \ + ((((type) & PD_HEADER_TYPE_MASK) << PD_HEADER_TYPE_SHIFT) | \ + ((pwr) == TYPEC_SOURCE ? PD_HEADER_PWR_ROLE : 0) | \ + ((data) == TYPEC_HOST ? PD_HEADER_DATA_ROLE : 0) | \ + (PD_REV20 << PD_HEADER_REV_SHIFT) | \ + (((id) & PD_HEADER_ID_MASK) << PD_HEADER_ID_SHIFT) | \ + (((cnt) & PD_HEADER_CNT_MASK) << PD_HEADER_CNT_SHIFT)) + + +static inline unsigned int pd_header_cnt(uint16_t header) +{ + return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK; +} + +static inline unsigned int pd_header_cnt_le(__le16 header) +{ + return pd_header_cnt(le16_to_cpu(header)); +} + +static inline unsigned int pd_header_type(uint16_t header) +{ + return (header >> PD_HEADER_TYPE_SHIFT) & PD_HEADER_TYPE_MASK; +} + +static inline unsigned int pd_header_type_le(__le16 header) +{ + return pd_header_type(le16_to_cpu(header)); +} + +#define PD_MAX_PAYLOAD 7 + +struct pd_message { + uint8_t frametype; + uint16_t header; + uint32_t payload[PD_MAX_PAYLOAD]; +} __packed; + +enum pd_pdo_type { + PDO_TYPE_FIXED = 0, + PDO_TYPE_BATT = 1, + PDO_TYPE_VAR = 2, +}; + + +#define PDO_TYPE_SHIFT 30 +#define PDO_TYPE_MASK 0x3 + +#define PDO_TYPE(t) ((t) << PDO_TYPE_SHIFT) + +#define PDO_VOLT_MASK 0x3ff +#define PDO_CURR_MASK 0x3ff +#define PDO_PWR_MASK 0x3ff + +#define PDO_FIXED_DUAL_ROLE BIT(29) /* Power role swap supported */ +#define PDO_FIXED_SUSPEND BIT(28) /* USB Suspend supported (Source) */ +#define PDO_FIXED_HIGHER_CAP BIT(28) /* Requires more than vSafe5V (Sink) */ +#define PDO_FIXED_EXTPOWER BIT(27) /* Externally powered */ +#define PDO_FIXED_USB_COMM BIT(26) /* USB communications capable */ +#define PDO_FIXED_DATA_SWAP BIT(25) /* Data role swap supported */ +#define PDO_FIXED_VOLT_SHIFT 10 /* 50mV units */ +#define PDO_FIXED_CURR_SHIFT 0 /* 10mA units */ + +#define PDO_FIXED_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_FIXED_VOLT_SHIFT) +#define PDO_FIXED_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_FIXED_CURR_SHIFT) + +#define PDO_FIXED(mv, ma, flags) \ + (PDO_TYPE(PDO_TYPE_FIXED) | (flags) | \ + PDO_FIXED_VOLT(mv) | PDO_FIXED_CURR(ma)) + +#define PDO_BATT_MAX_VOLT_SHIFT 20 /* 50mV units */ +#define PDO_BATT_MIN_VOLT_SHIFT 10 /* 50mV units */ +#define PDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */ + +#define PDO_BATT_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MIN_VOLT_SHIFT) +#define PDO_BATT_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MAX_VOLT_SHIFT) +#define PDO_BATT_MAX_POWER(mw) ((((mw) / 250) & PDO_PWR_MASK) << PDO_BATT_MAX_PWR_SHIFT) + +#define PDO_BATT(min_mv, max_mv, max_mw) \ + (PDO_TYPE(PDO_TYPE_BATT) | PDO_BATT_MIN_VOLT(min_mv) | \ + PDO_BATT_MAX_VOLT(max_mv) | PDO_BATT_MAX_POWER(max_mw)) + +#define PDO_VAR_MAX_VOLT_SHIFT 20 /* 50mV units */ +#define PDO_VAR_MIN_VOLT_SHIFT 10 /* 50mV units */ +#define PDO_VAR_MAX_CURR_SHIFT 0 /* 10mA units */ + +#define PDO_VAR_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MIN_VOLT_SHIFT) +#define PDO_VAR_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MAX_VOLT_SHIFT) +#define PDO_VAR_MAX_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_VAR_MAX_CURR_SHIFT) + +#define PDO_VAR(min_mv, max_mv, max_ma) \ + (PDO_TYPE(PDO_TYPE_VAR) | PDO_VAR_MIN_VOLT(min_mv) | \ + PDO_VAR_MAX_VOLT(max_mv) | PDO_VAR_MAX_CURR(max_ma)) + +static inline enum pd_pdo_type pdo_type(uint32_t pdo) +{ + return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK; +} + +static inline unsigned int pdo_fixed_voltage(uint32_t pdo) +{ + return ((pdo >> PDO_FIXED_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; +} + +static inline unsigned int pdo_min_voltage(uint32_t pdo) +{ + return ((pdo >> PDO_VAR_MIN_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; +} + +static inline unsigned int pdo_max_voltage(uint32_t pdo) +{ + return ((pdo >> PDO_VAR_MAX_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; +} + +static inline unsigned int pdo_max_current(uint32_t pdo) +{ + return ((pdo >> PDO_VAR_MAX_CURR_SHIFT) & PDO_CURR_MASK) * 10; +} + +static inline unsigned int pdo_max_power(uint32_t pdo) +{ + return ((pdo >> PDO_BATT_MAX_PWR_SHIFT) & PDO_PWR_MASK) * 250; +} + +/* RDO: Request Data Object */ +#define RDO_OBJ_POS_SHIFT 28 +#define RDO_OBJ_POS_MASK 0x7 +#define RDO_GIVE_BACK BIT(27) /* Supports reduced operating current */ +#define RDO_CAP_MISMATCH BIT(26) /* Not satisfied by source caps */ +#define RDO_USB_COMM BIT(25) /* USB communications capable */ +#define RDO_NO_SUSPEND BIT(24) /* USB Suspend not supported */ + +#define RDO_PWR_MASK 0x3ff +#define RDO_CURR_MASK 0x3ff + +#define RDO_FIXED_OP_CURR_SHIFT 10 +#define RDO_FIXED_MAX_CURR_SHIFT 0 + +#define RDO_OBJ(idx) (((idx) & RDO_OBJ_POS_MASK) << RDO_OBJ_POS_SHIFT) + +#define PDO_FIXED_OP_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_OP_CURR_SHIFT) +#define PDO_FIXED_MAX_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_MAX_CURR_SHIFT) + +#define RDO_FIXED(idx, op_ma, max_ma, flags) \ + (RDO_OBJ(idx) | (flags) | \ + PDO_FIXED_OP_CURR(op_ma) | PDO_FIXED_MAX_CURR(max_ma)) + +#define RDO_BATT_OP_PWR_SHIFT 10 /* 250mW units */ +#define RDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */ + +#define RDO_BATT_OP_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_OP_PWR_SHIFT) +#define RDO_BATT_MAX_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_MAX_PWR_SHIFT) + +#define RDO_BATT(idx, op_mw, max_mw, flags) \ + (RDO_OBJ(idx) | (flags) | \ + RDO_BATT_OP_PWR(op_mw) | RDO_BATT_MAX_PWR(max_mw)) + +static inline unsigned int rdo_index(u32 rdo) +{ + return (rdo >> RDO_OBJ_POS_SHIFT) & RDO_OBJ_POS_MASK; +} + +static inline unsigned int rdo_op_current(u32 rdo) +{ + return ((rdo >> RDO_FIXED_OP_CURR_SHIFT) & RDO_CURR_MASK) * 10; +} + +static inline unsigned int rdo_max_current(u32 rdo) +{ + return ((rdo >> RDO_FIXED_MAX_CURR_SHIFT) & + RDO_CURR_MASK) * 10; +} + +static inline unsigned int rdo_op_power(u32 rdo) +{ + return ((rdo >> RDO_BATT_OP_PWR_SHIFT) & RDO_PWR_MASK) * 250; +} + +static inline unsigned int rdo_max_power(u32 rdo) +{ + return ((rdo >> RDO_BATT_MAX_PWR_SHIFT) & RDO_PWR_MASK) * 250; +} + +#define TCPC_LOG_BUFFER_SIZE 1024 + +struct tcpc_port; + +typedef void (*ss_mux_sel)(enum typec_cc_polarity pol); +typedef int (*ext_pd_switch_setup)(struct tcpc_port *port_p); + +enum tcpc_port_type { + TYPEC_PORT_DFP, + TYPEC_PORT_UFP, + TYPEC_PORT_DRP, +}; + +struct tcpc_port_config { + uint8_t i2c_bus; + uint8_t addr; + enum tcpc_port_type port_type; + uint32_t max_snk_mv; + uint32_t max_snk_ma; + uint32_t max_snk_mw; + uint32_t op_snk_mv; + bool disable_pd; + ext_pd_switch_setup switch_setup_func; +}; + +struct tcpc_port { + struct tcpc_port_config cfg; + struct udevice *i2c_dev; + ss_mux_sel ss_sel_func; + enum pd_sink_state pd_state; + uint32_t tx_msg_id; + uint32_t log_size; + char logbuffer[TCPC_LOG_BUFFER_SIZE]; + char *log_p; + char *log_print; +}; + +int tcpc_set_cc_to_source(struct tcpc_port *port); +int tcpc_set_cc_to_sink(struct tcpc_port *port); +int tcpc_set_plug_orientation(struct tcpc_port *port, enum typec_cc_polarity polarity); +int tcpc_get_cc_status(struct tcpc_port *port, enum typec_cc_polarity *polarity, enum typec_cc_state *state); +int tcpc_clear_alert(struct tcpc_port *port, uint16_t clear_mask); +int tcpc_send_command(struct tcpc_port *port, uint8_t command); +int tcpc_polling_reg(struct tcpc_port *port, uint8_t reg, + uint8_t reg_width, uint16_t mask, uint16_t value, ulong timeout_ms); +int tcpc_setup_dfp_mode(struct tcpc_port *port); +int tcpc_setup_ufp_mode(struct tcpc_port *port); +int tcpc_disable_src_vbus(struct tcpc_port *port); +int tcpc_init(struct tcpc_port *port, struct tcpc_port_config config, ss_mux_sel ss_sel_func); +bool tcpc_pd_sink_check_charging(struct tcpc_port *port); +void tcpc_print_log(struct tcpc_port *port); + +#ifdef CONFIG_SPL_BUILD +int tcpc_setup_ufp_mode(struct tcpc_port *port) +{ + return 0; +} +int tcpc_setup_dfp_mode(struct tcpc_port *port) +{ + return 0; +} + +int tcpc_disable_src_vbus(struct tcpc_port *port) +{ + return 0; +} +#endif +#endif /* __TCPCI_H */