diff mbox series

[1/8] tcpc: Add driver for USB typec port controller (TCPC)

Message ID 20201012062354.3743-2-peng.fan@nxp.com
State Changes Requested
Delegated to: Stefano Babic
Headers show
Series i.MX8MM: add host/gadget support | expand

Commit Message

Peng Fan Oct. 12, 2020, 6:23 a.m. UTC
From: Ye Li <ye.li@nxp.com>

The functionalities in this driver include:
1. USB power delivery support at dead battery
2. Support configure to UFP or DFP mode
3. Support callback to setup external PD switch. When PD process is enabled,
   we call this function only when SINK_VBUS is enabled to avoid system power
   shut down.

Signed-off-by: Li Jun <jun.li@nxp.com>
Signed-off-by: Ye Li <ye.li@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 board/freescale/common/Kconfig  |    6 +
 board/freescale/common/Makefile |    4 +
 board/freescale/common/tcpc.c   | 1018 +++++++++++++++++++++++++++++++
 board/freescale/common/tcpc.h   |  469 ++++++++++++++
 4 files changed, 1497 insertions(+)
 create mode 100644 board/freescale/common/tcpc.c
 create mode 100644 board/freescale/common/tcpc.h

Comments

Marek Vasut Oct. 12, 2020, 8:50 a.m. UTC | #1
On 10/12/20 8:23 AM, Peng Fan wrote:
[...]
>  board/freescale/common/Kconfig  |    6 +
>  board/freescale/common/Makefile |    4 +
>  board/freescale/common/tcpc.c   | 1018 +++++++++++++++++++++++++++++++
>  board/freescale/common/tcpc.h   |  469 ++++++++++++++
>  4 files changed, 1497 insertions(+)
>  create mode 100644 board/freescale/common/tcpc.c
>  create mode 100644 board/freescale/common/tcpc.h

Shouldn't driver be in drivers/ ?

[...]

> 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 @@

[...]

> +#ifdef DEBUG
> +#define tcpc_debug_log(port, fmt, args...) tcpc_log(port, fmt, ##args)
> +#else
> +#define tcpc_debug_log(port, fmt, args...)
> +#endif

Use dev_dbg() or pr_debug() or just debug() ?

[...]
Peng Fan Oct. 12, 2020, 9:43 a.m. UTC | #2
> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port controller (TCPC)
> 
> On 10/12/20 8:23 AM, Peng Fan wrote:
> [...]
> >  board/freescale/common/Kconfig  |    6 +
> >  board/freescale/common/Makefile |    4 +
> >  board/freescale/common/tcpc.c   | 1018
> +++++++++++++++++++++++++++++++
> >  board/freescale/common/tcpc.h   |  469 ++++++++++++++
> >  4 files changed, 1497 insertions(+)
> >  create mode 100644 board/freescale/common/tcpc.c  create mode
> 100644
> > board/freescale/common/tcpc.h
> 
> Shouldn't driver be in drivers/ ?

You mean drivers/usb/typec as kernel? There is no common framework now.

Regards,
Peng.

> 
> [...]
> 
> > 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 @@
> 
> [...]
> 
> > +#ifdef DEBUG
> > +#define tcpc_debug_log(port, fmt, args...) tcpc_log(port, fmt,
> > +##args) #else #define tcpc_debug_log(port, fmt, args...) #endif
> 
> Use dev_dbg() or pr_debug() or just debug() ?
> 
> [...]
Marek Vasut Oct. 12, 2020, 10:21 a.m. UTC | #3
On 10/12/20 11:43 AM, Peng Fan wrote:
>> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port controller (TCPC)
>>
>> On 10/12/20 8:23 AM, Peng Fan wrote:
>> [...]
>>>  board/freescale/common/Kconfig  |    6 +
>>>  board/freescale/common/Makefile |    4 +
>>>  board/freescale/common/tcpc.c   | 1018
>> +++++++++++++++++++++++++++++++
>>>  board/freescale/common/tcpc.h   |  469 ++++++++++++++
>>>  4 files changed, 1497 insertions(+)
>>>  create mode 100644 board/freescale/common/tcpc.c  create mode
>> 100644
>>> board/freescale/common/tcpc.h
>>
>> Shouldn't driver be in drivers/ ?
> 
> You mean drivers/usb/typec as kernel?

Yes, looks much better than putting drivers into board/ , right ?

> There is no common framework now.

What can we do about that ?
Peng Fan Oct. 13, 2020, 9:39 a.m. UTC | #4
> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port controller (TCPC)
> 
> On 10/12/20 11:43 AM, Peng Fan wrote:
> >> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port
> >> controller (TCPC)
> >>
> >> On 10/12/20 8:23 AM, Peng Fan wrote:
> >> [...]
> >>>  board/freescale/common/Kconfig  |    6 +
> >>>  board/freescale/common/Makefile |    4 +
> >>>  board/freescale/common/tcpc.c   | 1018
> >> +++++++++++++++++++++++++++++++
> >>>  board/freescale/common/tcpc.h   |  469 ++++++++++++++
> >>>  4 files changed, 1497 insertions(+)  create mode 100644
> >>> board/freescale/common/tcpc.c  create mode
> >> 100644
> >>> board/freescale/common/tcpc.h
> >>
> >> Shouldn't driver be in drivers/ ?
> >
> > You mean drivers/usb/typec as kernel?
> 
> Yes, looks much better than putting drivers into board/ , right ?
> 
> > There is no common framework now.
> 
> What can we do about that ?

I have no bandwidth to study the new typec area currently. I'll leave
this to Jun and Ye.

BTW: are you ok to take the ehci-mx6 patches?
https://patchwork.ozlabs.org/project/uboot/list/?series=202112

Thanks,
Peng.
Marek Vasut Oct. 13, 2020, 9:45 a.m. UTC | #5
On 10/13/20 11:39 AM, Peng Fan wrote:
>> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port controller (TCPC)
>>
>> On 10/12/20 11:43 AM, Peng Fan wrote:
>>>> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port
>>>> controller (TCPC)
>>>>
>>>> On 10/12/20 8:23 AM, Peng Fan wrote:
>>>> [...]
>>>>>  board/freescale/common/Kconfig  |    6 +
>>>>>  board/freescale/common/Makefile |    4 +
>>>>>  board/freescale/common/tcpc.c   | 1018
>>>> +++++++++++++++++++++++++++++++
>>>>>  board/freescale/common/tcpc.h   |  469 ++++++++++++++
>>>>>  4 files changed, 1497 insertions(+)  create mode 100644
>>>>> board/freescale/common/tcpc.c  create mode
>>>> 100644
>>>>> board/freescale/common/tcpc.h
>>>>
>>>> Shouldn't driver be in drivers/ ?
>>>
>>> You mean drivers/usb/typec as kernel?
>>
>> Yes, looks much better than putting drivers into board/ , right ?
>>
>>> There is no common framework now.
>>
>> What can we do about that ?
> 
> I have no bandwidth to study the new typec area currently. I'll leave
> this to Jun and Ye.
> 
> BTW: are you ok to take the ehci-mx6 patches?
> https://patchwork.ozlabs.org/project/uboot/list/?series=202112

Sure, I am waiting for the cleanup patchset:

https://patchwork.ozlabs.org/project/uboot/patch/20200916125705.4341-14-peng.fan@nxp.com/
Peng Fan Oct. 13, 2020, 10:05 a.m. UTC | #6
> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port controller (TCPC)
> 
> On 10/13/20 11:39 AM, Peng Fan wrote:
> >> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port
> >> controller (TCPC)
> >>
> >> On 10/12/20 11:43 AM, Peng Fan wrote:
> >>>> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port
> >>>> controller (TCPC)
> >>>>
> >>>> On 10/12/20 8:23 AM, Peng Fan wrote:
> >>>> [...]
> >>>>>  board/freescale/common/Kconfig  |    6 +
> >>>>>  board/freescale/common/Makefile |    4 +
> >>>>>  board/freescale/common/tcpc.c   | 1018
> >>>> +++++++++++++++++++++++++++++++
> >>>>>  board/freescale/common/tcpc.h   |  469 ++++++++++++++
> >>>>>  4 files changed, 1497 insertions(+)  create mode 100644
> >>>>> board/freescale/common/tcpc.c  create mode
> >>>> 100644
> >>>>> board/freescale/common/tcpc.h
> >>>>
> >>>> Shouldn't driver be in drivers/ ?
> >>>
> >>> You mean drivers/usb/typec as kernel?
> >>
> >> Yes, looks much better than putting drivers into board/ , right ?
> >>
> >>> There is no common framework now.
> >>
> >> What can we do about that ?
> >
> > I have no bandwidth to study the new typec area currently. I'll leave
> > this to Jun and Ye.
> >
> > BTW: are you ok to take the ehci-mx6 patches?
> > https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpatc
> >
> hwork.ozlabs.org%2Fproject%2Fuboot%2Flist%2F%3Fseries%3D202112&amp
> ;dat
> >
> a=02%7C01%7Cpeng.fan%40nxp.com%7C9b1ed52ddbf848fff1e408d86f5ddd
> 1e%7C68
> >
> 6ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C637381796212195230&a
> mp;sdata=
> >
> BHlWLIhiVEEcRfKVXAQP9ldcyveozTCjpxo%2FrBMLD%2F4%3D&amp;reserved
> =0
> 
> Sure, I am waiting for the cleanup patchset:

With alias set in dtsi, we could remove bind function here to calculate req_seq.
So I would send the dtsi patch to Linux/uboot community, and remove
the find function.

Do you agree?

Thanks,
Peng.

> 
> https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpatch
> work.ozlabs.org%2Fproject%2Fuboot%2Fpatch%2F20200916125705.4341-14
> -peng.fan%40nxp.com%2F&amp;data=02%7C01%7Cpeng.fan%40nxp.com%7
> C9b1ed52ddbf848fff1e408d86f5ddd1e%7C686ea1d3bc2b4c6fa92cd99c5c30
> 1635%7C0%7C0%7C637381796212195230&amp;sdata=Ofqu3wsb2CmStzt0V
> dMbotuTeIJyYeQIaCcb%2Bx9sY5Y%3D&amp;reserved=0
Marek Vasut Oct. 13, 2020, 10:14 a.m. UTC | #7
On 10/13/20 12:05 PM, Peng Fan wrote:
>> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port controller (TCPC)
>>
>> On 10/13/20 11:39 AM, Peng Fan wrote:
>>>> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port
>>>> controller (TCPC)
>>>>
>>>> On 10/12/20 11:43 AM, Peng Fan wrote:
>>>>>> Subject: Re: [PATCH 1/8] tcpc: Add driver for USB typec port
>>>>>> controller (TCPC)
>>>>>>
>>>>>> On 10/12/20 8:23 AM, Peng Fan wrote:
>>>>>> [...]
>>>>>>>  board/freescale/common/Kconfig  |    6 +
>>>>>>>  board/freescale/common/Makefile |    4 +
>>>>>>>  board/freescale/common/tcpc.c   | 1018
>>>>>> +++++++++++++++++++++++++++++++
>>>>>>>  board/freescale/common/tcpc.h   |  469 ++++++++++++++
>>>>>>>  4 files changed, 1497 insertions(+)  create mode 100644
>>>>>>> board/freescale/common/tcpc.c  create mode
>>>>>> 100644
>>>>>>> board/freescale/common/tcpc.h
>>>>>>
>>>>>> Shouldn't driver be in drivers/ ?
>>>>>
>>>>> You mean drivers/usb/typec as kernel?
>>>>
>>>> Yes, looks much better than putting drivers into board/ , right ?
>>>>
>>>>> There is no common framework now.
>>>>
>>>> What can we do about that ?
>>>
>>> I have no bandwidth to study the new typec area currently. I'll leave
>>> this to Jun and Ye.
>>>
>>> BTW: are you ok to take the ehci-mx6 patches?
>>> https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpatc
>>>
>> hwork.ozlabs.org%2Fproject%2Fuboot%2Flist%2F%3Fseries%3D202112&amp
>> ;dat
>>>
>> a=02%7C01%7Cpeng.fan%40nxp.com%7C9b1ed52ddbf848fff1e408d86f5ddd
>> 1e%7C68
>>>
>> 6ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C637381796212195230&a
>> mp;sdata=
>>>
>> BHlWLIhiVEEcRfKVXAQP9ldcyveozTCjpxo%2FrBMLD%2F4%3D&amp;reserved
>> =0
>>
>> Sure, I am waiting for the cleanup patchset:
> 
> With alias set in dtsi, we could remove bind function here to calculate req_seq.
> So I would send the dtsi patch to Linux/uboot community, and remove
> the find function.
> 
> Do you agree?

I'm not quite sure what this means, so please send a patch and lets see.
diff mbox series

Patch

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