diff options
author | Alec Berg <alecaberg@chromium.org> | 2015-05-04 09:22:42 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-05-22 18:12:02 +0000 |
commit | 37a23855f7bba522f4cb300d8f4547764e5e3578 (patch) | |
tree | 5c17c6562bfb1231e164c75800e0d6ef3f3de5d0 /common/usb_pd_tcpc.c | |
parent | 608fa5efd868325ce85fca1f777a89c0909ea032 (diff) | |
download | chrome-ec-37a23855f7bba522f4cb300d8f4547764e5e3578.tar.gz |
pd: add first version of TCPCI I2C interface
Add first version of TCPCI (type-C port controller interface),
which is an I2C protocol for interfacing with TCPCs.
This is roughly tracking version 0.56 of the PD Interface spec.
BUG=none
BRANCH=none
TEST=tested on oak. modified oak EC to be TCPM and oak PD to
be TCPC and tested we can negotiate with hoho and zinger.
Change-Id: I83644ca83f2d3ce69d5d8356beca20a7ab155a87
Signed-off-by: Alec Berg <alecaberg@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/270172
Diffstat (limited to 'common/usb_pd_tcpc.c')
-rw-r--r-- | common/usb_pd_tcpc.c | 218 |
1 files changed, 183 insertions, 35 deletions
diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c index 4e72de34d3..272b84458c 100644 --- a/common/usb_pd_tcpc.c +++ b/common/usb_pd_tcpc.c @@ -184,6 +184,9 @@ static const uint8_t dec4b5b[] = { #define TYPE_C_SRC_1500_THRESHOLD 660 /* mV */ #define TYPE_C_SRC_3000_THRESHOLD 1230 /* mV */ +/* Convert TCPC Alert register to index into pd.alert[] */ +#define ALERT_REG_TO_INDEX(reg) (reg - TCPC_REG_ALERT1) + /* PD transmit errors */ enum pd_tx_errors { PD_TX_ERR_GOODCRC = -1, /* Failed to receive goodCRC */ @@ -213,6 +216,7 @@ static struct pd_port_controller { /* Next transmit */ enum tcpm_transmit_type tx_type; uint16_t tx_head; + uint32_t tx_payload[7]; const uint32_t *tx_data; } pd[PD_PORT_COUNT]; @@ -664,23 +668,23 @@ static int cc_voltage_to_status(int port, int cc_volt) /* If we have a pull-up, then we are source, check for Rd. */ if (pd[port].cc_pull == TYPEC_CC_RP) { if (CC_NC(cc_volt)) - return TYPEC_CC_SRC_OPEN; + return TYPEC_CC_VOLT_OPEN; else if (CC_RA(cc_volt)) - return TYPEC_CC_SRC_RA; + return TYPEC_CC_VOLT_RA; else - return TYPEC_CC_SRC_RD; + return TYPEC_CC_VOLT_SNK_DEF; /* If we have a pull-down, then we are sink, check for Rp. */ } #ifdef CONFIG_USB_PD_DUAL_ROLE else if (pd[port].cc_pull == TYPEC_CC_RD) { if (cc_volt >= TYPE_C_SRC_3000_THRESHOLD) - return TYPEC_CC_SNK_PWR_3_0; + return TYPEC_CC_VOLT_SNK_3_0; else if (cc_volt >= TYPE_C_SRC_1500_THRESHOLD) - return TYPEC_CC_SNK_PWR_1_5; + return TYPEC_CC_VOLT_SNK_1_5; else if (CC_RP(cc_volt)) - return TYPEC_CC_SNK_PWR_DEFAULT; + return TYPEC_CC_VOLT_SNK_DEF; else - return TYPEC_CC_SNK_OPEN; + return TYPEC_CC_VOLT_OPEN; } #endif /* If we are open, then always return 0 */ @@ -690,7 +694,7 @@ static int cc_voltage_to_status(int port, int cc_volt) static void alert(int port, int reg, int mask) { - pd[port].alert[reg] |= mask; + pd[port].alert[ALERT_REG_TO_INDEX(reg)] |= mask; tcpc_alert(); } @@ -718,10 +722,10 @@ int tcpc_run(int port, int evt) handle_request(port, pd[port].rx_head, pd[port].rx_payload); - alert(port, TCPC_ALERT0, TCPC_ALERT0_RX_STATUS); + alert(port, TCPC_REG_ALERT1, TCPC_REG_ALERT1_RX_STATUS); } else if (pd[port].rx_head == PD_RX_ERR_HARD_RESET) { - alert(port, TCPC_ALERT0, - TCPC_ALERT0_RX_HARD_RST); + alert(port, TCPC_REG_ALERT1, + TCPC_REG_ALERT1_RX_HARD_RST); } } @@ -746,14 +750,14 @@ int tcpc_run(int port, int evt) /* send appropriate alert for tx completion */ if (res >= 0) - alert(port, TCPC_ALERT0, - TCPC_ALERT0_TX_SUCCESS); + alert(port, TCPC_REG_ALERT1, + TCPC_REG_ALERT1_TX_SUCCESS); else if (res == PD_TX_ERR_GOODCRC) - alert(port, TCPC_ALERT0, - TCPC_ALERT0_TX_FAILED); + alert(port, TCPC_REG_ALERT1, + TCPC_REG_ALERT1_TX_FAILED); else - alert(port, TCPC_ALERT0, - TCPC_ALERT0_TX_DISCARDED); + alert(port, TCPC_REG_ALERT1, + TCPC_REG_ALERT1_TX_DISCARDED); } /* CC pull changed, wait 1ms for CC voltage to stabilize */ @@ -769,7 +773,7 @@ int tcpc_run(int port, int evt) cc = cc_voltage_to_status(port, cc); if (pd[port].cc_status[i] != cc) { pd[port].cc_status[i] = cc; - alert(port, TCPC_ALERT0, TCPC_ALERT0_CC_STATUS); + alert(port, TCPC_REG_ALERT1, TCPC_REG_ALERT1_CC_STATUS); } } @@ -806,20 +810,22 @@ void pd_rx_event(int port) task_set_event(PORT_TO_TASK_ID(port), PD_EVENT_RX, 0); } -int tcpc_alert_status(int port, int alert_reg) +int tcpc_alert_status(int port, int alert_reg, uint8_t *alert) { - int ret = pd[port].alert[alert_reg]; + int ret = pd[port].alert[ALERT_REG_TO_INDEX(alert_reg)]; + /* TODO: Need to use alert mask to know which bits to let through */ /* TODO: Alert register is read-clear for now, but shouldn't be */ - pd[port].alert[alert_reg] = 0; - return ret; + pd[port].alert[ALERT_REG_TO_INDEX(alert_reg)] = 0; + *alert = ret; + return EC_SUCCESS; } -void tcpc_set_cc(int port, int pull) +int tcpc_set_cc(int port, int pull) { /* If CC pull resistor not changing, then nothing to do */ if (pd[port].cc_pull == pull) - return; + return EC_SUCCESS; /* Change CC pull resistor */ pd[port].cc_pull = pull; @@ -832,8 +838,7 @@ void tcpc_set_cc(int port, int pull) * status, we should set the CC status to open, in case TCPM * asks before it is known for sure. */ - pd[port].cc_status[0] = pull == TYPEC_CC_RP ? TYPEC_CC_SRC_OPEN : - TYPEC_CC_SNK_OPEN; + pd[port].cc_status[0] = TYPEC_CC_VOLT_OPEN; pd[port].cc_status[1] = pd[port].cc_status[0]; /* Wake the PD phy task with special CC event mask */ @@ -843,28 +848,35 @@ void tcpc_set_cc(int port, int pull) #else task_set_event(PORT_TO_TASK_ID(port), PD_EVENT_CC, 0); #endif + return EC_SUCCESS; } -int tcpc_get_cc(int port, int polarity) +int tcpc_get_cc(int port, int *cc1, int *cc2) { - return pd[port].cc_status[polarity]; + *cc2 = pd[port].cc_status[1]; + *cc1 = pd[port].cc_status[0]; + + return EC_SUCCESS; } -void tcpc_set_polarity(int port, int polarity) +int tcpc_set_polarity(int port, int polarity) { pd[port].polarity = polarity; pd_select_polarity(port, pd[port].polarity); + + return EC_SUCCESS; } -void tcpc_set_vconn(int port, int enable) +int tcpc_set_vconn(int port, int enable) { #ifdef CONFIG_USBC_VCONN pd_set_vconn(port, pd[port].polarity, enable); #endif + return EC_SUCCESS; } -void tcpc_transmit(int port, enum tcpm_transmit_type type, uint16_t header, - const uint32_t *data) +int tcpc_transmit(int port, enum tcpm_transmit_type type, uint16_t header, + const uint32_t *data) { /* Store data to transmit and wake task to send it */ pd[port].tx_type = type; @@ -876,20 +888,156 @@ void tcpc_transmit(int port, enum tcpm_transmit_type type, uint16_t header, #else task_set_event(PORT_TO_TASK_ID(port), PD_EVENT_TX, 0); #endif + return EC_SUCCESS; } -void tcpc_set_msg_header(int port, int power_role, int data_role) +int tcpc_set_msg_header(int port, int power_role, int data_role) { pd[port].power_role = power_role; pd[port].data_role = data_role; + + return EC_SUCCESS; } -int tcpc_get_message(int port, uint32_t *payload) +int tcpc_get_message(int port, uint32_t *payload, int *head) { memcpy(payload, pd[port].rx_payload, sizeof(pd[port].rx_payload)); - return pd[port].rx_head; + *head = pd[port].rx_head; + return EC_SUCCESS; +} + +#ifndef CONFIG_USB_POWER_DELIVERY +static void tcpc_i2c_write(int port, int reg, int len, uint8_t *payload) +{ + switch (reg) { + case TCPC_REG_ROLE_CTRL: + tcpc_set_cc(port, TCPC_REG_ROLE_CTRL_CC1(payload[1])); + break; + case TCPC_REG_POWER_CTRL: + tcpc_set_polarity(port, + TCPC_REG_POWER_CTRL_POLARITY(payload[1])); + tcpc_set_vconn(port, TCPC_REG_POWER_CTRL_VCONN(payload[1])); + break; + case TCPC_REG_MSG_HDR_INFO: + tcpc_set_msg_header(port, + TCPC_REG_MSG_HDR_INFO_PROLE(payload[1]), + TCPC_REG_MSG_HDR_INFO_DROLE(payload[1])); + break; + case TCPC_REG_ALERT1: + case TCPC_REG_ALERT2: + /* TODO: clear alert status reg when writtent to */ + break; + case TCPC_REG_TX_HDR: + pd[port].tx_head = (payload[2] << 8) | payload[1]; + break; + case TCPC_REG_TX_DATA: + memcpy(pd[port].tx_payload, &payload[1], len - 1); + break; + case TCPC_REG_TRANSMIT: + tcpc_transmit(port, TCPC_REG_TRANSMIT_TYPE(payload[1]), + pd[port].tx_head, pd[port].tx_payload); + break; + } } +static int tcpc_i2c_read(int port, int reg, uint8_t *payload) +{ + int cc1, cc2; + + switch (reg) { + case TCPC_REG_CC1_STATUS: + tcpc_get_cc(port, &cc1, &cc2); + payload[0] = TCPC_REG_CC_STATUS_SET( + pd[port].cc_pull == TYPEC_CC_RP ? + TYPEC_CC_TERM_RP_DEF : TYPEC_CC_TERM_RD, + pd[port].cc_status[0]); + payload[1] = TCPC_REG_CC_STATUS_SET( + pd[port].cc_pull == TYPEC_CC_RP ? + TYPEC_CC_TERM_RP_DEF : TYPEC_CC_TERM_RD, + pd[port].cc_status[1]); + return 2; + case TCPC_REG_ROLE_CTRL: + payload[0] = TCPC_REG_ROLE_CTRL_SET(0, 0, + pd[port].cc_pull, + pd[port].cc_pull); + return 1; + case TCPC_REG_POWER_CTRL: + payload[0] = TCPC_REG_POWER_CTRL_SET(pd[port].polarity, 0); + return 1; + case TCPC_REG_MSG_HDR_INFO: + payload[0] = TCPC_REG_MSG_HDR_INFO_SET(pd[port].data_role, + pd[port].power_role); + return 1; + case TCPC_REG_ALERT1: + case TCPC_REG_ALERT2: + tcpc_alert_status(port, reg, payload); + return 1; + case TCPC_REG_RX_BYTE_CNT: + payload[0] = 4*PD_HEADER_CNT(pd[port].rx_head); + return 1; + case TCPC_REG_RX_HDR: + payload[0] = pd[port].rx_head & 0xff; + payload[1] = (pd[port].rx_head >> 8) & 0xff; + return 2; + case TCPC_REG_RX_DATA: + memcpy(payload, pd[port].rx_payload, + sizeof(pd[port].rx_payload)); + return sizeof(pd[port].rx_payload); + case TCPC_REG_TX_BYTE_CNT: + payload[0] = PD_HEADER_CNT(pd[port].tx_head); + return 1; + case TCPC_REG_TX_HDR: + payload[0] = pd[port].tx_head & 0xff; + payload[1] = (pd[port].tx_head >> 8) & 0xff; + return 2; + case TCPC_REG_TX_DATA: + memcpy(payload, pd[port].tx_payload, + sizeof(pd[port].tx_payload)); + return sizeof(pd[port].tx_payload); + default: + return 0; + } +} + +void tcpc_i2c_process(int read, int port, int len, uint8_t *payload, + void (*send_response)(int)) +{ + int i, reg; + + if (debug_level >= 1) { + CPRINTF("tcpci p%d: ", port); + for (i = 0; i < len; i++) + CPRINTF("0x%02x ", payload[i]); + CPRINTF("\n"); + } + + /* length must always be at least 1 */ + if (len == 0) { + /* + * if this is a read, we must call send_response() for + * i2c transaction to finishe properly + */ + if (read) + (*send_response)(0); + } + + /* if this is a write, length must be at least 2 */ + if (!read && len < 2) + return; + + /* register is always first byte */ + reg = payload[0]; + + /* perform read or write */ + if (read) { + len = tcpc_i2c_read(port, reg, payload); + (*send_response)(len); + } else { + tcpc_i2c_write(port, reg, len, payload); + } +} +#endif + #ifdef CONFIG_COMMON_RUNTIME static int command_tcpc(int argc, char **argv) { |