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>
This commit is contained in:
Dino Li
2016-04-18 15:41:27 +08:00
committed by chrome-bot
parent 068cd08506
commit 69668a0443
9 changed files with 713 additions and 6 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

456
driver/tcpm/it83xx.c Normal file
View File

@@ -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,
};

112
driver/tcpm/it83xx_pd.h Normal file
View File

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

View File

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