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 <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;
+
+	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 <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 */
