imx8mm-somdevices: Add somdevices board necessary files based on freescale/common.
authorJosep Orga <jorga@somdevices.com>
Thu, 2 Sep 2021 07:14:41 +0000 (09:14 +0200)
committerJosep Orga <jorga@somdevices.com>
Thu, 2 Sep 2021 07:14:41 +0000 (09:14 +0200)
Signed-off-by: Josep Orga <jorga@somdevices.com>
board/somdevices/common/Kconfig [new file with mode: 0644]
board/somdevices/common/Makefile [new file with mode: 0644]
board/somdevices/common/mmc.c [new file with mode: 0644]
board/somdevices/common/tcpc.c [new file with mode: 0644]
board/somdevices/common/tcpc.h [new file with mode: 0644]

diff --git a/board/somdevices/common/Kconfig b/board/somdevices/common/Kconfig
new file mode 100644 (file)
index 0000000..9ed58cd
--- /dev/null
@@ -0,0 +1,63 @@
+config CHAIN_OF_TRUST
+       depends on !FIT_SIGNATURE && NXP_ESBC
+       imply CMD_BLOB
+       imply CMD_HASH if ARM
+       select FSL_CAAM
+       select SPL_BOARD_INIT if (ARM && SPL)
+       select SHA_HW_ACCEL
+       select SHA_PROG_HW_ACCEL
+       select ENV_IS_NOWHERE
+       select CMD_EXT4 if ARM
+       select CMD_EXT4_WRITE if ARM
+       bool
+       default y
+
+config CMD_ESBC_VALIDATE
+       bool "Enable the 'esbc_validate' and 'esbc_halt' commands"
+       default y if CHAIN_OF_TRUST
+       help
+         This option enables two commands used for secure booting:
+
+           esbc_validate - validate signature using RSA verification
+           esbc_halt - put the core in spin loop (Secure Boot Only)
+
+config VID
+       depends on DM_I2C
+       bool "Enable Freescale VID"
+       help
+        This option enables setting core voltage based on individual
+        values saved in SoC fuses.
+
+config VOL_MONITOR_LTC3882_READ
+       depends on VID
+       bool "Enable the LTC3882 voltage monitor read"
+       help
+        This option enables LTC3882 voltage monitor read
+        functionality. It is used by the common VID driver.
+
+config VOL_MONITOR_LTC3882_SET
+       depends on VID
+       bool "Enable the LTC3882 voltage monitor set"
+       help
+        This option enables LTC3882 voltage monitor set
+        functionality. It is used by the common VID driver.
+
+config VOL_MONITOR_ISL68233_READ
+       depends on VID
+       bool "Enable the ISL68233 voltage monitor read"
+       help
+        This option enables ISL68233 voltage monitor read
+        functionality. It is used by the common VID driver.
+
+config VOL_MONITOR_ISL68233_SET
+       depends on VID
+       bool "Enable the ISL68233 voltage monitor set"
+       help
+        This option enables ISL68233 voltage monitor set
+        functionality. It is used by the 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/somdevices/common/Makefile b/board/somdevices/common/Makefile
new file mode 100644 (file)
index 0000000..abc3a66
--- /dev/null
@@ -0,0 +1,93 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+
+MINIMAL=
+
+ifdef CONFIG_SPL_BUILD
+ifdef CONFIG_SPL_INIT_MINIMAL
+MINIMAL=y
+endif
+endif
+
+ifdef MINIMAL
+# necessary to create built-in.o
+obj- := __dummy__.o
+else
+obj-$(CONFIG_FSL_CADMUS)       += cadmus.o
+obj-$(CONFIG_FSL_VIA)          += cds_via.o
+obj-$(CONFIG_FMAN_ENET)        += fman.o
+obj-$(CONFIG_FSL_PIXIS)        += pixis.o
+ifndef CONFIG_SPL_BUILD
+obj-$(CONFIG_FSL_NGPIXIS)      += ngpixis.o
+endif
+obj-$(CONFIG_VID)              += vid.o
+obj-$(CONFIG_FSL_QIXIS)        += qixis.o
+obj-$(CONFIG_PQ_MDS_PIB)       += pq-mds-pib.o
+ifndef CONFIG_SPL_BUILD
+obj-$(CONFIG_ID_EEPROM)        += sys_eeprom.o
+endif
+obj-$(CONFIG_FSL_SGMII_RISER)  += sgmii_riser.o
+ifndef CONFIG_RAMBOOT_PBL
+obj-$(CONFIG_FSL_FIXED_MMC_LOCATION)   += sdhc_boot.o
+endif
+
+obj-$(CONFIG_FSL_DIU_CH7301)   += diu_ch7301.o
+
+ifdef CONFIG_ARM
+obj-$(CONFIG_DEEP_SLEEP)               += arm_sleep.o
+else
+obj-$(CONFIG_DEEP_SLEEP)               += mpc85xx_sleep.o
+endif
+
+obj-$(CONFIG_FSL_DCU_SII9022A)    += dcu_sii9022a.o
+
+obj-$(CONFIG_TARGET_MPC8541CDS)        += cds_pci_ft.o
+obj-$(CONFIG_TARGET_MPC8548CDS)        += cds_pci_ft.o
+obj-$(CONFIG_TARGET_MPC8555CDS)        += cds_pci_ft.o
+
+obj-$(CONFIG_TARGET_MPC8536DS) += ics307_clk.o
+obj-$(CONFIG_TARGET_P1022DS)           += ics307_clk.o
+obj-$(CONFIG_P2020DS)          += ics307_clk.o
+obj-$(CONFIG_TARGET_P3041DS)           += ics307_clk.o
+obj-$(CONFIG_TARGET_P4080DS)           += ics307_clk.o
+obj-$(CONFIG_TARGET_P5040DS)           += ics307_clk.o
+obj-$(CONFIG_VSC_CROSSBAR)    += vsc3316_3308.o
+obj-$(CONFIG_IDT8T49N222A)     += idt8t49n222a_serdes_clk.o
+obj-$(CONFIG_ZM7300)           += zm7300.o
+obj-$(CONFIG_POWER_PFUZE100)   += pfuze.o
+obj-$(CONFIG_DM_PMIC_PFUZE100) += pfuze.o
+obj-$(CONFIG_POWER_MC34VR500)  += mc34vr500.o
+obj-$(CONFIG_MXC_EPDC)         += epdc_setup.o
+ifneq (,$(filter $(SOC), mx25 mx31 mx35 mx5 mx6 mx7 mx7ulp imx8 imx8m vf610))
+obj-y                          += mmc.o
+endif
+ifdef CONFIG_FSL_FASTBOOT
+obj-${CONFIG_ANDROID_RECOVERY} += recovery_keypad.o
+endif
+
+obj-$(CONFIG_LS102XA_STREAM_ID)        += ls102xa_stream_id.o
+
+obj-$(CONFIG_EMC2305)              += emc2305.o
+obj-$(CONFIG_QSFP_EEPROM)      += qsfp_eeprom.o
+
+# deal with common files for P-series corenet based devices
+obj-$(CONFIG_TARGET_P2041RDB)  += p_corenet/
+obj-$(CONFIG_TARGET_P3041DS)   += p_corenet/
+obj-$(CONFIG_TARGET_P4080DS)   += p_corenet/
+obj-$(CONFIG_TARGET_P5020DS)   += p_corenet/
+obj-$(CONFIG_TARGET_P5040DS)   += p_corenet/
+
+obj-$(CONFIG_LAYERSCAPE_NS_ACCESS)     += ns_access.o
+
+ifdef CONFIG_NXP_ESBC
+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/somdevices/common/mmc.c b/board/somdevices/common/mmc.c
new file mode 100644 (file)
index 0000000..ab1652d
--- /dev/null
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2018 NXP
+ */
+#include <common.h>
+#include <command.h>
+#include <asm/arch/sys_proto.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <stdbool.h>
+#include <mmc.h>
+#include <env.h>
+
+static int check_mmc_autodetect(void)
+{
+       char *autodetect_str = env_get("mmcautodetect");
+
+       if ((autodetect_str != NULL) &&
+               (strcmp(autodetect_str, "yes") == 0)) {
+               return 1;
+       }
+
+       return 0;
+}
+
+/* This should be defined for each board */
+__weak int mmc_map_to_kernel_blk(int dev_no)
+{
+       return dev_no;
+}
+
+void board_late_mmc_env_init(void)
+{
+       char cmd[32];
+       char mmcblk[32];
+       u32 dev_no = mmc_get_env_dev();
+
+       if (!check_mmc_autodetect())
+               return;
+
+       env_set_ulong("mmcdev", dev_no);
+
+       /* Set mmcblk env */
+       sprintf(mmcblk, "/dev/mmcblk%dp2 rootwait rw",
+               mmc_map_to_kernel_blk(dev_no));
+       env_set("mmcroot", mmcblk);
+
+       sprintf(cmd, "mmc dev %d", dev_no);
+       run_command(cmd, 0);
+}
diff --git a/board/somdevices/common/tcpc.c b/board/somdevices/common/tcpc.c
new file mode 100644 (file)
index 0000000..f7d6ea6
--- /dev/null
@@ -0,0 +1,1036 @@
+/*
+ * Copyright 2017,2019 NXP
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <i2c.h>
+#include <time.h>
+#include <linux/delay.h>
+#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 = 0;
+
+       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)) {
+               err = dm_i2c_read(port->i2c_dev, TCPC_POWER_CTRL, (uint8_t *)&valb, 1);
+               if (err) {
+                       tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, err);
+                       return -EIO;
+               }
+
+               valb &= ~TCPC_POWER_CTRL_AUTO_DISCH_DISCO; /* disable AutoDischargeDisconnect */
+               err = dm_i2c_write(port->i2c_dev, TCPC_POWER_CTRL, (const uint8_t *)&valb, 1);
+               if (err) {
+                       tcpc_log(port, "%s dm_i2c_write failed, err %d\n", __func__, err);
+                       return -EIO;
+               }
+
+               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;
+       }
+
+       err = dm_i2c_read(port->i2c_dev, TCPC_POWER_CTRL, (uint8_t *)&valb, 1);
+       if (err) {
+               tcpc_log(port, "%s dm_i2c_read failed, err %d\n", __func__, err);
+               return -EIO;
+       }
+
+       valb &= ~TCPC_POWER_CTRL_AUTO_DISCH_DISCO; /* disable AutoDischargeDisconnect */
+       err = dm_i2c_write(port->i2c_dev, TCPC_POWER_CTRL, (const uint8_t *)&valb, 1);
+       if (err) {
+               tcpc_log(port, "%s dm_i2c_write failed, err %d\n", __func__, err);
+               return -EIO;
+       }
+
+       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/somdevices/common/tcpc.h b/board/somdevices/common/tcpc.h
new file mode 100644 (file)
index 0000000..ff74951
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * Copyright 2017 NXP
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef __TCPCI_H
+#define __TCPCI_H
+
+#include <dm.h>
+
+#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 */