diff options
author | Dino Li <dino.li@ite.com.tw> | 2016-04-18 15:41:27 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-04-18 17:32:41 -0700 |
commit | 69668a04436700071bc2a26400f5f52f0e499020 (patch) | |
tree | 163d7ff68d473b703471e3639692041508ad0d93 | |
parent | 068cd0850684ee28a5a514e5a270edce2edb3979 (diff) | |
download | chrome-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.c | 8 | ||||
-rw-r--r-- | chip/it83xx/intc.c | 49 | ||||
-rw-r--r-- | chip/it83xx/irq.c | 2 | ||||
-rw-r--r-- | chip/it83xx/registers.h | 84 | ||||
-rw-r--r-- | chip/it83xx/system.c | 6 | ||||
-rw-r--r-- | driver/build.mk | 1 | ||||
-rw-r--r-- | driver/tcpm/it83xx.c | 456 | ||||
-rw-r--r-- | driver/tcpm/it83xx_pd.h | 112 | ||||
-rw-r--r-- | include/config.h | 1 |
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 |