summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDino Li <dino.li@ite.com.tw>2016-04-18 15:41:27 +0800
committerchrome-bot <chrome-bot@chromium.org>2016-04-18 17:32:41 -0700
commit69668a04436700071bc2a26400f5f52f0e499020 (patch)
tree163d7ff68d473b703471e3639692041508ad0d93
parent068cd0850684ee28a5a514e5a270edce2edb3979 (diff)
downloadchrome-ec-69668a04436700071bc2a26400f5f52f0e499020.tar.gz
chip: it83xx: add USBPD module
Add USBPD module for it8320 emulation board BRANCH=none BUG=none TEST=manual plug zinger adapter, connect uart console and type commands: pd 1 dev [20|12|5] pd 1 charger pd 1 swap power and check PD states Change-Id: I9ca1822deeb4b4dce1279a09490ed4175890cf3a Signed-off-by: Leon-Lee <leon.lee@ite.com.tw> Signed-off-by: Dino Li <dino.li@ite.com.tw> Reviewed-on: https://chromium-review.googlesource.com/326230 Reviewed-by: Shawn N <shawnn@chromium.org>
-rw-r--r--chip/it83xx/gpio.c8
-rw-r--r--chip/it83xx/intc.c49
-rw-r--r--chip/it83xx/irq.c2
-rw-r--r--chip/it83xx/registers.h84
-rw-r--r--chip/it83xx/system.c6
-rw-r--r--driver/build.mk1
-rw-r--r--driver/tcpm/it83xx.c456
-rw-r--r--driver/tcpm/it83xx_pd.h112
-rw-r--r--include/config.h1
9 files changed, 713 insertions, 6 deletions
diff --git a/chip/it83xx/gpio.c b/chip/it83xx/gpio.c
index 41e18ed13c..d3e6da7c1e 100644
--- a/chip/it83xx/gpio.c
+++ b/chip/it83xx/gpio.c
@@ -414,6 +414,14 @@ void gpio_pre_init(void)
IT83XX_GPIO_GCR = 0x06;
+#ifndef CONFIG_USB_PD_TCPM_ITE83XX
+ /* To prevent cc pins leakage if we don't use pd module */
+ for (i = 0; i < USBPD_PORT_COUNT; i++) {
+ IT83XX_USBPD_CCGCR(i) = 0x1f;
+ IT83XX_USBPD_CCPSR(i) = 0x66;
+ }
+#endif
+
for (i = 0; i < GPIO_COUNT; i++, g++) {
flags = g->flags;
diff --git a/chip/it83xx/intc.c b/chip/it83xx/intc.c
index 9b51b74fc3..a230fe6efb 100644
--- a/chip/it83xx/intc.c
+++ b/chip/it83xx/intc.c
@@ -4,10 +4,42 @@
*/
#include "common.h"
+#include "intc.h"
+#include "it83xx_pd.h"
+#include "kmsc_chip.h"
#include "registers.h"
#include "task.h"
-#include "kmsc_chip.h"
-#include "intc.h"
+#include "usb_pd.h"
+
+#ifdef CONFIG_USB_PD_TCPM_ITE83XX
+static void chip_pd_irq(enum usbpd_port port)
+{
+ task_clear_pending_irq(usbpd_ctrl_regs[port].irq);
+
+ /* check status */
+ if (USBPD_IS_HARD_RESET_DETECT(port)) {
+ /* clear interrupt */
+ IT83XX_USBPD_ISR(port) = USBPD_REG_MASK_HARD_RESET_DETECT;
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ PD_EVENT_TCPC_RESET, 0);
+ } else {
+ if (USBPD_IS_RX_DONE(port)) {
+ /* mask RX done interrupt */
+ IT83XX_USBPD_IMR(port) |= USBPD_REG_MASK_MSG_RX_DONE;
+ /* clear RX done interrupt */
+ IT83XX_USBPD_ISR(port) = USBPD_REG_MASK_MSG_RX_DONE;
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ PD_EVENT_RX, 0);
+ }
+ if (USBPD_IS_TX_DONE(port)) {
+ /* clear TX done interrupt */
+ IT83XX_USBPD_ISR(port) = USBPD_REG_MASK_MSG_TX_DONE;
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ TASK_EVENT_PHY_TX_DONE, 0);
+ }
+ }
+}
+#endif
void intc_cpu_int_group_5(void)
{
@@ -74,6 +106,15 @@ void intc_cpu_int_group_12(void)
peci_interrupt();
break;
#endif
+#ifdef CONFIG_USB_PD_TCPM_ITE83XX
+ case IT83XX_IRQ_USBPD0:
+ chip_pd_irq(USBPD_PORT_A);
+ break;
+
+ case IT83XX_IRQ_USBPD1:
+ chip_pd_irq(USBPD_PORT_B);
+ break;
+#endif /* CONFIG_USB_PD_TCPM_ITE83XX */
default:
break;
}
@@ -86,7 +127,7 @@ void intc_cpu_int_group_6(void)
int intc_group_6 = intc_get_ec_int();
switch (intc_group_6) {
-
+#ifdef CONFIG_I2C
case IT83XX_IRQ_SMB_A:
i2c_interrupt(IT83XX_I2C_CH_A);
break;
@@ -110,7 +151,7 @@ void intc_cpu_int_group_6(void)
case IT83XX_IRQ_SMB_F:
i2c_interrupt(IT83XX_I2C_CH_F);
break;
-
+#endif
default:
break;
}
diff --git a/chip/it83xx/irq.c b/chip/it83xx/irq.c
index 93752f0c7c..78d0688bbd 100644
--- a/chip/it83xx/irq.c
+++ b/chip/it83xx/irq.c
@@ -40,7 +40,7 @@ static const struct {
IRQ_GROUP(17, {-1, -1, -1, -1, -1, -1, -1, -1}),
IRQ_GROUP(18, { 2, 2, 2, 2, 2, 4, 4, 7}),
IRQ_GROUP(19, { 6, 6, 12, 3, 3, 3, 3, 3}),
- IRQ_GROUP(20, {12, 12, -1, -1, -1, -1, -1, -1}),
+ IRQ_GROUP(20, {12, 12, -1, -1, -1, 12, 12, -1}),
};
int chip_enable_irq(int irq)
diff --git a/chip/it83xx/registers.h b/chip/it83xx/registers.h
index 759bdc84eb..9b3594d482 100644
--- a/chip/it83xx/registers.h
+++ b/chip/it83xx/registers.h
@@ -146,7 +146,13 @@
#define IT83XX_IRQ_EXT_TIMER7 159
#define IT83XX_IRQ_PECI 160
#define IT83XX_IRQ_SOFTWARE 161
-#define IT83XX_IRQ_COUNT 162
+#define IT83XX_IRQ_WKO162 162
+#define IT83XX_IRQ_WKO163 163
+#define IT83XX_IRQ_WKO164 164
+#define IT83XX_IRQ_USBPD0 165
+#define IT83XX_IRQ_USBPD1 166
+#define IT83XX_IRQ_WKO167 167
+#define IT83XX_IRQ_COUNT 168
/* IRQ dispatching to CPU INT vectors */
#define IT83XX_CPU_INT_IRQ_1 2
@@ -291,6 +297,12 @@
#define IT83XX_CPU_INT_IRQ_159 3
#define IT83XX_CPU_INT_IRQ_160 12
#define IT83XX_CPU_INT_IRQ_161 12
+#define IT83XX_CPU_INT_IRQ_162 12
+#define IT83XX_CPU_INT_IRQ_163 12
+#define IT83XX_CPU_INT_IRQ_164 12
+#define IT83XX_CPU_INT_IRQ_165 12
+#define IT83XX_CPU_INT_IRQ_166 12
+#define IT83XX_CPU_INT_IRQ_167 12
/* "Fake" IRQ to declare in readable fashion all WKO IRQ routed to INT#2 */
#define CPU_INT_2_ALL_GPIOS 255
@@ -622,6 +634,7 @@ enum ec_pll_ctrl {
#define IT83XX_ECPM_SCDCR1 REG8(IT83XX_ECPM_BASE+0x0d)
#define IT83XX_ECPM_SCDCR2 REG8(IT83XX_ECPM_BASE+0x0e)
#define IT83XX_ECPM_SCDCR3 REG8(IT83XX_ECPM_BASE+0x0f)
+#define IT83XX_ECPM_SCDCR4 REG8(IT83XX_ECPM_BASE+0x10)
/*
* The clock gate offsets combine the register offset from ECPM_BASE and the
@@ -1087,6 +1100,75 @@ enum i2c_channels {
IT83XX_I2C_PORT_COUNT,
};
+/* USBPD Controller */
+#define IT83XX_USBPD_BASE(port) (0x00F03700 + (0x100 * (port)))
+
+#define IT83XX_USBPD_GCR(p) REG8(IT83XX_USBPD_BASE(p)+0x0)
+#define USBPD_REG_MASK_SW_RESET_BIT (1 << 7)
+#define USBPD_REG_MASK_TYPE_C_DETECT_RESET (1 << 6)
+#define USBPD_REG_MASK_BMC_PHY (1 << 4)
+#define USBPD_REG_MASK_AUTO_SEND_SW_RESET (1 << 3)
+#define USBPD_REG_MASK_AUTO_SEND_HW_RESET (1 << 2)
+#define USBPD_REG_MASK_SNIFFER_MODE (1 << 1)
+#define USBPD_REG_MASK_GLOBAL_ENABLE (1 << 0)
+#define IT83XX_USBPD_PDMSR(p) REG8(IT83XX_USBPD_BASE(p)+0x01)
+#define USBPD_REG_MASK_SOPPP_ENABLE (1 << 7)
+#define USBPD_REG_MASK_SOPP_ENABLE (1 << 6)
+#define USBPD_REG_MASK_SOP_ENABLE (1 << 5)
+#define IT83XX_USBPD_CCGCR(p) REG8(IT83XX_USBPD_BASE(p)+0x04)
+#define USBPD_REG_MASK_DISABLE_CC (1 << 4)
+#define IT83XX_USBPD_CCCSR(p) REG8(IT83XX_USBPD_BASE(p)+0x05)
+#define IT83XX_USBPD_CCPSR(p) REG8(IT83XX_USBPD_BASE(p)+0x06)
+#define USBPD_REG_MASK_DISCONNECT_POWER_CC2 (1 << 5)
+#define USBPD_REG_MASK_DISCONNECT_POWER_CC1 (1 << 1)
+#define IT83XX_USBPD_DFPVDR(p) REG8(IT83XX_USBPD_BASE(p)+0x08)
+#define IT83XX_USBPD_UFPVDR(p) REG8(IT83XX_USBPD_BASE(p)+0x09)
+#define IT83XX_USBPD_CCADCR(p) REG8(IT83XX_USBPD_BASE(p)+0x0C)
+#define IT83XX_USBPD_ISR(p) REG8(IT83XX_USBPD_BASE(p)+0x14)
+#define USBPD_REG_MASK_TYPE_C_DETECT (1 << 7)
+#define USBPD_REG_MASK_CABLE_RESET_DETECT (1 << 6)
+#define USBPD_REG_MASK_HARD_RESET_DETECT (1 << 5)
+#define USBPD_REG_MASK_MSG_RX_DONE (1 << 4)
+#define USBPD_REG_MASK_AUTO_SOFT_RESET_TX_DONE (1 << 3)
+#define USBPD_REG_MASK_HARD_RESET_TX_DONE (1 << 2)
+#define USBPD_REG_MASK_MSG_TX_DONE (1 << 1)
+#define USBPD_REG_MASK_TIMER_TIMEOUT (1 << 0)
+#define IT83XX_USBPD_IMR(p) REG8(IT83XX_USBPD_BASE(p)+0x15)
+#define IT83XX_USBPD_MTCR(p) REG8(IT83XX_USBPD_BASE(p)+0x18)
+#define USBPD_REG_MASK_SW_RESET_TX_STAT (1 << 3)
+#define USBPD_REG_MASK_TX_BUSY_STAT (1 << 2)
+#define USBPD_REG_MASK_TX_DISCARD_STAT (1 << 2)
+#define USBPD_REG_MASK_TX_ERR_STAT (1 << 1)
+#define USBPD_REG_MASK_TX_START (1 << 0)
+#define IT83XX_USBPD_MTSR0(p) REG8(IT83XX_USBPD_BASE(p)+0x19)
+#define USBPD_REG_MASK_CABLE_ENABLE (1 << 7)
+#define USBPD_REG_MASK_SEND_HW_RESET (1 << 6)
+#define USBPD_REG_MASK_SEND_BIST_MODE_2 (1 << 5)
+#define IT83XX_USBPD_MTSR1(p) REG8(IT83XX_USBPD_BASE(p)+0x1A)
+#define IT83XX_USBPD_VDMMCSR(p) REG8(IT83XX_USBPD_BASE(p)+0x1B)
+#define IT83XX_USBPD_MRSR(p) REG8(IT83XX_USBPD_BASE(p)+0x1C)
+#define USBPD_REG_MASK_RX_MSG_VALID (1 << 0)
+#define IT83XX_USBPD_PEFSMR(p) REG8(IT83XX_USBPD_BASE(p)+0x1D)
+#define IT83XX_USBPD_PES0R(p) REG8(IT83XX_USBPD_BASE(p)+0x1E)
+#define IT83XX_USBPD_PES1R(p) REG8(IT83XX_USBPD_BASE(p)+0x1F)
+#define IT83XX_USBPD_TDO_BASE(p) (IT83XX_USBPD_BASE(p)+0x20)
+#define IT83XX_USBPD_AGTMHLR(p) REG8(IT83XX_USBPD_BASE(p)+0x3C)
+#define IT83XX_USBPD_AGTMHHR(p) REG8(IT83XX_USBPD_BASE(p)+0x3D)
+#define IT83XX_USBPD_TMHLR(p) REG8(IT83XX_USBPD_BASE(p)+0x3E)
+#define IT83XX_USBPD_TMHHR(p) REG8(IT83XX_USBPD_BASE(p)+0x3F)
+#define IT83XX_USBPD_RDO_BASE(p) (IT83XX_USBPD_BASE(p)+0x40)
+#define IT83XX_USBPD_RMH_BASE(p) (IT83XX_USBPD_BASE(p)+0x5E)
+#define IT83XX_USBPD_RMHLR(p) REG8(IT83XX_USBPD_BASE(p)+0x5E)
+#define IT83XX_USBPD_RMHHR(p) REG8(IT83XX_USBPD_BASE(p)+0x5F)
+#define IT83XX_USBPD_BMCSR(p) REG8(IT83XX_USBPD_BASE(p)+0x64)
+#define IT83XX_USBPD_PDMHSR(p) REG8(IT83XX_USBPD_BASE(p)+0x65)
+
+enum usbpd_port {
+ USBPD_PORT_A,
+ USBPD_PORT_B,
+ USBPD_PORT_COUNT,
+};
+
/* --- MISC (not implemented yet) --- */
#define IT83XX_PS2_BASE 0x00F01700
diff --git a/chip/it83xx/system.c b/chip/it83xx/system.c
index 94ef635f91..cc98fb4fd5 100644
--- a/chip/it83xx/system.c
+++ b/chip/it83xx/system.c
@@ -276,9 +276,11 @@ static int command_rw_ec_reg(int argc, char **argv)
{
volatile uint8_t *addr;
uint8_t value = 0;
+#ifdef CONFIG_EC2I
enum ec2i_message em;
enum logical_device_number ldn;
enum host_pnpcfg_index idx;
+#endif
int i;
char *e;
@@ -297,6 +299,7 @@ static int command_rw_ec_reg(int argc, char **argv)
/* access PNPCFG registers */
if (((uint32_t)addr & 0xffff0000) == 0xec210000) {
+#ifdef CONFIG_EC2I
/* set LDN */
ldn = ((uint32_t)addr & 0xff00) >> 8;
idx = (uint32_t)addr & 0xff;
@@ -326,6 +329,9 @@ static int command_rw_ec_reg(int argc, char **argv)
ccprintf(" %02x", value);
}
}
+#else
+ return EC_ERROR_ACCESS_DENIED;
+#endif
/* access EC registers */
} else {
/* write register */
diff --git a/driver/build.mk b/driver/build.mk
index 19e108eb89..999cc52032 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -77,6 +77,7 @@ driver-$(CONFIG_THERMISTOR_NCP15WB)+=temp_sensor/thermistor_ncp15wb.o
driver-$(CONFIG_USB_PD_TCPM_STUB)+=tcpm/stub.o
driver-$(CONFIG_USB_PD_TCPM_TCPCI)+=tcpm/tcpci.o
driver-$(CONFIG_USB_PD_TCPM_FUSB302)+=tcpm/fusb302.o
+driver-$(CONFIG_USB_PD_TCPM_ITE83XX)+=tcpm/it83xx.o
# USB switches
driver-$(CONFIG_USB_SWITCH_PI3USB9281)+=usb_switch_pi3usb9281.o
diff --git a/driver/tcpm/it83xx.c b/driver/tcpm/it83xx.c
new file mode 100644
index 0000000000..4a1257514b
--- /dev/null
+++ b/driver/tcpm/it83xx.c
@@ -0,0 +1,456 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* TCPM for MCU also running TCPC */
+
+#include "common.h"
+#include "config.h"
+#include "console.h"
+#include "it83xx_pd.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpm.h"
+
+const struct usbpd_ctrl_t usbpd_ctrl_regs[] = {
+ {&IT83XX_GPIO_GPCRF4, &IT83XX_GPIO_GPCRF5, IT83XX_IRQ_USBPD0},
+ {&IT83XX_GPIO_GPCRH1, &IT83XX_GPIO_GPCRH2, IT83XX_IRQ_USBPD1},
+};
+BUILD_ASSERT(ARRAY_SIZE(usbpd_ctrl_regs) == USBPD_PORT_COUNT);
+
+static enum tcpc_cc_voltage_status it83xx_get_cc(
+ enum usbpd_port port,
+ enum usbpd_cc_pin cc_pin)
+{
+ enum usbpd_ufp_volt_status ufp_volt;
+ enum usbpd_dfp_volt_status dfp_volt;
+ enum tcpc_cc_voltage_status cc_state = TYPEC_CC_VOLT_OPEN;
+ int pull;
+
+ pull = (cc_pin == USBPD_CC_PIN_1) ?
+ USBPD_GET_CC1_PULL_REGISTER_SELECTION(port) :
+ USBPD_GET_CC2_PULL_REGISTER_SELECTION(port);
+
+ /* select Rp */
+ if (pull)
+ CLEAR_MASK(cc_state, (1 << 2));
+ /* select Rd */
+ else
+ SET_MASK(cc_state, (1 << 2));
+
+ /* sink */
+ if (USBPD_GET_POWER_ROLE(port) == USBPD_POWER_ROLE_CONSUMER) {
+ if (cc_pin == USBPD_CC_PIN_1)
+ ufp_volt = IT83XX_USBPD_UFPVDR(port) & 0xf;
+ else
+ ufp_volt = (IT83XX_USBPD_UFPVDR(port) >> 4) & 0xf;
+
+ switch (ufp_volt) {
+ case USBPD_UFP_STATE_SNK_DEF:
+ cc_state |= (TYPEC_CC_VOLT_SNK_DEF & 3);
+ break;
+ case USBPD_UFP_STATE_SNK_1_5:
+ cc_state |= (TYPEC_CC_VOLT_SNK_1_5 & 3);
+ break;
+ case USBPD_UFP_STATE_SNK_3_0:
+ cc_state |= (TYPEC_CC_VOLT_SNK_3_0 & 3);
+ break;
+ case USBPD_UFP_STATE_SNK_OPEN:
+ cc_state = TYPEC_CC_VOLT_OPEN;
+ break;
+ default:
+ cc_state = TYPEC_CC_VOLT_OPEN;
+ break;
+ }
+ /* source */
+ } else {
+ if (cc_pin == USBPD_CC_PIN_1)
+ dfp_volt = IT83XX_USBPD_DFPVDR(port) & 0xf;
+ else
+ dfp_volt = (IT83XX_USBPD_DFPVDR(port) >> 4) & 0xf;
+
+ switch (dfp_volt) {
+ case USBPD_DFP_STATE_SRC_RA:
+ cc_state |= TYPEC_CC_VOLT_RA;
+ break;
+ case USBPD_DFP_STATE_SRC_RD:
+ cc_state |= TYPEC_CC_VOLT_RD;
+ break;
+ case USBPD_DFP_STATE_SRC_OPEN:
+ cc_state = TYPEC_CC_VOLT_OPEN;
+ break;
+ default:
+ cc_state = TYPEC_CC_VOLT_OPEN;
+ break;
+ }
+ }
+
+ return cc_state;
+}
+
+static void it83xx_rx_data(enum usbpd_port port, int *head, uint32_t *buf)
+{
+ struct usbpd_header *p_head = (struct usbpd_header *)head;
+ *head = 0;
+
+ if (!USBPD_IS_RX_DONE(port))
+ return;
+
+ /* store header */
+ *p_head = *((struct usbpd_header *)IT83XX_USBPD_RMH_BASE(port));
+ /* check data message */
+ if (p_head->data_obj_num)
+ memcpy(buf,
+ (uint8_t *)IT83XX_USBPD_RDO_BASE(port),
+ p_head->data_obj_num * 4);
+ /*
+ * Note: clear RX done interrupt after get the data.
+ * If clear this bit, USBPD receives next packet
+ */
+ IT83XX_USBPD_MRSR(port) = USBPD_REG_MASK_RX_MSG_VALID;
+}
+
+static enum tcpc_transmit_complete it83xx_tx_data(
+ enum usbpd_port port,
+ enum tcpm_transmit_type type,
+ uint8_t msg_type,
+ uint8_t length,
+ const uint32_t *buf)
+{
+ int r;
+ uint32_t evt;
+
+ /* set message type */
+ IT83XX_USBPD_MTSR0(port) =
+ (IT83XX_USBPD_MTSR0(port) & ~0x1f) | (msg_type & 0xf);
+ /* SOP type: bit[5:4] 00 SOP, 01 SOP', 10 SOP" */
+ IT83XX_USBPD_MTSR1(port) =
+ (IT83XX_USBPD_MTSR1(port) & ~0x30) | ((type & 0x3) << 4);
+ /* bit7: transmit message is send to cable or not */
+ if (TCPC_TX_SOP == type)
+ IT83XX_USBPD_MTSR0(port) &= ~USBPD_REG_MASK_CABLE_ENABLE;
+ else
+ IT83XX_USBPD_MTSR0(port) |= USBPD_REG_MASK_CABLE_ENABLE;
+ /* clear msg length */
+ IT83XX_USBPD_MTSR1(port) &= (~0x7);
+ /* Limited by PD_HEADER_CNT() */
+ ASSERT(length <= 0x7);
+
+ if (length) {
+ /* set data bit */
+ IT83XX_USBPD_MTSR0(port) |= (1 << 4);
+ /* set data length setting */
+ IT83XX_USBPD_MTSR1(port) |= length;
+ /* set data */
+ memcpy((uint8_t *)IT83XX_USBPD_TDO_BASE(port), buf, length * 4);
+ }
+
+ for (r = 0; r <= PD_RETRY_COUNT; r++) {
+ /* Start TX */
+ USBPD_KICK_TX_START(port);
+ evt = task_wait_event_mask(TASK_EVENT_PHY_TX_DONE,
+ PD_T_TCPC_TX_TIMEOUT);
+ /* check TX status */
+ if (USBPD_IS_TX_ERR(port) || (evt & TASK_EVENT_TIMER)) {
+ /*
+ * If discard, means HW doesn't send the msg and resend.
+ */
+ if (USBPD_IS_TX_DISCARD(port))
+ continue;
+ else
+ return TCPC_TX_COMPLETE_FAILED;
+ } else {
+ break;
+ }
+ }
+
+ if (r > PD_RETRY_COUNT)
+ return TCPC_TX_COMPLETE_DISCARDED;
+
+ return TCPC_TX_COMPLETE_SUCCESS;
+}
+
+static enum tcpc_transmit_complete it83xx_send_hw_reset(enum usbpd_port port,
+ enum tcpm_transmit_type reset_type)
+{
+ if (reset_type == TCPC_TX_CABLE_RESET)
+ IT83XX_USBPD_MTSR0(port) |= USBPD_REG_MASK_CABLE_ENABLE;
+ else
+ IT83XX_USBPD_MTSR0(port) &= ~USBPD_REG_MASK_CABLE_ENABLE;
+
+ /* send hard reset */
+ USBPD_SEND_HARD_RESET(port);
+ usleep(MSEC);
+
+ if (IT83XX_USBPD_MTSR0(port) & USBPD_REG_MASK_SEND_HW_RESET)
+ return TCPC_TX_COMPLETE_FAILED;
+
+ return TCPC_TX_COMPLETE_SUCCESS;
+}
+
+static void it83xx_send_bist_mode2_pattern(enum usbpd_port port)
+{
+ USBPD_ENABLE_SEND_BIST_MODE_2(port);
+ usleep(PD_T_BIST_TRANSMIT);
+ USBPD_DISABLE_SEND_BIST_MODE_2(port);
+}
+
+static void it83xx_enable_vconn(enum usbpd_port port, int enabled)
+{
+ enum usbpd_cc_pin cc_pin;
+
+ if (USBPD_GET_PULL_CC_SELECTION(port))
+ cc_pin = USBPD_CC_PIN_1;
+ else
+ cc_pin = USBPD_CC_PIN_2;
+
+ if (enabled) {
+ /* Disable unused CC to become VCONN */
+ if (cc_pin == USBPD_CC_PIN_1) {
+ IT83XX_USBPD_CCCSR(port) =
+ (IT83XX_USBPD_CCCSR(port) | 0xa0) & ~0xa;
+ IT83XX_USBPD_CCPSR(port) = (IT83XX_USBPD_CCPSR(port)
+ & ~USBPD_REG_MASK_DISCONNECT_POWER_CC2)
+ | USBPD_REG_MASK_DISCONNECT_POWER_CC1;
+ } else {
+ IT83XX_USBPD_CCCSR(port) =
+ (IT83XX_USBPD_CCCSR(port) | 0xa) & ~0xa0;
+ IT83XX_USBPD_CCPSR(port) = (IT83XX_USBPD_CCPSR(port)
+ & ~USBPD_REG_MASK_DISCONNECT_POWER_CC1)
+ | USBPD_REG_MASK_DISCONNECT_POWER_CC2;
+ }
+ } else {
+ /* Enable cc1 and cc2 */
+ IT83XX_USBPD_CCCSR(port) &= ~0xaa;
+ IT83XX_USBPD_CCPSR(port) |=
+ (USBPD_REG_MASK_DISCONNECT_POWER_CC1 |
+ USBPD_REG_MASK_DISCONNECT_POWER_CC2);
+ }
+}
+
+static void it83xx_set_power_role(enum usbpd_port port, int power_role)
+{
+ /* PD_ROLE_SINK 0, PD_ROLE_SOURCE 1 */
+ if (power_role == PD_ROLE_SOURCE) {
+ /* bit0: source */
+ SET_MASK(IT83XX_USBPD_PDMSR(port), (1 << 0));
+ /* bit1: CC1 select Rp */
+ SET_MASK(IT83XX_USBPD_CCGCR(port), (1 << 1));
+ /* bit3: CC2 select Rp */
+ SET_MASK(IT83XX_USBPD_BMCSR(port), (1 << 3));
+ } else {
+ /* bit0: sink */
+ CLEAR_MASK(IT83XX_USBPD_PDMSR(port), (1 << 0));
+ /* bit1: CC1 select Rd */
+ CLEAR_MASK(IT83XX_USBPD_CCGCR(port), (1 << 1));
+ /* bit3: CC2 select Rd */
+ CLEAR_MASK(IT83XX_USBPD_BMCSR(port), (1 << 3));
+ }
+}
+
+static void it83xx_set_data_role(enum usbpd_port port, int pd_role)
+{
+ /* 0: PD_ROLE_UFP 1: PD_ROLE_DFP */
+ IT83XX_USBPD_PDMSR(port) =
+ (IT83XX_USBPD_PDMSR(port) & ~0xc) | ((pd_role & 0x1) << 2);
+}
+
+static void it83xx_init(enum usbpd_port port, int role)
+{
+ /* defalut PD Clock = PLL 48 / 6 = 8M. */
+ IT83XX_ECPM_SCDCR4 = (IT83XX_ECPM_SCDCR4 & 0xf0) | 5;
+ /* reset */
+ IT83XX_USBPD_GCR(port) = 0;
+ USBPD_SW_RESET(port);
+ /* set SOP: receive SOP message only.
+ * bit[7]: SOP" support enable.
+ * bit[6]: SOP' support enable.
+ * bit[5]: SOP support enable.
+ */
+ IT83XX_USBPD_PDMSR(port) = USBPD_REG_MASK_SOP_ENABLE;
+ /* W/C status */
+ IT83XX_USBPD_ISR(port) = 0xff;
+ /* enable cc, select cc1 and Rd (80uA output when Rp selected) */
+ IT83XX_USBPD_CCGCR(port) = 0xd;
+ /* change data role as the same power role */
+ it83xx_set_data_role(port, role);
+ /* set power role */
+ it83xx_set_power_role(port, role);
+ /* disable all interrupts */
+ IT83XX_USBPD_IMR(port) = 0xff;
+ /* enable tx done and reset detect interrupt */
+ IT83XX_USBPD_IMR(port) &= ~(USBPD_REG_MASK_MSG_TX_DONE |
+ USBPD_REG_MASK_HARD_RESET_DETECT);
+ IT83XX_USBPD_CCPSR(port) = 0xff;
+ /* cc connect */
+ IT83XX_USBPD_CCCSR(port) = 0;
+ /* disable vconn */
+ it83xx_enable_vconn(port, 0);
+ /* TX start from high */
+ IT83XX_USBPD_CCADCR(port) |= (1 << 6);
+ /* enable cc1/cc2 */
+ *usbpd_ctrl_regs[port].cc1 = 0x86;
+ *usbpd_ctrl_regs[port].cc2 = 0x86;
+ task_clear_pending_irq(usbpd_ctrl_regs[port].irq);
+ task_enable_irq(usbpd_ctrl_regs[port].irq);
+ USBPD_START(port);
+}
+
+static void it83xx_select_polarity(enum usbpd_port port,
+ enum usbpd_cc_pin cc_pin)
+{
+ /* cc1/cc2 selection */
+ if (cc_pin == USBPD_CC_PIN_1)
+ SET_MASK(IT83XX_USBPD_CCGCR(port), (1 << 0));
+ else
+ CLEAR_MASK(IT83XX_USBPD_CCGCR(port), (1 << 0));
+}
+
+static void it83xx_set_cc(enum usbpd_port port, int pull)
+{
+ if (pull == TYPEC_CC_RD)
+ it83xx_set_power_role(port, PD_ROLE_SINK);
+ else if (pull == TYPEC_CC_RP)
+ it83xx_set_power_role(port, PD_ROLE_SOURCE);
+}
+
+static int it83xx_tcpm_init(int port)
+{
+ /* Initialize physical layer */
+ it83xx_init(port, PD_ROLE_DEFAULT);
+
+ return EC_SUCCESS;
+}
+
+static int it83xx_tcpm_get_cc(int port, int *cc1, int *cc2)
+{
+ *cc2 = it83xx_get_cc(port, USBPD_CC_PIN_2);
+ *cc1 = it83xx_get_cc(port, USBPD_CC_PIN_1);
+
+ return EC_SUCCESS;
+}
+
+static int it83xx_tcpm_set_cc(int port, int pull)
+{
+ it83xx_set_cc(port, pull);
+
+ return EC_SUCCESS;
+}
+
+static int it83xx_tcpm_set_polarity(int port, int polarity)
+{
+ it83xx_select_polarity(port, polarity);
+
+ return EC_SUCCESS;
+}
+
+static int it83xx_tcpm_set_vconn(int port, int enable)
+{
+#ifdef CONFIG_USBC_VCONN
+ it83xx_enable_vconn(port, enable);
+ /* vconn switch */
+ board_pd_vconn_ctrl(port,
+ USBPD_GET_PULL_CC_SELECTION(port) ?
+ USBPD_CC_PIN_2 :
+ USBPD_CC_PIN_1, enable);
+#endif
+
+ return EC_SUCCESS;
+}
+
+static int it83xx_tcpm_set_msg_header(int port, int power_role, int data_role)
+{
+ it83xx_set_power_role(port, power_role);
+ it83xx_set_data_role(port, data_role);
+
+ return EC_SUCCESS;
+}
+
+static int it83xx_tcpm_set_rx_enable(int port, int enable)
+{
+ int i;
+
+ if (enable) {
+ IT83XX_USBPD_IMR(port) &= ~USBPD_REG_MASK_MSG_RX_DONE;
+ USBPD_ENABLE_BMC_PHY(port);
+ } else {
+ IT83XX_USBPD_IMR(port) |= USBPD_REG_MASK_MSG_RX_DONE;
+ USBPD_DISABLE_BMC_PHY(port);
+ }
+
+ /* If any PD port is connected, then disable deep sleep */
+ for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; ++i)
+ if (IT83XX_USBPD_GCR(port) | USBPD_REG_MASK_BMC_PHY)
+ break;
+
+ if (i == CONFIG_USB_PD_PORT_COUNT)
+ enable_sleep(SLEEP_MASK_USB_PD);
+ else
+ disable_sleep(SLEEP_MASK_USB_PD);
+
+ return EC_SUCCESS;
+}
+
+static int it83xx_tcpm_get_message(int port, uint32_t *payload, int *head)
+{
+ it83xx_rx_data(port, head, payload);
+ /* un-mask RX done interrupt */
+ IT83XX_USBPD_IMR(port) &= ~USBPD_REG_MASK_MSG_RX_DONE;
+
+ return EC_SUCCESS;
+}
+
+static int it83xx_tcpm_transmit(int port,
+ enum tcpm_transmit_type type,
+ uint16_t header,
+ const uint32_t *data)
+{
+ int status = TCPC_TX_COMPLETE_FAILED;
+
+ switch (type) {
+ case TCPC_TX_SOP:
+ case TCPC_TX_SOP_PRIME:
+ case TCPC_TX_SOP_PRIME_PRIME:
+ status = it83xx_tx_data(port,
+ type,
+ PD_HEADER_TYPE(header),
+ PD_HEADER_CNT(header),
+ data);
+ break;
+ case TCPC_TX_BIST_MODE_2:
+ it83xx_send_bist_mode2_pattern(port);
+ status = TCPC_TX_COMPLETE_SUCCESS;
+ break;
+ case TCPC_TX_HARD_RESET:
+ case TCPC_TX_CABLE_RESET:
+ status = it83xx_send_hw_reset(port, type);
+ break;
+ default:
+ status = TCPC_TX_COMPLETE_FAILED;
+ break;
+ }
+ pd_transmit_complete(port, status);
+
+ return EC_SUCCESS;
+}
+
+const struct tcpm_drv it83xx_tcpm_drv = {
+ .init = &it83xx_tcpm_init,
+ .get_cc = &it83xx_tcpm_get_cc,
+#ifdef CONFIG_USB_PD_TCPM_VBUS
+ .get_vbus_level = NULL,
+#endif
+ .set_cc = &it83xx_tcpm_set_cc,
+ .set_polarity = &it83xx_tcpm_set_polarity,
+ .set_vconn = &it83xx_tcpm_set_vconn,
+ .set_msg_header = &it83xx_tcpm_set_msg_header,
+ .set_rx_enable = &it83xx_tcpm_set_rx_enable,
+ .get_message = &it83xx_tcpm_get_message,
+ .transmit = &it83xx_tcpm_transmit,
+ .tcpc_alert = NULL,
+};
diff --git a/driver/tcpm/it83xx_pd.h b/driver/tcpm/it83xx_pd.h
new file mode 100644
index 0000000000..0702b24b84
--- /dev/null
+++ b/driver/tcpm/it83xx_pd.h
@@ -0,0 +1,112 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* USB Power delivery port management */
+#ifndef __CROS_EC_DRIVER_TCPM_IT83XX_H
+#define __CROS_EC_DRIVER_TCPM_IT83XX_H
+
+#define TASK_EVENT_PHY_TX_DONE TASK_EVENT_CUSTOM((1 << 17))
+
+#define SET_MASK(reg, bit_mask) ((reg) |= (bit_mask))
+#define CLEAR_MASK(reg, bit_mask) ((reg) &= (~(bit_mask)))
+#define IS_MASK_SET(reg, bit_mask) (((reg) & (bit_mask)) != 0)
+#define IS_MASK_CLEAR(reg, bit_mask) (((reg) & (bit_mask)) == 0)
+
+/* macros for set */
+#define USBPD_KICK_TX_START(port) \
+ SET_MASK(IT83XX_USBPD_MTCR(port), \
+ USBPD_REG_MASK_TX_START)
+#define USBPD_SEND_HARD_RESET(port) \
+ SET_MASK(IT83XX_USBPD_MTSR0(port), \
+ USBPD_REG_MASK_SEND_HW_RESET)
+#define USBPD_SW_RESET(port) \
+ SET_MASK(IT83XX_USBPD_GCR(port), \
+ USBPD_REG_MASK_SW_RESET_BIT)
+#define USBPD_ENABLE_BMC_PHY(port) \
+ SET_MASK(IT83XX_USBPD_GCR(port), \
+ USBPD_REG_MASK_BMC_PHY)
+#define USBPD_DISABLE_BMC_PHY(port) \
+ CLEAR_MASK(IT83XX_USBPD_GCR(port), \
+ USBPD_REG_MASK_BMC_PHY)
+#define USBPD_START(port) \
+ CLEAR_MASK(IT83XX_USBPD_CCGCR(port), \
+ USBPD_REG_MASK_DISABLE_CC)
+#define USBPD_ENABLE_SEND_BIST_MODE_2(port) \
+ SET_MASK(IT83XX_USBPD_MTSR0(port), \
+ USBPD_REG_MASK_SEND_BIST_MODE_2)
+#define USBPD_DISABLE_SEND_BIST_MODE_2(port) \
+ CLEAR_MASK(IT83XX_USBPD_MTSR0(port), \
+ USBPD_REG_MASK_SEND_BIST_MODE_2)
+
+/* macros for get */
+#define USBPD_GET_POWER_ROLE(port) \
+ (IT83XX_USBPD_PDMSR(port) & 1)
+#define USBPD_GET_CC1_PULL_REGISTER_SELECTION(port) \
+ (IT83XX_USBPD_CCGCR(port) & (1 << 1))
+#define USBPD_GET_CC2_PULL_REGISTER_SELECTION(port) \
+ (IT83XX_USBPD_BMCSR(port) & (1 << 3))
+#define USBPD_GET_PULL_CC_SELECTION(port) \
+ (IT83XX_USBPD_CCGCR(port) & 1)
+
+/* macros for check */
+#define USBPD_IS_TX_ERR(port) \
+ IS_MASK_SET(IT83XX_USBPD_MTCR(port), USBPD_REG_MASK_TX_ERR_STAT)
+#define USBPD_IS_TX_DISCARD(port) \
+ IS_MASK_SET(IT83XX_USBPD_MTCR(port), USBPD_REG_MASK_TX_DISCARD_STAT)
+
+/* macros for PD ISR */
+#define USBPD_IS_HARD_RESET_DETECT(port) \
+ IS_MASK_SET(IT83XX_USBPD_ISR(port), USBPD_REG_MASK_HARD_RESET_DETECT)
+#define USBPD_IS_TX_DONE(port) \
+ IS_MASK_SET(IT83XX_USBPD_ISR(port), USBPD_REG_MASK_MSG_TX_DONE)
+#define USBPD_IS_RX_DONE(port) \
+ IS_MASK_SET(IT83XX_USBPD_ISR(port), USBPD_REG_MASK_MSG_RX_DONE)
+
+enum usbpd_cc_pin {
+ USBPD_CC_PIN_1,
+ USBPD_CC_PIN_2,
+};
+
+enum usbpd_ufp_volt_status {
+ USBPD_UFP_STATE_SNK_OPEN = 0,
+ USBPD_UFP_STATE_SNK_DEF = 1,
+ USBPD_UFP_STATE_SNK_1_5 = 3,
+ USBPD_UFP_STATE_SNK_3_0 = 7,
+};
+
+enum usbpd_dfp_volt_status {
+ USBPD_DFP_STATE_SRC_RA = 0,
+ USBPD_DFP_STATE_SRC_RD = 1,
+ USBPD_DFP_STATE_SRC_OPEN = 3,
+};
+
+enum usbpd_power_role {
+ USBPD_POWER_ROLE_CONSUMER,
+ USBPD_POWER_ROLE_PROVIDER,
+ USBPD_POWER_ROLE_CONSUMER_PROVIDER,
+ USBPD_POWER_ROLE_PROVIDER_CONSUMER,
+};
+
+struct usbpd_header {
+ uint8_t msg_type : 4;
+ uint8_t reserved : 1;
+ uint8_t port_role : 1;
+ uint8_t spec_ver : 2;
+ uint8_t power_role : 1;
+ uint8_t msg_id : 3;
+ uint8_t data_obj_num : 3;
+ uint8_t reserved2 : 1;
+};
+
+struct usbpd_ctrl_t {
+ volatile uint8_t *cc1;
+ volatile uint8_t *cc2;
+ uint8_t irq;
+};
+
+extern const struct usbpd_ctrl_t usbpd_ctrl_regs[];
+extern const struct tcpm_drv it83xx_tcpm_drv;
+
+#endif /* __CROS_EC_DRIVER_TCPM_IT83XX_H */
diff --git a/include/config.h b/include/config.h
index 87df86c786..bfbc0b17a2 100644
--- a/include/config.h
+++ b/include/config.h
@@ -1858,6 +1858,7 @@
#undef CONFIG_USB_PD_TCPM_STUB
#undef CONFIG_USB_PD_TCPM_TCPCI
#undef CONFIG_USB_PD_TCPM_FUSB302
+#undef CONFIG_USB_PD_TCPM_ITE83XX
/*
* Use this option if the TCPC port controller supports the optional register