1
0
forked from me/IronOS

Import existing FUSB302 library

This commit is contained in:
Ben V. Brown
2020-06-11 23:30:06 +10:00
parent 954770373c
commit 450ce17935
15 changed files with 9892 additions and 244 deletions

View File

@@ -0,0 +1,952 @@
/*
FUSB302.c - Library for interacting with the FUSB302B chip.
Copyright 2015 The Chromium OS Authors
Copyright 2017 Jason Cerundolo
Released under an MIT license. See LICENSE file.
*/
#include "FUSB302.h"
#include "usb_pd_tcpm.h"
#include "USBC_TCPM/tcpm.h"
#include "USBC_PD/usb_pd.h"
#define PACKET_IS_GOOD_CRC(head) (PD_HEADER_TYPE(head) == PD_CTRL_GOOD_CRC && \
PD_HEADER_CNT(head) == 0)
static struct fusb302_chip_state {
int cc_polarity;
int vconn_enabled;
/* 1 = pulling up (DFP) 0 = pulling down (UFP) */
int pulling_up;
int rx_enable;
uint8_t mdac_vnc;
uint8_t mdac_rd;
} state[CONFIG_USB_PD_PORT_COUNT];
/*
* Bring the FUSB302 out of reset after Hard Reset signaling. This will
* automatically flush both the Rx and Tx FIFOs.
*/
static void fusb302_pd_reset(int port) {
tcpc_write(port, TCPC_REG_RESET, TCPC_REG_RESET_PD_RESET);
}
/*
* Flush our Rx FIFO. To prevent packet framing issues, this function should
* only be called when Rx is disabled.
*/
static void fusb302_flush_rx_fifo(int port) {
/*
* other bits in the register _should_ be 0
* until the day we support other SOP* types...
* then we'll have to keep a shadow of what this register
* value should be so we don't clobber it here!
*/
i2c_master_lock(tcpc_config[port].i2c_host_port);
tcpc_write(port, TCPC_REG_CONTROL1, TCPC_REG_CONTROL1_RX_FLUSH);
}
static void fusb302_flush_tx_fifo(int port) {
int reg;
i2c_master_lock(tcpc_config[port].i2c_host_port);
tcpc_read(port, TCPC_REG_CONTROL0, &reg);
reg |= TCPC_REG_CONTROL0_TX_FLUSH;
tcpc_write(port, TCPC_REG_CONTROL0, reg);
}
static void fusb302_auto_goodcrc_enable(int port, int enable) {
int reg;
i2c_master_lock(tcpc_config[port].i2c_host_port);
tcpc_read(port, TCPC_REG_SWITCHES1, &reg);
if (enable)
reg |= TCPC_REG_SWITCHES1_AUTO_GCRC;
else
reg &= ~TCPC_REG_SWITCHES1_AUTO_GCRC;
tcpc_write(port, TCPC_REG_SWITCHES1, reg);
}
/* Convert BC LVL values (in FUSB302) to Type-C CC Voltage Status */
static int convert_bc_lvl(int port, int bc_lvl) {
/* assume OPEN unless one of the following conditions is true... */
int ret = TYPEC_CC_VOLT_OPEN;
if (state[port].pulling_up) {
if (bc_lvl == 0x00)
ret = TYPEC_CC_VOLT_RA;
else if (bc_lvl < 0x3)
ret = TYPEC_CC_VOLT_RD;
} else {
if (bc_lvl == 0x1)
ret = TYPEC_CC_VOLT_SNK_DEF;
else if (bc_lvl == 0x2)
ret = TYPEC_CC_VOLT_SNK_1_5;
else if (bc_lvl == 0x3)
ret = TYPEC_CC_VOLT_SNK_3_0;
}
return ret;
}
static int measure_cc_pin_source(int port, int cc_measure) {
int switches0_reg;
int reg;
int cc_lvl;
i2c_master_lock(tcpc_config[port].i2c_host_port);
/* Read status register */
tcpc_read(port, TCPC_REG_SWITCHES0, &reg);
/* Save current value */
switches0_reg = reg;
/* Clear pull-up register settings and measure bits */
reg &= ~(TCPC_REG_SWITCHES0_MEAS_CC1 | TCPC_REG_SWITCHES0_MEAS_CC2);
/* Set desired pullup register bit */
if (cc_measure == TCPC_REG_SWITCHES0_MEAS_CC1)
reg |= TCPC_REG_SWITCHES0_CC1_PU_EN;
else
reg |= TCPC_REG_SWITCHES0_CC2_PU_EN;
/* Set CC measure bit */
reg |= cc_measure;
/* Set measurement switch */
tcpc_write(port, TCPC_REG_SWITCHES0, reg);
/* Set MDAC for Open vs Rd/Ra comparison */
tcpc_write(port, TCPC_REG_MEASURE, state[port].mdac_vnc);
/* Wait on measurement */
usleep(250);
/* Read status register */
tcpc_read(port, TCPC_REG_STATUS0, &reg);
/* Assume open */
cc_lvl = TYPEC_CC_VOLT_OPEN;
/* CC level is below the 'no connect' threshold (vOpen) */
if ((reg & TCPC_REG_STATUS0_COMP) == 0) {
/* Set MDAC for Rd vs Ra comparison */
tcpc_write(port, TCPC_REG_MEASURE, state[port].mdac_rd);
/* Wait on measurement */
usleep(250);
/* Read status register */
tcpc_read(port, TCPC_REG_STATUS0, &reg);
cc_lvl =
(reg & TCPC_REG_STATUS0_COMP) ?
TYPEC_CC_VOLT_RD : TYPEC_CC_VOLT_RA;
}
/* Restore SWITCHES0 register to its value prior */
tcpc_write(port, TCPC_REG_SWITCHES0, switches0_reg);
return cc_lvl;
}
/* Determine cc pin state for source when in manual detect mode */
static void detect_cc_pin_source_manual(int port, int *cc1_lvl, int *cc2_lvl) {
int cc1_measure = TCPC_REG_SWITCHES0_MEAS_CC1;
int cc2_measure = TCPC_REG_SWITCHES0_MEAS_CC2;
if (state[port].vconn_enabled) {
/* If VCONN enabled, measure cc_pin that matches polarity */
if (state[port].cc_polarity)
*cc2_lvl = measure_cc_pin_source(port, cc2_measure);
else
*cc1_lvl = measure_cc_pin_source(port, cc1_measure);
} else {
/* If VCONN not enabled, measure both cc1 and cc2 */
*cc1_lvl = measure_cc_pin_source(port, cc1_measure);
*cc2_lvl = measure_cc_pin_source(port, cc2_measure);
}
}
/* Determine cc pin state for sink */
static void detect_cc_pin_sink(int port, int *cc1, int *cc2) {
int reg;
int orig_meas_cc1;
int orig_meas_cc2;
int bc_lvl_cc1;
int bc_lvl_cc2;
i2c_master_lock(tcpc_config[port].i2c_host_port);
/*
* Measure CC1 first.
*/
tcpc_read(port, TCPC_REG_SWITCHES0, &reg);
/* save original state to be returned to later... */
if (reg & TCPC_REG_SWITCHES0_MEAS_CC1)
orig_meas_cc1 = 1;
else
orig_meas_cc1 = 0;
if (reg & TCPC_REG_SWITCHES0_MEAS_CC2)
orig_meas_cc2 = 1;
else
orig_meas_cc2 = 0;
/* Disable CC2 measurement switch, enable CC1 measurement switch */
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2;
reg |= TCPC_REG_SWITCHES0_MEAS_CC1;
tcpc_write(port, TCPC_REG_SWITCHES0, reg);
/* CC1 is now being measured by FUSB302. */
/* Wait on measurement */
usleep(250);
tcpc_read(port, TCPC_REG_STATUS0, &bc_lvl_cc1);
/* mask away unwanted bits */
bc_lvl_cc1 &= (TCPC_REG_STATUS0_BC_LVL0 | TCPC_REG_STATUS0_BC_LVL1);
/*
* Measure CC2 next.
*/
tcpc_read(port, TCPC_REG_SWITCHES0, &reg);
/* Disable CC1 measurement switch, enable CC2 measurement switch */
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1;
reg |= TCPC_REG_SWITCHES0_MEAS_CC2;
tcpc_write(port, TCPC_REG_SWITCHES0, reg);
/* CC2 is now being measured by FUSB302. */
/* Wait on measurement */
usleep(250);
tcpc_read(port, TCPC_REG_STATUS0, &bc_lvl_cc2);
/* mask away unwanted bits */
bc_lvl_cc2 &= (TCPC_REG_STATUS0_BC_LVL0 | TCPC_REG_STATUS0_BC_LVL1);
*cc1 = convert_bc_lvl(port, bc_lvl_cc1);
*cc2 = convert_bc_lvl(port, bc_lvl_cc2);
/* return MEAS_CC1/2 switches to original state */
tcpc_read(port, TCPC_REG_SWITCHES0, &reg);
if (orig_meas_cc1)
reg |= TCPC_REG_SWITCHES0_MEAS_CC1;
else
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1;
if (orig_meas_cc2)
reg |= TCPC_REG_SWITCHES0_MEAS_CC2;
else
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2;
tcpc_write(port, TCPC_REG_SWITCHES0, reg);
}
/* Parse header bytes for the size of packet */
static int get_num_bytes(uint16_t header) {
int rv;
/* Grab the Number of Data Objects field.*/
rv = PD_HEADER_CNT(header);
/* Multiply by four to go from 32-bit words -> bytes */
rv *= 4;
/* Plus 2 for header */
rv += 2;
return rv;
}
static int fusb302_send_message(int port, uint16_t header, const uint32_t *data,
uint8_t *buf, int buf_pos) {
int rv;
int reg;
int len;
len = get_num_bytes(header);
/*
* packsym tells the TXFIFO that the next X bytes are payload,
* and should not be interpreted as special tokens.
* The 5 LSBs represent X, the number of bytes.
*/
reg = fusb302_TKN_PACKSYM;
reg |= (len & 0x1F);
buf[buf_pos++] = reg;
/* write in the header */
reg = header;
buf[buf_pos++] = reg & 0xFF;
reg >>= 8;
buf[buf_pos++] = reg & 0xFF;
/* header is done, subtract from length to make this for-loop simpler */
len -= 2;
/* write data objects, if present */
memcpy(&buf[buf_pos], data, len);
buf_pos += len;
/* put in the CRC */
buf[buf_pos++] = fusb302_TKN_JAMCRC;
/* put in EOP */
buf[buf_pos++] = fusb302_TKN_EOP;
/* Turn transmitter off after sending message */
buf[buf_pos++] = fusb302_TKN_TXOFF;
/* Start transmission */
reg = fusb302_TKN_TXON;
buf[buf_pos++] = fusb302_TKN_TXON;
/* burst write for speed! */
i2c_master_lock(tcpc_config[port].i2c_host_port);
rv = tcpc_xfer(port, buf, buf_pos, 0, 0, I2C_XFER_SINGLE);
return rv;
}
static int fusb302_tcpm_select_rp_value(int port, int rp) {
int reg;
int rv;
uint8_t vnc, rd;
i2c_master_lock(tcpc_config[port].i2c_host_port);
rv = tcpc_read(port, TCPC_REG_CONTROL0, &reg);
if (rv)
return rv;
/* Set the current source for Rp value */
reg &= ~TCPC_REG_CONTROL0_HOST_CUR_MASK;
switch (rp) {
case TYPEC_RP_1A5:
reg |= TCPC_REG_CONTROL0_HOST_CUR_1A5;
vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_1_5_VNC_MV);
rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_1_5_RD_THRESH_MV);
break;
case TYPEC_RP_3A0:
reg |= TCPC_REG_CONTROL0_HOST_CUR_3A0;
vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_3_0_VNC_MV);
rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_3_0_RD_THRESH_MV);
break;
case TYPEC_RP_USB:
default:
reg |= TCPC_REG_CONTROL0_HOST_CUR_USB;
vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_VNC_MV);
rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_RD_THRESH_MV);
}
state[port].mdac_vnc = vnc;
state[port].mdac_rd = rd;
rv = tcpc_write(port, TCPC_REG_CONTROL0, reg);
return rv;
}
static int fusb302_tcpm_init(int port) {
int reg;
/* set default */
state[port].cc_polarity = -1;
/* set the voltage threshold for no connect detection (vOpen) */
state[port].mdac_vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_VNC_MV);
/* set the voltage threshold for Rd vs Ra detection */
state[port].mdac_rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_RD_THRESH_MV);
/* all other variables assumed to default to 0 */
i2c_master_lock(tcpc_config[port].i2c_host_port);
/* Restore default settings */
tcpc_write(port, TCPC_REG_RESET, TCPC_REG_RESET_SW_RESET);
/* Turn on retries and set number of retries */
tcpc_read(port, TCPC_REG_CONTROL3, &reg);
reg |= TCPC_REG_CONTROL3_AUTO_RETRY;
reg |= (PD_RETRY_COUNT & 0x3) <<
TCPC_REG_CONTROL3_N_RETRIES_POS;
tcpc_write(port, TCPC_REG_CONTROL3, reg);
/* Create interrupt masks */
reg = 0xFF;
/* CC level changes */
reg &= ~TCPC_REG_MASK_BC_LVL;
/* collisions */
reg &= ~TCPC_REG_MASK_COLLISION;
/* misc alert */
reg &= ~TCPC_REG_MASK_ALERT;
/* packet received with correct CRC */
reg &= ~TCPC_REG_MASK_CRC_CHK;
tcpc_write(port, TCPC_REG_MASK, reg);
reg = 0xFF;
/* when all pd message retries fail... */
reg &= ~TCPC_REG_MASKA_RETRYFAIL;
/* when fusb302 send a hard reset. */
reg &= ~TCPC_REG_MASKA_HARDSENT;
/* when fusb302 receives GoodCRC ack for a pd message */
reg &= ~TCPC_REG_MASKA_TX_SUCCESS;
/* when fusb302 receives a hard reset */
reg &= ~TCPC_REG_MASKA_HARDRESET;
tcpc_write(port, TCPC_REG_MASKA, reg);
reg = 0xFF;
/* when fusb302 sends GoodCRC to ack a pd message */
reg &= ~TCPC_REG_MASKB_GCRCSENT;
tcpc_write(port, TCPC_REG_MASKB, reg);
/* Interrupt Enable */
tcpc_read(port, TCPC_REG_CONTROL0, &reg);
reg &= ~TCPC_REG_CONTROL0_INT_MASK;
tcpc_write(port, TCPC_REG_CONTROL0, reg);
/* Set VCONN switch defaults */
tcpm_set_polarity(port, 0);
tcpm_set_vconn(port, 0);
/* Turn on the power! */
/* TODO: Reduce power consumption */
tcpc_write(port, TCPC_REG_POWER, TCPC_REG_POWER_PWR_ALL);
return 0;
}
static int fusb302_tcpm_release(int port) {
return EC_ERROR_UNIMPLEMENTED;
}
static int fusb302_tcpm_get_cc(int port, int *cc1, int *cc2) {
if (state[port].pulling_up) {
/* Source mode? */
detect_cc_pin_source_manual(port, cc1, cc2);
} else {
/* Sink mode? */
detect_cc_pin_sink(port, cc1, cc2);
}
return 0;
}
static int fusb302_tcpm_set_cc(int port, int pull) {
int reg;
i2c_master_lock(tcpc_config[port].i2c_host_port);
/* NOTE: FUSB302 toggles a single pull-up between CC1 and CC2 */
/* NOTE: FUSB302 Does not support Ra. */
switch (pull) {
case TYPEC_CC_RP:
/* enable the pull-up we know to be necessary */
tcpc_read(port, TCPC_REG_SWITCHES0, &reg);
reg &= ~(TCPC_REG_SWITCHES0_CC2_PU_EN |
TCPC_REG_SWITCHES0_CC1_PU_EN |
TCPC_REG_SWITCHES0_CC1_PD_EN |
TCPC_REG_SWITCHES0_CC2_PD_EN |
TCPC_REG_SWITCHES0_VCONN_CC1 |
TCPC_REG_SWITCHES0_VCONN_CC2);
reg |= TCPC_REG_SWITCHES0_CC1_PU_EN |
TCPC_REG_SWITCHES0_CC2_PU_EN;
if (state[port].vconn_enabled)
reg |= state[port].cc_polarity ?
TCPC_REG_SWITCHES0_VCONN_CC1 :
TCPC_REG_SWITCHES0_VCONN_CC2;
tcpc_write(port, TCPC_REG_SWITCHES0, reg);
state[port].pulling_up = 1;
break;
case TYPEC_CC_RD:
/* Enable UFP Mode */
/* turn off toggle */
tcpc_read(port, TCPC_REG_CONTROL2, &reg);
reg &= ~TCPC_REG_CONTROL2_TOGGLE;
tcpc_write(port, TCPC_REG_CONTROL2, reg);
/* enable pull-downs, disable pullups */
tcpc_read(port, TCPC_REG_SWITCHES0, &reg);
reg &= ~(TCPC_REG_SWITCHES0_CC2_PU_EN);
reg &= ~(TCPC_REG_SWITCHES0_CC1_PU_EN);
reg |= (TCPC_REG_SWITCHES0_CC1_PD_EN);
reg |= (TCPC_REG_SWITCHES0_CC2_PD_EN);
tcpc_write(port, TCPC_REG_SWITCHES0, reg);
state[port].pulling_up = 0;
break;
case TYPEC_CC_OPEN:
/* Disable toggling */
tcpc_read(port, TCPC_REG_CONTROL2, &reg);
reg &= ~TCPC_REG_CONTROL2_TOGGLE;
tcpc_write(port, TCPC_REG_CONTROL2, reg);
/* Ensure manual switches are opened */
tcpc_read(port, TCPC_REG_SWITCHES0, &reg);
reg &= ~TCPC_REG_SWITCHES0_CC1_PU_EN;
reg &= ~TCPC_REG_SWITCHES0_CC2_PU_EN;
reg &= ~TCPC_REG_SWITCHES0_CC1_PD_EN;
reg &= ~TCPC_REG_SWITCHES0_CC2_PD_EN;
tcpc_write(port, TCPC_REG_SWITCHES0, reg);
state[port].pulling_up = 0;
break;
default:
/* Unsupported... */
return EC_ERROR_UNIMPLEMENTED;
}
return 0;
}
static int fusb302_tcpm_set_polarity(int port, int polarity) {
/* Port polarity : 0 => CC1 is CC line, 1 => CC2 is CC line */
int reg;
i2c_master_lock(tcpc_config[port].i2c_host_port);
tcpc_read(port, TCPC_REG_SWITCHES0, &reg);
/* clear VCONN switch bits */
reg &= ~TCPC_REG_SWITCHES0_VCONN_CC1;
reg &= ~TCPC_REG_SWITCHES0_VCONN_CC2;
if (state[port].vconn_enabled) {
/* set VCONN switch to be non-CC line */
if (polarity)
reg |= TCPC_REG_SWITCHES0_VCONN_CC1;
else
reg |= TCPC_REG_SWITCHES0_VCONN_CC2;
}
/* clear meas_cc bits (RX line select) */
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1;
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2;
/* set rx polarity */
if (polarity)
reg |= TCPC_REG_SWITCHES0_MEAS_CC2;
else
reg |= TCPC_REG_SWITCHES0_MEAS_CC1;
tcpc_write(port, TCPC_REG_SWITCHES0, reg);
tcpc_read(port, TCPC_REG_SWITCHES1, &reg);
/* clear tx_cc bits */
reg &= ~TCPC_REG_SWITCHES1_TXCC1_EN;
reg &= ~TCPC_REG_SWITCHES1_TXCC2_EN;
/* set tx polarity */
if (polarity)
reg |= TCPC_REG_SWITCHES1_TXCC2_EN;
else
reg |= TCPC_REG_SWITCHES1_TXCC1_EN;
tcpc_write(port, TCPC_REG_SWITCHES1, reg);
/* Save the polarity for later */
state[port].cc_polarity = polarity;
return 0;
}
static int fusb302_tcpm_set_vconn(int port, int enable) {
/*
* FUSB302 does not have dedicated VCONN Enable switch.
* We'll get through this by disabling both of the
* VCONN - CC* switches to disable, and enabling the
* saved polarity when enabling.
* Therefore at startup, tcpm_set_polarity should be called first,
* or else live with the default put into tcpm_init.
*/
int reg;
/* save enable state for later use */
state[port].vconn_enabled = enable;
if (enable) {
/* set to saved polarity */
tcpm_set_polarity(port, state[port].cc_polarity);
} else {
i2c_master_lock(tcpc_config[port].i2c_host_port);
tcpc_read(port, TCPC_REG_SWITCHES0, &reg);
/* clear VCONN switch bits */
reg &= ~TCPC_REG_SWITCHES0_VCONN_CC1;
reg &= ~TCPC_REG_SWITCHES0_VCONN_CC2;
tcpc_write(port, TCPC_REG_SWITCHES0, reg);
}
return 0;
}
static int fusb302_tcpm_set_msg_header(int port, int power_role, int data_role) {
int reg;
tcpc_read(port, TCPC_REG_SWITCHES1, &reg);
reg &= ~TCPC_REG_SWITCHES1_POWERROLE;
reg &= ~TCPC_REG_SWITCHES1_DATAROLE;
if (power_role)
reg |= TCPC_REG_SWITCHES1_POWERROLE;
if (data_role)
reg |= TCPC_REG_SWITCHES1_DATAROLE;
tcpc_write(port, TCPC_REG_SWITCHES1, reg);
return 0;
}
static int fusb302_tcpm_set_rx_enable(int port, int enable) {
int reg;
state[port].rx_enable = enable;
i2c_master_lock(tcpc_config[port].i2c_host_port);
/* Get current switch state */
tcpc_read(port, TCPC_REG_SWITCHES0, &reg);
/* Clear CC1/CC2 measure bits */
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1;
reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2;
if (enable) {
switch (state[port].cc_polarity) {
/* if CC polarity hasnt been determined, can't enable */
case -1:
return EC_ERROR_UNKNOWN;
case 0:
reg |= TCPC_REG_SWITCHES0_MEAS_CC1;
break;
case 1:
reg |= TCPC_REG_SWITCHES0_MEAS_CC2;
break;
default:
/* "shouldn't get here" */
return EC_ERROR_UNKNOWN;
}
tcpc_write(port, TCPC_REG_SWITCHES0, reg);
/* Disable BC_LVL interrupt when enabling PD comm */
if (!tcpc_read(port, TCPC_REG_MASK, &reg))
tcpc_write(port, TCPC_REG_MASK, reg | TCPC_REG_MASK_BC_LVL);
/* flush rx fifo in case messages have been coming our way */
fusb302_flush_rx_fifo(port);
} else {
tcpc_write(port, TCPC_REG_SWITCHES0, reg);
/* Enable BC_LVL interrupt when disabling PD comm */
if (!tcpc_read(port, TCPC_REG_MASK, &reg))
tcpc_write(port, TCPC_REG_MASK, reg & ~TCPC_REG_MASK_BC_LVL);
}
fusb302_auto_goodcrc_enable(port, enable);
return 0;
}
/* Return true if our Rx FIFO is empty */
static int fusb302_rx_fifo_is_empty(int port) {
int reg, ret;
i2c_master_lock(tcpc_config[port].i2c_host_port);
ret = (!tcpc_read(port, TCPC_REG_STATUS1, &reg))
&& (reg & TCPC_REG_STATUS1_RX_EMPTY);
return ret;
}
static int fusb302_tcpm_get_message(int port, uint32_t *payload, int *head) {
/*
* This is the buffer that will get the burst-read data
* from the fusb302.
*
* It's re-used in a couple different spots, the worst of which
* is the PD packet (not header) and CRC.
* maximum size necessary = 28 + 4 = 32
*/
uint8_t buf[32];
int rv, len;
/* If our FIFO is empty then we have no packet */
if (fusb302_rx_fifo_is_empty(port))
return EC_ERROR_UNKNOWN;
/* Read until we have a non-GoodCRC packet or an empty FIFO */
do {
buf[0] = TCPC_REG_FIFOS;
i2c_master_lock(tcpc_config[port].i2c_host_port);
/*
* PART 1 OF BURST READ: Write in register address.
* Issue a START, no STOP.
*/
rv = tcpc_xfer(port, buf, 1, 0, 0, I2C_XFER_START);
/*
* PART 2 OF BURST READ: Read up to the header.
* Issue a repeated START, no STOP.
* only grab three bytes so we can get the header
* and determine how many more bytes we need to read.
* TODO: Check token to ensure valid packet.
*/
rv |= tcpc_xfer(port, 0, 0, buf, 3, I2C_XFER_START);
/* Grab the header */
*head = (buf[1] & 0xFF);
*head |= ((buf[2] << 8) & 0xFF00);
/* figure out packet length, subtract header bytes */
len = get_num_bytes(*head) - 2;
/*
* PART 3 OF BURST READ: Read everything else.
* No START, but do issue a STOP at the end.
* add 4 to len to read CRC out
*/
rv |= tcpc_xfer(port, 0, 0, buf, len + 4, I2C_XFER_STOP);
} while (!rv && PACKET_IS_GOOD_CRC(*head) && !fusb302_rx_fifo_is_empty(port));
if (!rv) {
/* Discard GoodCRC packets */
if (PACKET_IS_GOOD_CRC(*head))
rv = EC_ERROR_UNKNOWN;
else
memcpy(payload, buf, len);
}
/*
* If our FIFO is non-empty then we may have a packet, we may get
* fewer interrupts than packets due to interrupt latency.
*/
//if (!fusb302_rx_fifo_is_empty(port))
// task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_RX, 0);
return rv;
}
static int fusb302_tcpm_transmit(int port, enum tcpm_transmit_type type,
uint16_t header, const uint32_t *data) {
/*
* this is the buffer that will be burst-written into the fusb302
* maximum size necessary =
* 1: FIFO register address
* 4: SOP* tokens
* 1: Token that signifies "next X bytes are not tokens"
* 30: 2 for header and up to 7*4 = 28 for rest of message
* 1: "Insert CRC" Token
* 1: EOP Token
* 1: "Turn transmitter off" token
* 1: "Star Transmission" Command
* -
* 40: 40 bytes worst-case
*/
uint8_t buf[40];
int buf_pos = 0;
int reg;
/* Flush the TXFIFO */
fusb302_flush_tx_fifo(port);
switch (type) {
case TCPC_TX_SOP:
/* put register address first for of burst tcpc write */
buf[buf_pos++] = TCPC_REG_FIFOS;
/* Write the SOP Ordered Set into TX FIFO */
buf[buf_pos++] = fusb302_TKN_SYNC1;
buf[buf_pos++] = fusb302_TKN_SYNC1;
buf[buf_pos++] = fusb302_TKN_SYNC1;
buf[buf_pos++] = fusb302_TKN_SYNC2;
fusb302_send_message(port, header, data, buf, buf_pos);
// wait for the GoodCRC to come back before we let the rest
// of the code do stuff like change polarity and miss it
delay_us(600);
return;
case TCPC_TX_HARD_RESET:
i2c_master_lock(tcpc_config[port].i2c_host_port);
/* Simply hit the SEND_HARD_RESET bit */
tcpc_read(port, TCPC_REG_CONTROL3, &reg);
reg |= TCPC_REG_CONTROL3_SEND_HARDRESET;
tcpc_write(port, TCPC_REG_CONTROL3, reg);
break;
case TCPC_TX_BIST_MODE_2:
i2c_master_lock(tcpc_config[port].i2c_host_port);
/* Hit the BIST_MODE2 bit and start TX */
tcpc_read(port, TCPC_REG_CONTROL1, &reg);
reg |= TCPC_REG_CONTROL1_BIST_MODE2;
tcpc_write(port, TCPC_REG_CONTROL1, reg);
tcpc_read(port, TCPC_REG_CONTROL0, &reg);
reg |= TCPC_REG_CONTROL0_TX_START;
tcpc_write(port, TCPC_REG_CONTROL0, reg);
//task_wait_event(PD_T_BIST_TRANSMIT);
/* Clear BIST mode bit, TX_START is self-clearing */
tcpc_read(port, TCPC_REG_CONTROL1, &reg);
reg &= ~TCPC_REG_CONTROL1_BIST_MODE2;
tcpc_write(port, TCPC_REG_CONTROL1, reg);
break;
default:
return EC_ERROR_UNIMPLEMENTED;
}
return 0;
}
#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
static int fusb302_tcpm_get_vbus_level(int port)
{
int reg;
/* Read status register */
i2c_master_lock(tcpc_config[port].i2c_host_port);
tcpc_read(port, TCPC_REG_STATUS0, &reg);
return (reg & TCPC_REG_STATUS0_VBUSOK) ? 1 : 0;
}
#endif
void fusb302_tcpc_alert(int port) {
/* interrupt has been received */
int interrupt;
int interrupta;
int interruptb;
/* reading interrupt registers clears them */
i2c_master_lock(tcpc_config[port].i2c_host_port);
tcpc_read(port, TCPC_REG_INTERRUPT, &interrupt);
tcpc_read(port, TCPC_REG_INTERRUPTA, &interrupta);
tcpc_read(port, TCPC_REG_INTERRUPTB, &interruptb);
/*
* Ignore BC_LVL changes when transmitting / receiving PD,
* since CC level will constantly change.
*/
if (state[port].rx_enable)
interrupt &= ~TCPC_REG_INTERRUPT_BC_LVL;
if (interrupt & TCPC_REG_INTERRUPT_BC_LVL) {
/* CC Status change */
//task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0);
}
if (interrupt & TCPC_REG_INTERRUPT_COLLISION) {
/* packet sending collided */
pd_transmit_complete(port, TCPC_TX_COMPLETE_FAILED);
}
/* GoodCRC was received, our FIFO is now non-empty */
if (interrupta & TCPC_REG_INTERRUPTA_TX_SUCCESS) {
//task_set_event(PD_PORT_TO_TASK_ID(port),
// PD_EVENT_RX, 0);
pd_transmit_complete(port, TCPC_TX_COMPLETE_SUCCESS);
}
if (interrupta & TCPC_REG_INTERRUPTA_RETRYFAIL) {
/* all retries have failed to get a GoodCRC */
pd_transmit_complete(port, TCPC_TX_COMPLETE_FAILED);
}
if (interrupta & TCPC_REG_INTERRUPTA_HARDSENT) {
/* hard reset has been sent */
/* bring FUSB302 out of reset */
fusb302_pd_reset(port);
pd_transmit_complete(port, TCPC_TX_COMPLETE_SUCCESS);
}
if (interrupta & TCPC_REG_INTERRUPTA_HARDRESET) {
/* hard reset has been received */
/* bring FUSB302 out of reset */
fusb302_pd_reset(port);
pd_execute_hard_reset(port);
//task_wake(PD_PORT_TO_TASK_ID(port));
}
if (interruptb & TCPC_REG_INTERRUPTB_GCRCSENT) {
/* Packet received and GoodCRC sent */
/* (this interrupt fires after the GoodCRC finishes) */
if (state[port].rx_enable) {
//task_set_event(PD_PORT_TO_TASK_ID(port),
// PD_EVENT_RX, 0);
} else {
/* flush rx fifo if rx isn't enabled */
fusb302_flush_rx_fifo(port);
}
}
}
/* For BIST receiving */
void tcpm_set_bist_test_data(int port) {
int reg;
i2c_master_lock(tcpc_config[port].i2c_host_port);
/* Read control3 register */
tcpc_read(port, TCPC_REG_CONTROL3, &reg);
/* Set the BIST_TMODE bit (Clears on Hard Reset) */
reg |= TCPC_REG_CONTROL3_BIST_TMODE;
/* Write the updated value */
tcpc_write(port, TCPC_REG_CONTROL3, reg);
}
const struct tcpm_drv fusb302_tcpm_drv = { .init = &fusb302_tcpm_init,
.release = &fusb302_tcpm_release, .get_cc = &fusb302_tcpm_get_cc,
#ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC
.get_vbus_level = &fusb302_tcpm_get_vbus_level,
#endif
.select_rp_value = &fusb302_tcpm_select_rp_value, .set_cc =
&fusb302_tcpm_set_cc,
.set_polarity = &fusb302_tcpm_set_polarity, .set_vconn =
&fusb302_tcpm_set_vconn, .set_msg_header =
&fusb302_tcpm_set_msg_header, .set_rx_enable =
&fusb302_tcpm_set_rx_enable, .get_message =
&fusb302_tcpm_get_message, .transmit = &fusb302_tcpm_transmit,
.tcpc_alert = &fusb302_tcpc_alert, };

View File

@@ -0,0 +1,250 @@
/*
FUSB302.h - Library for interacting with the FUSB302B chip.
Copyright 2010 The Chromium OS Authors
Copyright 2017 Jason Cerundolo
Released under an MIT license. See LICENSE file.
*/
#ifndef fusb302_H
#define fusb302_H
#include <stdint.h>
#include "usb_pd_tcpm.h"
#include "USBC_PD/usb_pd.h"
/* Chip Device ID - 302A or 302B */
#define fusb302_DEVID_302A 0x08
#define fusb302_DEVID_302B 0x09
/* I2C slave address varies by part number */
/* FUSB302BUCX / FUSB302BMPX */
#define fusb302_I2C_SLAVE_ADDR 0x22 // 7-bit address for Arduino
/* FUSB302B01MPX */
#define fusb302_I2C_SLAVE_ADDR_B01 0x23
/* FUSB302B10MPX */
#define fusb302_I2C_SLAVE_ADDR_B10 0x24
/* FUSB302B11MPX */
#define fusb302_I2C_SLAVE_ADDR_B11 0x25
/* Default retry count for transmitting */
#define PD_RETRY_COUNT 3
/* Time to wait for TCPC to complete transmit */
#define PD_T_TCPC_TX_TIMEOUT (100*MSEC)
#define TCPC_REG_DEVICE_ID 0x01
#define TCPC_REG_SWITCHES0 0x02
#define TCPC_REG_SWITCHES0_CC2_PU_EN (1<<7)
#define TCPC_REG_SWITCHES0_CC1_PU_EN (1<<6)
#define TCPC_REG_SWITCHES0_VCONN_CC2 (1<<5)
#define TCPC_REG_SWITCHES0_VCONN_CC1 (1<<4)
#define TCPC_REG_SWITCHES0_MEAS_CC2 (1<<3)
#define TCPC_REG_SWITCHES0_MEAS_CC1 (1<<2)
#define TCPC_REG_SWITCHES0_CC2_PD_EN (1<<1)
#define TCPC_REG_SWITCHES0_CC1_PD_EN (1<<0)
#define TCPC_REG_SWITCHES1 0x03
#define TCPC_REG_SWITCHES1_POWERROLE (1<<7)
#define TCPC_REG_SWITCHES1_SPECREV1 (1<<6)
#define TCPC_REG_SWITCHES1_SPECREV0 (1<<5)
#define TCPC_REG_SWITCHES1_DATAROLE (1<<4)
#define TCPC_REG_SWITCHES1_AUTO_GCRC (1<<2)
#define TCPC_REG_SWITCHES1_TXCC2_EN (1<<1)
#define TCPC_REG_SWITCHES1_TXCC1_EN (1<<0)
#define TCPC_REG_MEASURE 0x04
#define TCPC_REG_MEASURE_VBUS (1<<6)
#define TCPC_REG_MEASURE_MDAC_MV(mv) (((mv)/42) & 0x3f)
#define TCPC_REG_CONTROL0 0x06
#define TCPC_REG_CONTROL0_TX_FLUSH (1<<6)
#define TCPC_REG_CONTROL0_INT_MASK (1<<5)
#define TCPC_REG_CONTROL0_HOST_CUR_MASK (3<<2)
#define TCPC_REG_CONTROL0_HOST_CUR_3A0 (3<<2)
#define TCPC_REG_CONTROL0_HOST_CUR_1A5 (2<<2)
#define TCPC_REG_CONTROL0_HOST_CUR_USB (1<<2)
#define TCPC_REG_CONTROL0_TX_START (1<<0)
#define TCPC_REG_CONTROL1 0x07
#define TCPC_REG_CONTROL1_ENSOP2DB (1<<6)
#define TCPC_REG_CONTROL1_ENSOP1DB (1<<5)
#define TCPC_REG_CONTROL1_BIST_MODE2 (1<<4)
#define TCPC_REG_CONTROL1_RX_FLUSH (1<<2)
#define TCPC_REG_CONTROL1_ENSOP2 (1<<1)
#define TCPC_REG_CONTROL1_ENSOP1 (1<<0)
#define TCPC_REG_CONTROL2 0x08
/* two-bit field, valid values below */
#define TCPC_REG_CONTROL2_MODE (1<<1)
#define TCPC_REG_CONTROL2_MODE_DFP (0x3)
#define TCPC_REG_CONTROL2_MODE_UFP (0x2)
#define TCPC_REG_CONTROL2_MODE_DRP (0x1)
#define TCPC_REG_CONTROL2_MODE_POS (1)
#define TCPC_REG_CONTROL2_TOGGLE (1<<0)
#define TCPC_REG_CONTROL3 0x09
#define TCPC_REG_CONTROL3_SEND_HARDRESET (1<<6)
#define TCPC_REG_CONTROL3_BIST_TMODE (1<<5) /* 302B Only */
#define TCPC_REG_CONTROL3_AUTO_HARDRESET (1<<4)
#define TCPC_REG_CONTROL3_AUTO_SOFTRESET (1<<3)
/* two-bit field */
#define TCPC_REG_CONTROL3_N_RETRIES (1<<1)
#define TCPC_REG_CONTROL3_N_RETRIES_POS (1)
#define TCPC_REG_CONTROL3_N_RETRIES_SIZE (2)
#define TCPC_REG_CONTROL3_AUTO_RETRY (1<<0)
#define TCPC_REG_MASK 0x0A
#define TCPC_REG_MASK_VBUSOK (1<<7)
#define TCPC_REG_MASK_ACTIVITY (1<<6)
#define TCPC_REG_MASK_COMP_CHNG (1<<5)
#define TCPC_REG_MASK_CRC_CHK (1<<4)
#define TCPC_REG_MASK_ALERT (1<<3)
#define TCPC_REG_MASK_WAKE (1<<2)
#define TCPC_REG_MASK_COLLISION (1<<1)
#define TCPC_REG_MASK_BC_LVL (1<<0)
#define TCPC_REG_POWER 0x0B
#define TCPC_REG_POWER_PWR (1<<0) /* four-bit field */
#define TCPC_REG_POWER_PWR_LOW 0x1 /* Bandgap + Wake circuitry */
#define TCPC_REG_POWER_PWR_MEDIUM 0x3 /* LOW + Receiver + Current refs */
#define TCPC_REG_POWER_PWR_HIGH 0x7 /* MEDIUM + Measure block */
#define TCPC_REG_POWER_PWR_ALL 0xF /* HIGH + Internal Oscillator */
#define TCPC_REG_RESET 0x0C
#define TCPC_REG_RESET_PD_RESET (1<<1)
#define TCPC_REG_RESET_SW_RESET (1<<0)
#define TCPC_REG_MASKA 0x0E
#define TCPC_REG_MASKA_OCP_TEMP (1<<7)
#define TCPC_REG_MASKA_TOGDONE (1<<6)
#define TCPC_REG_MASKA_SOFTFAIL (1<<5)
#define TCPC_REG_MASKA_RETRYFAIL (1<<4)
#define TCPC_REG_MASKA_HARDSENT (1<<3)
#define TCPC_REG_MASKA_TX_SUCCESS (1<<2)
#define TCPC_REG_MASKA_SOFTRESET (1<<1)
#define TCPC_REG_MASKA_HARDRESET (1<<0)
#define TCPC_REG_MASKB 0x0F
#define TCPC_REG_MASKB_GCRCSENT (1<<0)
#define TCPC_REG_STATUS0A 0x3C
#define TCPC_REG_STATUS0A_SOFTFAIL (1<<5)
#define TCPC_REG_STATUS0A_RETRYFAIL (1<<4)
#define TCPC_REG_STATUS0A_POWER (1<<2) /* two-bit field */
#define TCPC_REG_STATUS0A_RX_SOFT_RESET (1<<1)
#define TCPC_REG_STATUS0A_RX_HARD_RESET (1<<0)
#define TCPC_REG_STATUS1A 0x3D
/* three-bit field, valid values below */
#define TCPC_REG_STATUS1A_TOGSS (1<<3)
#define TCPC_REG_STATUS1A_TOGSS_RUNNING 0x0
#define TCPC_REG_STATUS1A_TOGSS_SRC1 0x1
#define TCPC_REG_STATUS1A_TOGSS_SRC2 0x2
#define TCPC_REG_STATUS1A_TOGSS_SNK1 0x5
#define TCPC_REG_STATUS1A_TOGSS_SNK2 0x6
#define TCPC_REG_STATUS1A_TOGSS_AA 0x7
#define TCPC_REG_STATUS1A_TOGSS_POS (3)
#define TCPC_REG_STATUS1A_TOGSS_MASK (0x7)
#define TCPC_REG_STATUS1A_RXSOP2DB (1<<2)
#define TCPC_REG_STATUS1A_RXSOP1DB (1<<1)
#define TCPC_REG_STATUS1A_RXSOP (1<<0)
#define TCPC_REG_INTERRUPTA 0x3E
#define TCPC_REG_INTERRUPTA_OCP_TEMP (1<<7)
#define TCPC_REG_INTERRUPTA_TOGDONE (1<<6)
#define TCPC_REG_INTERRUPTA_SOFTFAIL (1<<5)
#define TCPC_REG_INTERRUPTA_RETRYFAIL (1<<4)
#define TCPC_REG_INTERRUPTA_HARDSENT (1<<3)
#define TCPC_REG_INTERRUPTA_TX_SUCCESS (1<<2)
#define TCPC_REG_INTERRUPTA_SOFTRESET (1<<1)
#define TCPC_REG_INTERRUPTA_HARDRESET (1<<0)
#define TCPC_REG_INTERRUPTB 0x3F
#define TCPC_REG_INTERRUPTB_GCRCSENT (1<<0)
#define TCPC_REG_STATUS0 0x40
#define TCPC_REG_STATUS0_VBUSOK (1<<7)
#define TCPC_REG_STATUS0_ACTIVITY (1<<6)
#define TCPC_REG_STATUS0_COMP (1<<5)
#define TCPC_REG_STATUS0_CRC_CHK (1<<4)
#define TCPC_REG_STATUS0_ALERT (1<<3)
#define TCPC_REG_STATUS0_WAKE (1<<2)
#define TCPC_REG_STATUS0_BC_LVL1 (1<<1) /* two-bit field */
#define TCPC_REG_STATUS0_BC_LVL0 (1<<0) /* two-bit field */
#define TCPC_REG_STATUS1 0x41
#define TCPC_REG_STATUS1_RXSOP2 (1<<7)
#define TCPC_REG_STATUS1_RXSOP1 (1<<6)
#define TCPC_REG_STATUS1_RX_EMPTY (1<<5)
#define TCPC_REG_STATUS1_RX_FULL (1<<4)
#define TCPC_REG_STATUS1_TX_EMPTY (1<<3)
#define TCPC_REG_STATUS1_TX_FULL (1<<2)
#define TCPC_REG_INTERRUPT 0x42
#define TCPC_REG_INTERRUPT_VBUSOK (1<<7)
#define TCPC_REG_INTERRUPT_ACTIVITY (1<<6)
#define TCPC_REG_INTERRUPT_COMP_CHNG (1<<5)
#define TCPC_REG_INTERRUPT_CRC_CHK (1<<4)
#define TCPC_REG_INTERRUPT_ALERT (1<<3)
#define TCPC_REG_INTERRUPT_WAKE (1<<2)
#define TCPC_REG_INTERRUPT_COLLISION (1<<1)
#define TCPC_REG_INTERRUPT_BC_LVL (1<<0)
#define TCPC_REG_FIFOS 0x43
/* Tokens defined for the FUSB302 TX FIFO */
enum fusb302_txfifo_tokens {
fusb302_TKN_TXON = 0xA1,
fusb302_TKN_SYNC1 = 0x12,
fusb302_TKN_SYNC2 = 0x13,
fusb302_TKN_SYNC3 = 0x1B,
fusb302_TKN_RST1 = 0x15,
fusb302_TKN_RST2 = 0x16,
fusb302_TKN_PACKSYM = 0x80,
fusb302_TKN_JAMCRC = 0xFF,
fusb302_TKN_EOP = 0x14,
fusb302_TKN_TXOFF = 0xFE,
};
extern const struct tcpm_drv fusb302_tcpm_drv;
/*
// Common methods for TCPM implementations
int fusb302_init(void);
int fusb302_get_cc(int *cc1, int *cc2);
int fusb302_get_vbus_level(void);
int fusb302_select_rp_value(int rp);
int fusb302_set_cc(int pull);
int fusb302_set_polarity(int polarity);
int fusb302_set_vconn(int enable);
int fusb302_set_msg_header(int power_role, int data_role);
int fusb302_set_rx_enable(int enable);
int fusb302_get_message(uint32_t *payload, int *head);
int fusb302_transmit(enum tcpm_transmit_type type,
uint16_t header, const uint32_t *data);
//int alert(void);
void fusb302_pd_reset(int port);
void fusb302_auto_goodcrc_enable(int enable);
int fusb302_convert_bc_lvl(int bc_lvl);
void fusb302_detect_cc_pin_source_manual(int *cc1_lvl, int *cc2_lvl);
int fusb302_measure_cc_pin_source(int cc_measure);
void fusb302_detect_cc_pin_sink(int *cc1, int *cc2);
int fusb302_send_message(uint16_t header, const uint32_t *data,
uint8_t *buf, int buf_pos);
void fusb302_flush_rx_fifo(int port);
void fusb302_flush_tx_fifo(int port);
void fusb302_clear_int_pin(void);
void fusb302_set_bist_test_data(void);
int fusb302_get_chip_id(int *id);
uint32_t fusb302_get_interrupt_reason(void);
int fusb302_tcpc_write(int reg, int val);
int fusb302_tcpc_read(int reg, int *val);
int fusb302_tcpc_xfer(const uint8_t *out,
int out_size, uint8_t *in,
int in_size, int flags);
*/
#endif /* fusb302_H */

View File

@@ -1,40 +0,0 @@
/*
* FUSB302.hpp
*
* Created on: 11-06-2020
* Author: Ralim
*/
#ifndef FUSB302_H_
#define FUSB302_H_
#include "BSP.h"
#include "FUSB302_includes.h"
#include "I2C_Wrapper.hpp"
//While the ST4500 is nice, the FUSB302 is _cheap_ so its what is used in the TS80P for example
class FUSB302 {
public:
//Returns true if the FUSB302 is detected on the I2C bus
bool detect();
private:
// Bring up out of reset and clear fifo's
void fusb302_reset();
void flush_rx_fifo();
void flush_tx_fifo();
void enable_auto_good_crc();
int set_rp_value(int rp);
int set_cc(int pull);
struct fusb302_chip_state {
int cc_polarity;
int vconn_enabled;
/* 1 = pulling up (DFP) 0 = pulling down (UFP) */
int pulling_up;
int rx_enable;
uint8_t mdac_vnc;
uint8_t mdac_rd;
};
fusb302_chip_state state;
};
#endif /* LIS2DH12_HPP_ */

View File

@@ -1,204 +0,0 @@
#ifndef FUSB302_INCLUDES_H_
#define FUSB302_INCLUDES_H_
/* Whole bunch of includes to make working with the FUSB302 easier*/
/* Chip Device ID - 302A or 302B */
#define fusb302_DEVID_302A 0x08
#define fusb302_DEVID_302B 0x09
/* I2C slave address varies by part number */
/* FUSB302BUCX / FUSB302BMPX */
#define fusb302_I2C_SLAVE_ADDR 0x22 // 7-bit address for Arduino
/* FUSB302B01MPX */
#define fusb302_I2C_SLAVE_ADDR_B01 0x23
/* FUSB302B10MPX */
#define fusb302_I2C_SLAVE_ADDR_B10 0x24
/* FUSB302B11MPX */
#define fusb302_I2C_SLAVE_ADDR_B11 0x25
/* Default retry count for transmitting */
#define PD_RETRY_COUNT 3
/* Time to wait for TCPC to complete transmit */
#define PD_T_TCPC_TX_TIMEOUT (100 * MSEC)
#define TCPC_REG_DEVICE_ID 0x01
#define TCPC_REG_SWITCHES0 0x02
#define TCPC_REG_SWITCHES0_CC2_PU_EN (1 << 7)
#define TCPC_REG_SWITCHES0_CC1_PU_EN (1 << 6)
#define TCPC_REG_SWITCHES0_VCONN_CC2 (1 << 5)
#define TCPC_REG_SWITCHES0_VCONN_CC1 (1 << 4)
#define TCPC_REG_SWITCHES0_MEAS_CC2 (1 << 3)
#define TCPC_REG_SWITCHES0_MEAS_CC1 (1 << 2)
#define TCPC_REG_SWITCHES0_CC2_PD_EN (1 << 1)
#define TCPC_REG_SWITCHES0_CC1_PD_EN (1 << 0)
#define TCPC_REG_SWITCHES1 0x03
#define TCPC_REG_SWITCHES1_POWERROLE (1 << 7)
#define TCPC_REG_SWITCHES1_SPECREV1 (1 << 6)
#define TCPC_REG_SWITCHES1_SPECREV0 (1 << 5)
#define TCPC_REG_SWITCHES1_DATAROLE (1 << 4)
#define TCPC_REG_SWITCHES1_AUTO_GCRC (1 << 2)
#define TCPC_REG_SWITCHES1_TXCC2_EN (1 << 1)
#define TCPC_REG_SWITCHES1_TXCC1_EN (1 << 0)
#define TCPC_REG_MEASURE 0x04
#define TCPC_REG_MEASURE_VBUS (1 << 6)
#define TCPC_REG_MEASURE_MDAC_MV(mv) (((mv) / 42) & 0x3f)
#define TCPC_REG_CONTROL0 0x06
#define TCPC_REG_CONTROL0_TX_FLUSH (1 << 6)
#define TCPC_REG_CONTROL0_INT_MASK (1 << 5)
#define TCPC_REG_CONTROL0_HOST_CUR_MASK (3 << 2)
#define TCPC_REG_CONTROL0_HOST_CUR_3A0 (3 << 2)
#define TCPC_REG_CONTROL0_HOST_CUR_1A5 (2 << 2)
#define TCPC_REG_CONTROL0_HOST_CUR_USB (1 << 2)
#define TCPC_REG_CONTROL0_TX_START (1 << 0)
#define TCPC_REG_CONTROL1 0x07
#define TCPC_REG_CONTROL1_ENSOP2DB (1 << 6)
#define TCPC_REG_CONTROL1_ENSOP1DB (1 << 5)
#define TCPC_REG_CONTROL1_BIST_MODE2 (1 << 4)
#define TCPC_REG_CONTROL1_RX_FLUSH (1 << 2)
#define TCPC_REG_CONTROL1_ENSOP2 (1 << 1)
#define TCPC_REG_CONTROL1_ENSOP1 (1 << 0)
#define TCPC_REG_CONTROL2 0x08
/* two-bit field, valid values below */
#define TCPC_REG_CONTROL2_MODE (1 << 1)
#define TCPC_REG_CONTROL2_MODE_DFP (0x3)
#define TCPC_REG_CONTROL2_MODE_UFP (0x2)
#define TCPC_REG_CONTROL2_MODE_DRP (0x1)
#define TCPC_REG_CONTROL2_MODE_POS (1)
#define TCPC_REG_CONTROL2_TOGGLE (1 << 0)
#define TCPC_REG_CONTROL3 0x09
#define TCPC_REG_CONTROL3_SEND_HARDRESET (1 << 6)
#define TCPC_REG_CONTROL3_BIST_TMODE (1 << 5) /* 302B Only */
#define TCPC_REG_CONTROL3_AUTO_HARDRESET (1 << 4)
#define TCPC_REG_CONTROL3_AUTO_SOFTRESET (1 << 3)
/* two-bit field */
#define TCPC_REG_CONTROL3_N_RETRIES (1 << 1)
#define TCPC_REG_CONTROL3_N_RETRIES_POS (1)
#define TCPC_REG_CONTROL3_N_RETRIES_SIZE (2)
#define TCPC_REG_CONTROL3_AUTO_RETRY (1 << 0)
#define TCPC_REG_MASK 0x0A
#define TCPC_REG_MASK_VBUSOK (1 << 7)
#define TCPC_REG_MASK_ACTIVITY (1 << 6)
#define TCPC_REG_MASK_COMP_CHNG (1 << 5)
#define TCPC_REG_MASK_CRC_CHK (1 << 4)
#define TCPC_REG_MASK_ALERT (1 << 3)
#define TCPC_REG_MASK_WAKE (1 << 2)
#define TCPC_REG_MASK_COLLISION (1 << 1)
#define TCPC_REG_MASK_BC_LVL (1 << 0)
#define TCPC_REG_POWER 0x0B
#define TCPC_REG_POWER_PWR (1 << 0) /* four-bit field */
#define TCPC_REG_POWER_PWR_LOW 0x1 /* Bandgap + Wake circuitry */
#define TCPC_REG_POWER_PWR_MEDIUM 0x3 /* LOW + Receiver + Current refs */
#define TCPC_REG_POWER_PWR_HIGH 0x7 /* MEDIUM + Measure block */
#define TCPC_REG_POWER_PWR_ALL 0xF /* HIGH + Internal Oscillator */
#define TCPC_REG_RESET 0x0C
#define TCPC_REG_RESET_PD_RESET (1 << 1)
#define TCPC_REG_RESET_SW_RESET (1 << 0)
#define TCPC_REG_MASKA 0x0E
#define TCPC_REG_MASKA_OCP_TEMP (1 << 7)
#define TCPC_REG_MASKA_TOGDONE (1 << 6)
#define TCPC_REG_MASKA_SOFTFAIL (1 << 5)
#define TCPC_REG_MASKA_RETRYFAIL (1 << 4)
#define TCPC_REG_MASKA_HARDSENT (1 << 3)
#define TCPC_REG_MASKA_TX_SUCCESS (1 << 2)
#define TCPC_REG_MASKA_SOFTRESET (1 << 1)
#define TCPC_REG_MASKA_HARDRESET (1 << 0)
#define TCPC_REG_MASKB 0x0F
#define TCPC_REG_MASKB_GCRCSENT (1 << 0)
#define TCPC_REG_STATUS0A 0x3C
#define TCPC_REG_STATUS0A_SOFTFAIL (1 << 5)
#define TCPC_REG_STATUS0A_RETRYFAIL (1 << 4)
#define TCPC_REG_STATUS0A_POWER (1 << 2) /* two-bit field */
#define TCPC_REG_STATUS0A_RX_SOFT_RESET (1 << 1)
#define TCPC_REG_STATUS0A_RX_HARD_RESET (1 << 0)
#define TCPC_REG_STATUS1A 0x3D
/* three-bit field, valid values below */
#define TCPC_REG_STATUS1A_TOGSS (1 << 3)
#define TCPC_REG_STATUS1A_TOGSS_RUNNING 0x0
#define TCPC_REG_STATUS1A_TOGSS_SRC1 0x1
#define TCPC_REG_STATUS1A_TOGSS_SRC2 0x2
#define TCPC_REG_STATUS1A_TOGSS_SNK1 0x5
#define TCPC_REG_STATUS1A_TOGSS_SNK2 0x6
#define TCPC_REG_STATUS1A_TOGSS_AA 0x7
#define TCPC_REG_STATUS1A_TOGSS_POS (3)
#define TCPC_REG_STATUS1A_TOGSS_MASK (0x7)
#define TCPC_REG_STATUS1A_RXSOP2DB (1 << 2)
#define TCPC_REG_STATUS1A_RXSOP1DB (1 << 1)
#define TCPC_REG_STATUS1A_RXSOP (1 << 0)
#define TCPC_REG_INTERRUPTA 0x3E
#define TCPC_REG_INTERRUPTA_OCP_TEMP (1 << 7)
#define TCPC_REG_INTERRUPTA_TOGDONE (1 << 6)
#define TCPC_REG_INTERRUPTA_SOFTFAIL (1 << 5)
#define TCPC_REG_INTERRUPTA_RETRYFAIL (1 << 4)
#define TCPC_REG_INTERRUPTA_HARDSENT (1 << 3)
#define TCPC_REG_INTERRUPTA_TX_SUCCESS (1 << 2)
#define TCPC_REG_INTERRUPTA_SOFTRESET (1 << 1)
#define TCPC_REG_INTERRUPTA_HARDRESET (1 << 0)
#define TCPC_REG_INTERRUPTB 0x3F
#define TCPC_REG_INTERRUPTB_GCRCSENT (1 << 0)
#define TCPC_REG_STATUS0 0x40
#define TCPC_REG_STATUS0_VBUSOK (1 << 7)
#define TCPC_REG_STATUS0_ACTIVITY (1 << 6)
#define TCPC_REG_STATUS0_COMP (1 << 5)
#define TCPC_REG_STATUS0_CRC_CHK (1 << 4)
#define TCPC_REG_STATUS0_ALERT (1 << 3)
#define TCPC_REG_STATUS0_WAKE (1 << 2)
#define TCPC_REG_STATUS0_BC_LVL1 (1 << 1) /* two-bit field */
#define TCPC_REG_STATUS0_BC_LVL0 (1 << 0) /* two-bit field */
#define TCPC_REG_STATUS1 0x41
#define TCPC_REG_STATUS1_RXSOP2 (1 << 7)
#define TCPC_REG_STATUS1_RXSOP1 (1 << 6)
#define TCPC_REG_STATUS1_RX_EMPTY (1 << 5)
#define TCPC_REG_STATUS1_RX_FULL (1 << 4)
#define TCPC_REG_STATUS1_TX_EMPTY (1 << 3)
#define TCPC_REG_STATUS1_TX_FULL (1 << 2)
#define TCPC_REG_INTERRUPT 0x42
#define TCPC_REG_INTERRUPT_VBUSOK (1 << 7)
#define TCPC_REG_INTERRUPT_ACTIVITY (1 << 6)
#define TCPC_REG_INTERRUPT_COMP_CHNG (1 << 5)
#define TCPC_REG_INTERRUPT_CRC_CHK (1 << 4)
#define TCPC_REG_INTERRUPT_ALERT (1 << 3)
#define TCPC_REG_INTERRUPT_WAKE (1 << 2)
#define TCPC_REG_INTERRUPT_COLLISION (1 << 1)
#define TCPC_REG_INTERRUPT_BC_LVL (1 << 0)
#define TCPC_REG_FIFOS 0x43
/* Tokens defined for the FUSB302 TX FIFO */
enum fusb302_txfifo_tokens {
fusb302_TKN_TXON = 0xA1,
fusb302_TKN_SYNC1 = 0x12,
fusb302_TKN_SYNC2 = 0x13,
fusb302_TKN_SYNC3 = 0x1B,
fusb302_TKN_RST1 = 0x15,
fusb302_TKN_RST2 = 0x16,
fusb302_TKN_PACKSYM = 0x80,
fusb302_TKN_JAMCRC = 0xFF,
fusb302_TKN_EOP = 0x14,
fusb302_TKN_TXOFF = 0xFE,
};
#define PACKET_IS_GOOD_CRC(head) (PD_HEADER_TYPE(head) == PD_CTRL_GOOD_CRC && \
PD_HEADER_CNT(head) == 0)
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,276 @@
/* Copyright 2015 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 - common header for TCPM drivers */
#ifndef __CROS_EC_USB_PD_TCPM_TCPM_H
#define __CROS_EC_USB_PD_TCPM_TCPM_H
#include "tcpm_driver.h"
#include "usb_pd_tcpm.h"
#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && \
!defined(CONFIG_USB_PD_DUAL_ROLE)
#error "DRP auto toggle requires board to have DRP support"
#error "Please upgrade your board configuration"
#endif
#ifndef CONFIG_USB_PD_TCPC
extern const struct tcpc_config_t tcpc_config[];
/* I2C wrapper functions - get I2C port / slave addr from config struct. */
int tcpc_write(int port, int reg, int val);
int tcpc_write16(int port, int reg, int val);
int tcpc_read(int port, int reg, int *val);
int tcpc_read16(int port, int reg, int *val);
int tcpc_xfer(int port,
const uint8_t *out, int out_size,
uint8_t *in, int in_size,
int flags);
/* TCPM driver wrapper function */
static inline int tcpm_init(int port)
{
int rv;
rv = tcpc_config[port].drv->init(port);
if (rv)
return rv;
/* Board specific post TCPC init */
if (board_tcpc_post_init)
rv = board_tcpc_post_init(port);
return rv;
}
static inline int tcpm_release(int port)
{
return tcpc_config[port].drv->release(port);
}
static inline int tcpm_get_cc(int port, int *cc1, int *cc2)
{
return tcpc_config[port].drv->get_cc(port, cc1, cc2);
}
static inline int tcpm_get_vbus_level(int port)
{
return tcpc_config[port].drv->get_vbus_level(port);
}
static inline int tcpm_select_rp_value(int port, int rp)
{
return tcpc_config[port].drv->select_rp_value(port, rp);
}
static inline int tcpm_set_cc(int port, int pull)
{
return tcpc_config[port].drv->set_cc(port, pull);
}
static inline int tcpm_set_polarity(int port, int polarity)
{
return tcpc_config[port].drv->set_polarity(port, polarity);
}
static inline int tcpm_set_vconn(int port, int enable)
{
return tcpc_config[port].drv->set_vconn(port, enable);
}
static inline int tcpm_set_msg_header(int port, int power_role, int data_role)
{
return tcpc_config[port].drv->set_msg_header(port, power_role,
data_role);
}
static inline int tcpm_set_rx_enable(int port, int enable)
{
return tcpc_config[port].drv->set_rx_enable(port, enable);
}
static inline int tcpm_get_message(int port, uint32_t *payload, int *head)
{
return tcpc_config[port].drv->get_message(port, payload, head);
}
static inline int tcpm_transmit(int port, enum tcpm_transmit_type type,
uint16_t header, const uint32_t *data)
{
return tcpc_config[port].drv->transmit(port, type, header, data);
}
static inline void tcpc_alert(int port)
{
tcpc_config[port].drv->tcpc_alert(port);
}
static inline void tcpc_discharge_vbus(int port, int enable)
{
tcpc_config[port].drv->tcpc_discharge_vbus(port, enable);
}
#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
static inline int tcpm_auto_toggle_supported(int port)
{
return !!tcpc_config[port].drv->drp_toggle;
}
static inline int tcpm_set_drp_toggle(int port, int enable)
{
return tcpc_config[port].drv->drp_toggle(port, enable);
}
#endif
#ifdef CONFIG_CMD_I2C_STRESS_TEST_TCPC
static inline int tcpc_i2c_read(const int port, const int addr,
const int reg, int *data)
{
return tcpc_read(port, reg, data);
}
static inline int tcpc_i2c_write(const int port, const int addr,
const int reg, int data)
{
return tcpc_write(port, reg, data);
}
#endif
static inline int tcpm_get_chip_info(int port, int renew,
struct ec_response_pd_chip_info **info)
{
if (tcpc_config[port].drv->get_chip_info)
return tcpc_config[port].drv->get_chip_info(port, renew, info);
return EC_ERROR_UNIMPLEMENTED;
}
#else
/**
* Initialize TCPM driver and wait for TCPC readiness.
*
* @param port Type-C port number
*
* @return EC_SUCCESS or error
*/
int tcpm_init(int port);
/**
* Read the CC line status.
*
* @param port Type-C port number
* @param cc1 pointer to CC status for CC1
* @param cc2 pointer to CC status for CC2
*
* @return EC_SUCCESS or error
*/
int tcpm_get_cc(int port, int *cc1, int *cc2);
/**
* Read VBUS
*
* @param port Type-C port number
*
* @return 0 => VBUS not detected, 1 => VBUS detected
*/
int tcpm_get_vbus_level(int port);
/**
* Set the value of the CC pull-up used when we are a source.
*
* @param port Type-C port number
* @param rp One of enum tcpc_rp_value
*
* @return EC_SUCCESS or error
*/
int tcpm_select_rp_value(int port, int rp);
/**
* Set the CC pull resistor. This sets our role as either source or sink.
*
* @param port Type-C port number
* @param pull One of enum tcpc_cc_pull
*
* @return EC_SUCCESS or error
*/
int tcpm_set_cc(int port, int pull);
/**
* Set polarity
*
* @param port Type-C port number
* @param polarity 0=> transmit on CC1, 1=> transmit on CC2
*
* @return EC_SUCCESS or error
*/
int tcpm_set_polarity(int port, int polarity);
/**
* Set Vconn.
*
* @param port Type-C port number
* @param polarity Polarity of the CC line to read
*
* @return EC_SUCCESS or error
*/
int tcpm_set_vconn(int port, int enable);
/**
* Set PD message header to use for goodCRC
*
* @param port Type-C port number
* @param power_role Power role to use in header
* @param data_role Data role to use in header
*
* @return EC_SUCCESS or error
*/
int tcpm_set_msg_header(int port, int power_role, int data_role);
/**
* Set RX enable flag
*
* @param port Type-C port number
* @enable true for enable, false for disable
*
* @return EC_SUCCESS or error
*/
int tcpm_set_rx_enable(int port, int enable);
/**
* Read last received PD message.
*
* @param port Type-C port number
* @param payload Pointer to location to copy payload of message
* @param header of message
*
* @return EC_SUCCESS or error
*/
int tcpm_get_message(int port, uint32_t *payload, int *head);
/**
* Transmit PD message
*
* @param port Type-C port number
* @param type Transmit type
* @param header Packet header
* @param cnt Number of bytes in payload
* @param data Payload
*
* @return EC_SUCCESS or error
*/
int tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header,
const uint32_t *data);
/**
* TCPC is asserting alert
*
* @param port Type-C port number
*/
void tcpc_alert(int port);
#endif
#endif

View File

@@ -0,0 +1,354 @@
/* Copyright 2015 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_USB_PD_TCPM_H
#define __CROS_EC_USB_PD_TCPM_H
/* List of common error codes that can be returned */
enum ec_error_list {
/* Success - no error */
EC_SUCCESS = 0,
/* Unknown error */
EC_ERROR_UNKNOWN = 1,
/* Function not implemented yet */
EC_ERROR_UNIMPLEMENTED = 2,
/* Overflow error; too much input provided. */
EC_ERROR_OVERFLOW = 3,
/* Timeout */
EC_ERROR_TIMEOUT = 4,
/* Invalid argument */
EC_ERROR_INVAL = 5,
/* Already in use, or not ready yet */
EC_ERROR_BUSY = 6,
/* Access denied */
EC_ERROR_ACCESS_DENIED = 7,
/* Failed because component does not have power */
EC_ERROR_NOT_POWERED = 8,
/* Failed because component is not calibrated */
EC_ERROR_NOT_CALIBRATED = 9,
/* Failed because CRC error */
EC_ERROR_CRC = 10,
/* Invalid console command param (PARAMn means parameter n is bad) */
EC_ERROR_PARAM1 = 11,
EC_ERROR_PARAM2 = 12,
EC_ERROR_PARAM3 = 13,
EC_ERROR_PARAM4 = 14,
EC_ERROR_PARAM5 = 15,
EC_ERROR_PARAM6 = 16,
EC_ERROR_PARAM7 = 17,
EC_ERROR_PARAM8 = 18,
EC_ERROR_PARAM9 = 19,
/* Wrong number of params */
EC_ERROR_PARAM_COUNT = 20,
/* Interrupt event not handled */
EC_ERROR_NOT_HANDLED = 21,
/* Data has not changed */
EC_ERROR_UNCHANGED = 22,
/* Memory allocation */
EC_ERROR_MEMORY_ALLOCATION = 23,
/* Verified boot errors */
EC_ERROR_VBOOT_SIGNATURE = 0x1000, /* 4096 */
EC_ERROR_VBOOT_SIG_MAGIC = 0x1001,
EC_ERROR_VBOOT_SIG_SIZE = 0x1002,
EC_ERROR_VBOOT_SIG_ALGORITHM = 0x1003,
EC_ERROR_VBOOT_HASH_ALGORITHM = 0x1004,
EC_ERROR_VBOOT_SIG_OFFSET = 0x1005,
EC_ERROR_VBOOT_DATA_SIZE = 0x1006,
/* Verified boot key errors */
EC_ERROR_VBOOT_KEY = 0x1100,
EC_ERROR_VBOOT_KEY_MAGIC = 0x1101,
EC_ERROR_VBOOT_KEY_SIZE = 0x1102,
/* Verified boot data errors */
EC_ERROR_VBOOT_DATA = 0x1200,
EC_ERROR_VBOOT_DATA_VERIFY = 0x1201,
/* Module-internal error codes may use this range. */
EC_ERROR_INTERNAL_FIRST = 0x10000,
EC_ERROR_INTERNAL_LAST = 0x1FFFF
};
/* Flags for i2c_xfer() */
#define I2C_XFER_START (1 << 0) /* Start smbus session from idle state */
#define I2C_XFER_STOP (1 << 1) /* Terminate smbus session with stop bit */
#define I2C_XFER_SINGLE (I2C_XFER_START | I2C_XFER_STOP) /* One transaction */
/* Default retry count for transmitting */
#define PD_RETRY_COUNT 3
/* Time to wait for TCPC to complete transmit */
#define PD_T_TCPC_TX_TIMEOUT (100*MSEC)
enum tcpc_cc_voltage_status {
TYPEC_CC_VOLT_OPEN = 0,
TYPEC_CC_VOLT_RA = 1,
TYPEC_CC_VOLT_RD = 2,
TYPEC_CC_VOLT_SNK_DEF = 5,
TYPEC_CC_VOLT_SNK_1_5 = 6,
TYPEC_CC_VOLT_SNK_3_0 = 7,
};
enum tcpc_cc_pull {
TYPEC_CC_RA = 0,
TYPEC_CC_RP = 1,
TYPEC_CC_RD = 2,
TYPEC_CC_OPEN = 3,
};
enum tcpc_rp_value {
TYPEC_RP_USB = 0,
TYPEC_RP_1A5 = 1,
TYPEC_RP_3A0 = 2,
TYPEC_RP_RESERVED = 3,
};
enum tcpm_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 tcpc_transmit_complete {
TCPC_TX_COMPLETE_SUCCESS = 0,
TCPC_TX_COMPLETE_DISCARDED = 1,
TCPC_TX_COMPLETE_FAILED = 2,
};
struct tcpm_drv {
/**
* Initialize TCPM driver and wait for TCPC readiness.
*
* @param port Type-C port number
*
* @return EC_SUCCESS or error
*/
int (*init)(int port);
/**
* Release the TCPM hardware and disconnect the driver.
* Only .init() can be called after .release().
*
* @param port Type-C port number
*
* @return EC_SUCCESS or error
*/
int (*release)(int port);
/**
* Read the CC line status.
*
* @param port Type-C port number
* @param cc1 pointer to CC status for CC1
* @param cc2 pointer to CC status for CC2
*
* @return EC_SUCCESS or error
*/
int (*get_cc)(int port, int *cc1, int *cc2);
/**
* Read VBUS
*
* @param port Type-C port number
*
* @return 0 => VBUS not detected, 1 => VBUS detected
*/
int (*get_vbus_level)(int port);
/**
* Set the value of the CC pull-up used when we are a source.
*
* @param port Type-C port number
* @param rp One of enum tcpc_rp_value
*
* @return EC_SUCCESS or error
*/
int (*select_rp_value)(int port, int rp);
/**
* Set the CC pull resistor. This sets our role as either source or sink.
*
* @param port Type-C port number
* @param pull One of enum tcpc_cc_pull
*
* @return EC_SUCCESS or error
*/
int (*set_cc)(int port, int pull);
/**
* Set polarity
*
* @param port Type-C port number
* @param polarity 0=> transmit on CC1, 1=> transmit on CC2
*
* @return EC_SUCCESS or error
*/
int (*set_polarity)(int port, int polarity);
/**
* Set Vconn.
*
* @param port Type-C port number
* @param polarity Polarity of the CC line to read
*
* @return EC_SUCCESS or error
*/
int (*set_vconn)(int port, int enable);
/**
* Set PD message header to use for goodCRC
*
* @param port Type-C port number
* @param power_role Power role to use in header
* @param data_role Data role to use in header
*
* @return EC_SUCCESS or error
*/
int (*set_msg_header)(int port, int power_role, int data_role);
/**
* Set RX enable flag
*
* @param port Type-C port number
* @enable true for enable, false for disable
*
* @return EC_SUCCESS or error
*/
int (*set_rx_enable)(int port, int enable);
/**
* Read last received PD message.
*
* @param port Type-C port number
* @param payload Pointer to location to copy payload of message
* @param header of message
*
* @return EC_SUCCESS or error
*/
int (*get_message)(int port, uint32_t *payload, int *head);
/**
* Transmit PD message
*
* @param port Type-C port number
* @param type Transmit type
* @param header Packet header
* @param cnt Number of bytes in payload
* @param data Payload
*
* @return EC_SUCCESS or error
*/
int (*transmit)(int port, enum tcpm_transmit_type type, uint16_t header,
const uint32_t *data);
/**
* TCPC is asserting alert
*
* @param port Type-C port number
*/
void (*tcpc_alert)(int port);
/**
* Discharge PD VBUS on src/sink disconnect & power role swap
*
* @param port Type-C port number
* @param enable Discharge enable or disable
*/
void (*tcpc_discharge_vbus)(int port, int enable);
#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
/**
* Enable TCPC auto DRP toggling.
*
* @param port Type-C port number
* @param enable 1: Enable 0: Disable
*
* @return EC_SUCCESS or error
*/
int (*drp_toggle)(int port, int enable);
#endif
/**
* Get firmware version.
*
* @param port Type-C port number
* @param renew Force renewal
* @param info Pointer to pointer to PD chip info
*
* @return EC_SUCCESS or error
*/
int (*get_chip_info)(int port, int renew,
struct ec_response_pd_chip_info **info);
};
enum tcpc_alert_polarity {
TCPC_ALERT_ACTIVE_LOW,
TCPC_ALERT_ACTIVE_HIGH,
};
struct tcpc_config_t {
int i2c_host_port;
int i2c_slave_addr;
const struct tcpm_drv *drv;
enum tcpc_alert_polarity pol;
};
/**
* Returns the PD_STATUS_TCPC_ALERT_* mask corresponding to the TCPC ports
* that are currently asserting ALERT.
*
* @return PD_STATUS_TCPC_ALERT_* mask.
*/
uint16_t tcpc_get_alert_status(void);
/**
* Optional, set the TCPC power mode.
*
* @param port Type-C port number
* @param mode 0: off/sleep, 1: on/awake
*/
void board_set_tcpc_power_mode(int port, int mode) __attribute__((weak));
/**
* Initialize TCPC.
*
* @param port Type-C port number
*/
void tcpc_init(int port);
/**
* TCPC is asserting alert
*
* @param port Type-C port number
*/
void tcpc_alert_clear(int port);
/**
* Run TCPC task once. This checks for incoming messages, processes
* any outgoing messages, and reads CC lines.
*
* @param port Type-C port number
* @param evt Event type that woke up this task
*/
int tcpc_run(int port, int evt);
/**
* Initialize board specific TCPC functions post TCPC initialization.
*
* @param port Type-C port number
*
* @return EC_SUCCESS or error
*/
int board_tcpc_post_init(int port) __attribute__((weak));
#endif /* __CROS_EC_USB_PD_TCPM_H */

View File

@@ -0,0 +1,69 @@
/*
* tcpm_driver.c
*
* Created: 11/11/2017 18:42:26
* Author: jason
*/
#include "tcpm_driver.h"
#include "I2C_Wrapper.hpp"
extern const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT];
#define STATUS_OK 0
/* I2C wrapper functions - get I2C port / slave addr from config struct. */
int tcpc_write(int port, int reg, int val) {
FRToSI2C::Mem_Write(tcpc_config[port].i2c_slave_addr, reg, (uint8_t*) &val,
1);
return STATUS_OK;
}
int tcpc_write16(int port, int reg, int val) {
uint8_t data[2];
data[0] = (0xFF) & val;
data[1] = (0xFF) & (val >> 8);
FRToSI2C::Mem_Write(tcpc_config[port].i2c_slave_addr, reg, (uint8_t*) data,
2);
return STATUS_OK;
}
int tcpc_read(int port, int reg, int *val) {
uint8_t data[1];
FRToSI2C::Mem_Read(tcpc_config[port].i2c_slave_addr, reg, (uint8_t*) data,
1);
*val = data[0];
return STATUS_OK;
}
int tcpc_read16(int port, int reg, int *val) {
uint8_t data[2];
FRToSI2C::Mem_Write(tcpc_config[port].i2c_slave_addr, reg, (uint8_t*) data,
2);
*val = data[0];
*val |= (data[1] << 8);
return STATUS_OK;
}
int tcpc_xfer(int port, const uint8_t *out, int out_size, uint8_t *in,
int in_size, int flags) {
// Write out the I2C port to the given slave address
// Write out the out byte array to the device (sending a stop if the flag is set)
// Then issue a read from the device
if (flags & I2C_XFER_STOP) {
//Issuing a stop between the requests
//Send as a Tx followed by a Rx
FRToSI2C::Transmit(tcpc_config[port].i2c_slave_addr, (uint8_t*)out, out_size);
FRToSI2C::Receive(tcpc_config[port].i2c_slave_addr, in, in_size);
} else {
//issue as a continious transmit & recieve
FRToSI2C::TransmitReceive(tcpc_config[port].i2c_slave_addr, (uint8_t*)out,
out_size, in, in_size);
}
return STATUS_OK;
}

View File

@@ -0,0 +1,22 @@
/*
* tcpm_driver.h
*
* Created: 11/11/2017 18:42:39
* Author: jason
*/
#ifndef TCPM_DRIVER_H_
#define TCPM_DRIVER_H_
#include <stdint.h>
// USB-C Stuff
#include "USBC_TCPM/tcpm.h"
#include "FUSB302.h"
#define CONFIG_USB_PD_PORT_COUNT 1
extern struct i2c_master_module i2c_master_instance;
#endif /* TCPM_DRIVER_H_ */

View File

@@ -0,0 +1,414 @@
/*
* usb_pd_driver.c
*
* Created: 11/11/2017 23:55:12
* Author: jason
*/
#include "usb_pd_driver.h"
#include "USBC_PD/usb_pd.h"
#include <string.h>
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(t) (sizeof(t) / sizeof(t[0]))
#endif
extern struct tc_module tc_instance;
extern uint32_t g_us_timestamp_upper_32bit;
uint32_t pd_task_set_event(uint32_t event, int wait_for_reply) {
switch (event) {
case PD_EVENT_TX:
break;
default:
break;
}
return 0;
}
const uint32_t pd_src_pdo[] = { PDO_FIXED(5000, 1500, PDO_FIXED_FLAGS), };
const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo);
const uint32_t pd_snk_pdo[] = { PDO_FIXED(5000, 500, PDO_FIXED_FLAGS),
PDO_FIXED(9000, 500, PDO_FIXED_FLAGS), PDO_FIXED(20000, 500,
PDO_FIXED_FLAGS), };
const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo);
void pd_set_input_current_limit(int port, uint32_t max_ma,
uint32_t supply_voltage) {
}
int pd_is_valid_input_voltage(int mv) {
return 1;
}
int pd_snk_is_vbus_provided(int port) {
return 1;
}
timestamp_t get_time(void) {
timestamp_t t;
//TODO
//
// system_interrupt_enter_critical_section();
// t.le.lo = tc_get_count_value(&tc_instance);
// t.le.hi = g_us_timestamp_upper_32bit;
// system_interrupt_leave_critical_section();
return t;
}
void pd_power_supply_reset(int port) {
return;
}
void pd_execute_data_swap(int port, int data_role) {
/* Do nothing */
}
int pd_check_data_swap(int port, int data_role) {
// Never allow data swap
return 0;
}
int pd_check_power_swap(int port) {
/* Always refuse power swap */
return 0;
}
int pd_board_checks(void) {
return EC_SUCCESS;
}
int pd_set_power_supply_ready(int port) {
#if 0
/* Disable charging */
gpio_set_level(GPIO_USB_C0_CHARGE_L, 1);
/* Enable VBUS source */
gpio_set_level(GPIO_USB_C0_5V_EN, 1);
/* notify host of power info change */
pd_send_host_event(PD_EVENT_POWER_CHANGE);
#endif // if 0
return EC_SUCCESS; /* we are ready */
}
void pd_transition_voltage(int idx) {
/* No-operation: we are always 5V */
#if 0
timestamp_t deadline;
uint32_t mv = src_pdo_charge[idx - 1].mv;
/* Is this a transition to a new voltage? */
if (charge_port_is_active() && vbus[CHG].mv != mv) {
/*
* Alter voltage limit on charge port, this should cause
* the port to select the desired PDO.
*/
pd_set_external_voltage_limit(CHG, mv);
/* Wait for CHG transition */
deadline.val = get_time().val + PD_T_PS_TRANSITION;
CPRINTS("Waiting for CHG port transition");
while (charge_port_is_active() &&
vbus[CHG].mv != mv &&
get_time().val < deadline.val)
msleep(10);
if (vbus[CHG].mv != mv) {
CPRINTS("Missed CHG transition, resetting DUT");
pd_power_supply_reset(DUT);
return;
}
CPRINTS("CHG transitioned");
}
vbus[DUT].mv = vbus[CHG].mv;
vbus[DUT].ma = vbus[CHG].ma;
#endif // if 0
}
void pd_check_dr_role(int port, int dr_role, int flags) {
#if 0
/* If UFP, try to switch to DFP */
if ((flags & PD_FLAGS_PARTNER_DR_DATA) && dr_role == PD_ROLE_UFP)
pd_request_data_swap(port);
#endif
}
void pd_check_pr_role(int port, int pr_role, int flags) {
#if 0
/*
* If partner is dual-role power and dualrole toggling is on, consider
* if a power swap is necessary.
*/
if ((flags & PD_FLAGS_PARTNER_DR_POWER) &&
pd_get_dual_role() == PD_DRP_TOGGLE_ON) {
/*
* If we are a sink and partner is not externally powered, then
* swap to become a source. If we are source and partner is
* externally powered, swap to become a sink.
*/
int partner_extpower = flags & PD_FLAGS_PARTNER_EXTPOWER;
if ((!partner_extpower && pr_role == PD_ROLE_SINK) ||
(partner_extpower && pr_role == PD_ROLE_SOURCE))
pd_request_power_swap(port);
}
#endif // if 0
}
void pd_process_source_cap_callback(int port, int cnt, uint32_t *src_caps) {
// char str[256];
// int i;
// uint32_t ma, mv, pdo;
// uint8_t old_display;
//
// old_display = display_screen;
// display_screen = SCREEN_POWER;
// memset(display_buffer[SCREEN_POWER], 0x00, DISP_MEM_SIZE);
//
// sprintf(str, "Has Power Delivery");
// UG_PutString(0, 8, str);
//
// for (i = 0; i < cnt; i++)
// {
// pd_extract_pdo_power(src_caps[i], &ma, &mv);
// sprintf(str, "%5.2f V, %5.2f A", (float)mv/1000, (float)ma/1000);
// UG_PutString(0, 8*(i+2), str);
// }
//
// display_screen = old_display;
// display_needs_update = 1;
//TODO Handle information on supported voltages?
}
/* ----------------- Vendor Defined Messages ------------------ */
/* Holds valid object position (opos) for entered mode */
static int alt_mode[PD_AMODE_COUNT];
const uint32_t vdo_idh = VDO_IDH(0, /* data caps as USB host */
1, /* data caps as USB device */
IDH_PTYPE_AMA, /* Alternate mode */
1, /* supports alt modes */
USB_VID_GOOGLE);
const uint32_t vdo_product = VDO_PRODUCT(CONFIG_USB_PID, CONFIG_USB_BCD_DEV);
const uint32_t vdo_ama = VDO_AMA(CONFIG_USB_PD_IDENTITY_HW_VERS,
CONFIG_USB_PD_IDENTITY_SW_VERS, 0, 0, 0, 0, /* SS[TR][12] */
0, /* Vconn power */
0, /* Vconn power required */
1, /* Vbus power required */
AMA_USBSS_BBONLY /* USB SS support */);
static int svdm_response_identity(int port, uint32_t *payload) {
payload[VDO_I(IDH)] = vdo_idh;
/* TODO(tbroch): Do we plan to obtain TID (test ID) */
payload[VDO_I(CSTAT)] = VDO_CSTAT(0);
payload[VDO_I(PRODUCT)] = vdo_product;
payload[VDO_I(AMA)] = vdo_ama;
return VDO_I(AMA) + 1;
}
static int svdm_response_svids(int port, uint32_t *payload) {
payload[1] = VDO_SVID(USB_SID_DISPLAYPORT, USB_VID_GOOGLE);
payload[2] = 0;
return 3;
}
#define OPOS_DP 1
#define OPOS_GFU 1
const uint32_t vdo_dp_modes[1] = { VDO_MODE_DP(0, /* UFP pin cfg supported : none */
MODE_DP_PIN_C, /* DFP pin cfg supported */
1, /* no usb2.0 signalling in AMode */
CABLE_PLUG, /* its a plug */
MODE_DP_V13, /* DPv1.3 Support, no Gen2 */
MODE_DP_SNK) /* Its a sink only */
};
const uint32_t vdo_goog_modes[1] = { VDO_MODE_GOOGLE(MODE_GOOGLE_FU) };
static int svdm_response_modes(int port, uint32_t *payload) {
if (PD_VDO_VID(payload[0]) == USB_SID_DISPLAYPORT) {
memcpy(payload + 1, vdo_dp_modes, sizeof(vdo_dp_modes));
return ARRAY_SIZE(vdo_dp_modes) + 1;
} else if (PD_VDO_VID(payload[0]) == USB_VID_GOOGLE) {
memcpy(payload + 1, vdo_goog_modes, sizeof(vdo_goog_modes));
return ARRAY_SIZE(vdo_goog_modes) + 1;
} else {
return 0; /* nak */
}
}
static int dp_status(int port, uint32_t *payload) {
int opos = PD_VDO_OPOS(payload[0]);
int hpd = 0; // gpio_get_level(GPIO_DP_HPD);
if (opos != OPOS_DP)
return 0; /* nak */
payload[1] = VDO_DP_STATUS(0, /* IRQ_HPD */
(hpd == 1), /* HPD_HI|LOW */
0, /* request exit DP */
0, /* request exit USB */
0, /* MF pref */
0, //gpio_get_level(GPIO_PD_SBU_ENABLE),
0, /* power low */
0x2);
return 2;
}
static int dp_config(int port, uint32_t *payload) {
if (PD_DP_CFG_DPON(payload[1]))
0; //gpio_set_level(GPIO_PD_SBU_ENABLE, 1);
return 1;
}
static int svdm_enter_mode(int port, uint32_t *payload) {
int rv = 0; /* will generate a NAK */
char str[256];
uint8_t old_display;
/* SID & mode request is valid */
if ((PD_VDO_VID(payload[0]) == USB_SID_DISPLAYPORT)
&& (PD_VDO_OPOS(payload[0]) == OPOS_DP)) {
alt_mode[PD_AMODE_DISPLAYPORT] = OPOS_DP;
rv = 1;
//pd_log_event(PD_EVENT_VIDEO_DP_MODE, 0, 1, NULL);
} else if ((PD_VDO_VID(payload[0]) == USB_VID_GOOGLE)
&& (PD_VDO_OPOS(payload[0]) == OPOS_GFU)) {
alt_mode[PD_AMODE_GOOGLE] = OPOS_GFU;
rv = 1;
}
// if (rv)
/*
* If we failed initial mode entry we'll have enumerated the USB
* Billboard class. If so we should disconnect.
*/
//usb_disconnect();
// old_display = display_screen;
// display_screen = SCREEN_ALTMODE;
// memset(display_buffer[SCREEN_ALTMODE], 0x00, DISP_MEM_SIZE);
//
// sprintf(str, "Requested Alt Mode");
// UG_PutString(0, 8, str);
//
// display_screen = old_display;
// display_needs_update = 1;
//TODO handle alt mode ?
return rv;
}
int pd_alt_mode(int port, uint16_t svid) {
if (svid == USB_SID_DISPLAYPORT)
return alt_mode[PD_AMODE_DISPLAYPORT];
else if (svid == USB_VID_GOOGLE)
return alt_mode[PD_AMODE_GOOGLE];
return 0;
}
static int svdm_exit_mode(int port, uint32_t *payload) {
return 1; /* Must return ACK */
}
static struct amode_fx dp_fx = { .status = &dp_status, .config = &dp_config, };
const struct svdm_response svdm_rsp = { &svdm_response_identity,
&svdm_response_svids, &svdm_response_modes, &svdm_enter_mode,
&svdm_exit_mode, &dp_fx, };
int pd_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload) {
int rsize;
if (PD_VDO_VID(payload[0]) != USB_VID_GOOGLE || !alt_mode[PD_AMODE_GOOGLE])
return 0;
*rpayload = payload;
rsize = pd_custom_flash_vdm(port, cnt, payload);
if (!rsize) {
int cmd = PD_VDO_CMD(payload[0]);
switch (cmd) {
case VDO_CMD_GET_LOG:
rsize = pd_vdm_get_log_entry(payload);
break;
default:
/* Unknown : do not answer */
return 0;
}
}
/* respond (positively) to the request */
payload[0] |= VDO_SRC_RESPONDER;
return rsize;
}
int pd_custom_flash_vdm(int port, int cnt, uint32_t *payload) {
static int flash_offset;
int rsize = 1; /* default is just VDM header returned */
switch (PD_VDO_CMD(payload[0])) {
#if 0
case VDO_CMD_VERSION:
memcpy(payload + 1, &current_image_data.version, 24);
rsize = 7;
break;
case VDO_CMD_REBOOT:
/* ensure the power supply is in a safe state */
pd_power_supply_reset(0);
system_reset(0);
break;
case VDO_CMD_READ_INFO:
/* copy info into response */
pd_get_info(payload + 1);
rsize = 7;
break;
case VDO_CMD_FLASH_ERASE:
/* do not kill the code under our feet */
if (system_get_image_copy() != SYSTEM_IMAGE_RO)
break;
pd_log_event(PD_EVENT_ACC_RW_ERASE, 0, 0, NULL);
flash_offset = CONFIG_EC_WRITABLE_STORAGE_OFF +
CONFIG_RW_STORAGE_OFF;
flash_physical_erase(CONFIG_EC_WRITABLE_STORAGE_OFF +
CONFIG_RW_STORAGE_OFF, CONFIG_RW_SIZE);
rw_flash_changed = 1;
break;
case VDO_CMD_FLASH_WRITE:
/* do not kill the code under our feet */
if ((system_get_image_copy() != SYSTEM_IMAGE_RO) ||
(flash_offset < CONFIG_EC_WRITABLE_STORAGE_OFF +
CONFIG_RW_STORAGE_OFF))
break;
flash_physical_write(flash_offset, 4*(cnt - 1),
(const char *)(payload+1));
flash_offset += 4*(cnt - 1);
rw_flash_changed = 1;
break;
case VDO_CMD_ERASE_SIG:
/* this is not touching the code area */
{
uint32_t zero = 0;
int offset;
/* zeroes the area containing the RSA signature */
for (offset = FW_RW_END - RSANUMBYTES;
offset < FW_RW_END; offset += 4)
flash_physical_write(offset, 4,
(const char *)&zero);
}
break;
#endif // 0
default:
/* Unknown : do not answer */
return 0;
}
return rsize;
}

View File

@@ -0,0 +1,104 @@
/*
* usb_pd_driver.h
*
* Created: 11/11/2017 23:55:25
* Author: jason
*/
#ifndef USB_PD_DRIVER_H_
#define USB_PD_DRIVER_H_
#include "USBC_PD/usb_pd.h"
#include <stdint.h>
//#define CONFIG_BBRAM
#define CONFIG_CHARGE_MANAGER
//#define CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP
//#define CONFIG_USBC_VCONN_SWAP
#define CONFIG_USB_PD_ALT_MODE
//#define CONFIG_USB_PD_CHROMEOS
#define CONFIG_USB_PD_DUAL_ROLE
//#define CONFIG_USB_PD_GIVE_BACK
//#define CONFIG_USB_PD_SIMPLE_DFP
//#define CONFIG_USB_PD_TCPM_TCPCI
#define PD_PREFER_HIGH_VOLTAGE
/* Default pull-up value on the USB-C ports when they are used as source. */
#define CONFIG_USB_PD_PULLUP TYPEC_RP_USB
/* Override PD_ROLE_DEFAULT in usb_pd.h */
#define PD_ROLE_DEFAULT(port) (PD_ROLE_SINK)
/* Don't automatically change roles */
#undef CONFIG_USB_PD_INITIAL_DRP_STATE
#define CONFIG_USB_PD_INITIAL_DRP_STATE PD_DRP_FREEZE
/* Prefer higher voltage, and more importantly, lower current */
#define PD_PREFER_HIGH_VOLTAGE
//#define PD_PREFER_LOW_VOLTAGE
/* board specific type-C power constants */
/*
* delay to turn on the power supply max is ~16ms.
* delay to turn off the power supply max is about ~180ms.
*/
#define PD_POWER_SUPPLY_TURN_ON_DELAY 10000 /* us */
#define PD_POWER_SUPPLY_TURN_OFF_DELAY 20000 /* us */
/* Define typical operating power and max power */
#define PD_OPERATING_POWER_MW 15000
#define PD_MAX_POWER_MW 100000
#define PD_MAX_CURRENT_MA 5000
#define PD_MAX_VOLTAGE_MV 20000
#define PDO_FIXED_FLAGS (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |\
PDO_FIXED_COMM_CAP)
/* USB configuration */
#define CONFIG_USB_PID 0x502f // Stolen, so should change for anything useful
#define CONFIG_USB_BCD_DEV 0x0001 /* v 0.01 */
/* Optional features */
#define CONFIG_USB_PD_IDENTITY_HW_VERS 1
#define CONFIG_USB_PD_IDENTITY_SW_VERS 1
#define usleep(us) (delay_us(us))
#define msleep(us) (delay_ms(us))
typedef union {
uint64_t val;
struct {
uint32_t lo;
uint32_t hi;
} le /* little endian words */;
} timestamp_t;
uint32_t pd_task_set_event(uint32_t event, int wait_for_reply);
void pd_power_supply_reset(int port);
// Get the current timestamp from the system timer.
timestamp_t get_time(void);
/* Standard macros / definitions */
#ifndef MAX
#define MAX(a, b) \
({ \
__typeof__(a) temp_a = (a); \
__typeof__(b) temp_b = (b); \
\
temp_a > temp_b ? temp_a : temp_b; \
})
#endif
#ifndef MIN
#define MIN(a, b) \
({ \
__typeof__(a) temp_a = (a); \
__typeof__(b) temp_b = (b); \
\
temp_a < temp_b ? temp_a : temp_b; \
})
#endif
#endif /* USB_PD_DRIVER_H_ */

View File

@@ -0,0 +1,354 @@
/* Copyright 2015 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_USB_PD_TCPM_H
#define __CROS_EC_USB_PD_TCPM_H
/* List of common error codes that can be returned */
enum ec_error_list {
/* Success - no error */
EC_SUCCESS = 0,
/* Unknown error */
EC_ERROR_UNKNOWN = 1,
/* Function not implemented yet */
EC_ERROR_UNIMPLEMENTED = 2,
/* Overflow error; too much input provided. */
EC_ERROR_OVERFLOW = 3,
/* Timeout */
EC_ERROR_TIMEOUT = 4,
/* Invalid argument */
EC_ERROR_INVAL = 5,
/* Already in use, or not ready yet */
EC_ERROR_BUSY = 6,
/* Access denied */
EC_ERROR_ACCESS_DENIED = 7,
/* Failed because component does not have power */
EC_ERROR_NOT_POWERED = 8,
/* Failed because component is not calibrated */
EC_ERROR_NOT_CALIBRATED = 9,
/* Failed because CRC error */
EC_ERROR_CRC = 10,
/* Invalid console command param (PARAMn means parameter n is bad) */
EC_ERROR_PARAM1 = 11,
EC_ERROR_PARAM2 = 12,
EC_ERROR_PARAM3 = 13,
EC_ERROR_PARAM4 = 14,
EC_ERROR_PARAM5 = 15,
EC_ERROR_PARAM6 = 16,
EC_ERROR_PARAM7 = 17,
EC_ERROR_PARAM8 = 18,
EC_ERROR_PARAM9 = 19,
/* Wrong number of params */
EC_ERROR_PARAM_COUNT = 20,
/* Interrupt event not handled */
EC_ERROR_NOT_HANDLED = 21,
/* Data has not changed */
EC_ERROR_UNCHANGED = 22,
/* Memory allocation */
EC_ERROR_MEMORY_ALLOCATION = 23,
/* Verified boot errors */
EC_ERROR_VBOOT_SIGNATURE = 0x1000, /* 4096 */
EC_ERROR_VBOOT_SIG_MAGIC = 0x1001,
EC_ERROR_VBOOT_SIG_SIZE = 0x1002,
EC_ERROR_VBOOT_SIG_ALGORITHM = 0x1003,
EC_ERROR_VBOOT_HASH_ALGORITHM = 0x1004,
EC_ERROR_VBOOT_SIG_OFFSET = 0x1005,
EC_ERROR_VBOOT_DATA_SIZE = 0x1006,
/* Verified boot key errors */
EC_ERROR_VBOOT_KEY = 0x1100,
EC_ERROR_VBOOT_KEY_MAGIC = 0x1101,
EC_ERROR_VBOOT_KEY_SIZE = 0x1102,
/* Verified boot data errors */
EC_ERROR_VBOOT_DATA = 0x1200,
EC_ERROR_VBOOT_DATA_VERIFY = 0x1201,
/* Module-internal error codes may use this range. */
EC_ERROR_INTERNAL_FIRST = 0x10000,
EC_ERROR_INTERNAL_LAST = 0x1FFFF
};
/* Flags for i2c_xfer() */
#define I2C_XFER_START (1 << 0) /* Start smbus session from idle state */
#define I2C_XFER_STOP (1 << 1) /* Terminate smbus session with stop bit */
#define I2C_XFER_SINGLE (I2C_XFER_START | I2C_XFER_STOP) /* One transaction */
/* Default retry count for transmitting */
#define PD_RETRY_COUNT 3
/* Time to wait for TCPC to complete transmit */
#define PD_T_TCPC_TX_TIMEOUT (100*MSEC)
enum tcpc_cc_voltage_status {
TYPEC_CC_VOLT_OPEN = 0,
TYPEC_CC_VOLT_RA = 1,
TYPEC_CC_VOLT_RD = 2,
TYPEC_CC_VOLT_SNK_DEF = 5,
TYPEC_CC_VOLT_SNK_1_5 = 6,
TYPEC_CC_VOLT_SNK_3_0 = 7,
};
enum tcpc_cc_pull {
TYPEC_CC_RA = 0,
TYPEC_CC_RP = 1,
TYPEC_CC_RD = 2,
TYPEC_CC_OPEN = 3,
};
enum tcpc_rp_value {
TYPEC_RP_USB = 0,
TYPEC_RP_1A5 = 1,
TYPEC_RP_3A0 = 2,
TYPEC_RP_RESERVED = 3,
};
enum tcpm_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 tcpc_transmit_complete {
TCPC_TX_COMPLETE_SUCCESS = 0,
TCPC_TX_COMPLETE_DISCARDED = 1,
TCPC_TX_COMPLETE_FAILED = 2,
};
struct tcpm_drv {
/**
* Initialize TCPM driver and wait for TCPC readiness.
*
* @param port Type-C port number
*
* @return EC_SUCCESS or error
*/
int (*init)(int port);
/**
* Release the TCPM hardware and disconnect the driver.
* Only .init() can be called after .release().
*
* @param port Type-C port number
*
* @return EC_SUCCESS or error
*/
int (*release)(int port);
/**
* Read the CC line status.
*
* @param port Type-C port number
* @param cc1 pointer to CC status for CC1
* @param cc2 pointer to CC status for CC2
*
* @return EC_SUCCESS or error
*/
int (*get_cc)(int port, int *cc1, int *cc2);
/**
* Read VBUS
*
* @param port Type-C port number
*
* @return 0 => VBUS not detected, 1 => VBUS detected
*/
int (*get_vbus_level)(int port);
/**
* Set the value of the CC pull-up used when we are a source.
*
* @param port Type-C port number
* @param rp One of enum tcpc_rp_value
*
* @return EC_SUCCESS or error
*/
int (*select_rp_value)(int port, int rp);
/**
* Set the CC pull resistor. This sets our role as either source or sink.
*
* @param port Type-C port number
* @param pull One of enum tcpc_cc_pull
*
* @return EC_SUCCESS or error
*/
int (*set_cc)(int port, int pull);
/**
* Set polarity
*
* @param port Type-C port number
* @param polarity 0=> transmit on CC1, 1=> transmit on CC2
*
* @return EC_SUCCESS or error
*/
int (*set_polarity)(int port, int polarity);
/**
* Set Vconn.
*
* @param port Type-C port number
* @param polarity Polarity of the CC line to read
*
* @return EC_SUCCESS or error
*/
int (*set_vconn)(int port, int enable);
/**
* Set PD message header to use for goodCRC
*
* @param port Type-C port number
* @param power_role Power role to use in header
* @param data_role Data role to use in header
*
* @return EC_SUCCESS or error
*/
int (*set_msg_header)(int port, int power_role, int data_role);
/**
* Set RX enable flag
*
* @param port Type-C port number
* @enable true for enable, false for disable
*
* @return EC_SUCCESS or error
*/
int (*set_rx_enable)(int port, int enable);
/**
* Read last received PD message.
*
* @param port Type-C port number
* @param payload Pointer to location to copy payload of message
* @param header of message
*
* @return EC_SUCCESS or error
*/
int (*get_message)(int port, uint32_t *payload, int *head);
/**
* Transmit PD message
*
* @param port Type-C port number
* @param type Transmit type
* @param header Packet header
* @param cnt Number of bytes in payload
* @param data Payload
*
* @return EC_SUCCESS or error
*/
int (*transmit)(int port, enum tcpm_transmit_type type, uint16_t header,
const uint32_t *data);
/**
* TCPC is asserting alert
*
* @param port Type-C port number
*/
void (*tcpc_alert)(int port);
/**
* Discharge PD VBUS on src/sink disconnect & power role swap
*
* @param port Type-C port number
* @param enable Discharge enable or disable
*/
void (*tcpc_discharge_vbus)(int port, int enable);
#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
/**
* Enable TCPC auto DRP toggling.
*
* @param port Type-C port number
* @param enable 1: Enable 0: Disable
*
* @return EC_SUCCESS or error
*/
int (*drp_toggle)(int port, int enable);
#endif
/**
* Get firmware version.
*
* @param port Type-C port number
* @param renew Force renewal
* @param info Pointer to pointer to PD chip info
*
* @return EC_SUCCESS or error
*/
int (*get_chip_info)(int port, int renew,
struct ec_response_pd_chip_info **info);
};
enum tcpc_alert_polarity {
TCPC_ALERT_ACTIVE_LOW,
TCPC_ALERT_ACTIVE_HIGH,
};
struct tcpc_config_t {
int i2c_host_port;
int i2c_slave_addr;
const struct tcpm_drv *drv;
enum tcpc_alert_polarity pol;
};
/**
* Returns the PD_STATUS_TCPC_ALERT_* mask corresponding to the TCPC ports
* that are currently asserting ALERT.
*
* @return PD_STATUS_TCPC_ALERT_* mask.
*/
uint16_t tcpc_get_alert_status(void);
/**
* Optional, set the TCPC power mode.
*
* @param port Type-C port number
* @param mode 0: off/sleep, 1: on/awake
*/
void board_set_tcpc_power_mode(int port, int mode) __attribute__((weak));
/**
* Initialize TCPC.
*
* @param port Type-C port number
*/
void tcpc_init(int port);
/**
* TCPC is asserting alert
*
* @param port Type-C port number
*/
void tcpc_alert_clear(int port);
/**
* Run TCPC task once. This checks for incoming messages, processes
* any outgoing messages, and reads CC lines.
*
* @param port Type-C port number
* @param evt Event type that woke up this task
*/
int tcpc_run(int port, int evt);
/**
* Initialize board specific TCPC functions post TCPC initialization.
*
* @param port Type-C port number
*
* @return EC_SUCCESS or error
*/
int board_tcpc_post_init(int port) __attribute__((weak));
#endif /* __CROS_EC_USB_PD_TCPM_H */

View File

@@ -40,6 +40,9 @@ public:
static bool probe(uint16_t DevAddress);
static void Transmit(uint16_t DevAddress, uint8_t *pData, uint16_t Size);
static void Receive(uint16_t DevAddress, uint8_t *pData, uint16_t Size);
static void TransmitReceive(uint16_t DevAddress, uint8_t *pData_tx,
uint16_t Size_tx, uint8_t *pData_rx, uint16_t Size_rx);
static void I2C_RegisterWrite(uint8_t address, uint8_t reg, uint8_t data);
static uint8_t I2C_RegisterRead(uint8_t address, uint8_t reg);