diff options
-rw-r--r-- | common/usb_pd_protocol.c | 81 | ||||
-rw-r--r-- | common/usb_pd_tcpc.c | 218 | ||||
-rw-r--r-- | common/usb_pd_tcpm.c | 145 | ||||
-rw-r--r-- | common/usb_pd_tcpm_stub.c | 44 | ||||
-rw-r--r-- | include/usb_pd_tcpc.h | 26 | ||||
-rw-r--r-- | include/usb_pd_tcpm.h | 192 |
6 files changed, 558 insertions, 148 deletions
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 0bd0b6169a..639c62e5f7 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -278,7 +278,7 @@ static void inc_id(int port) static void pd_transmit_complete(int port, int status) { - if (status & TCPC_ALERT0_TX_SUCCESS) + if (status & TCPC_REG_ALERT1_TX_SUCCESS) inc_id(port); pd[port].tx_status = status; @@ -301,7 +301,7 @@ static int pd_transmit(int port, enum tcpm_transmit_type type, return -1; /* TODO: give different error condition for failed vs discarded */ - return pd[port].tx_status & TCPC_ALERT0_TX_SUCCESS ? 1 : -1; + return pd[port].tx_status & TCPC_REG_ALERT1_TX_SUCCESS ? 1 : -1; } static void pd_update_roles(int port) @@ -1237,9 +1237,9 @@ static inline int get_typec_current_limit(int cc) int charge; /* Detect type C charger current limit based upon vbus voltage. */ - if (cc == TYPEC_CC_SNK_PWR_3_0) + if (cc == TYPEC_CC_VOLT_SRC_3_0) charge = 3000; - else if (cc == TYPEC_CC_SNK_PWR_1_5) + else if (cc == TYPEC_CC_VOLT_SRC_1_5) charge = 1500; else charge = 0; @@ -1345,7 +1345,7 @@ void pd_task(void) /* process any potential incoming message */ incoming_packet = evt & PD_EVENT_RX; if (incoming_packet) { - head = tcpm_get_message(port, payload); + tcpm_get_message(port, payload, &head); if (head > 0) handle_request(port, head, payload); } @@ -1358,14 +1358,13 @@ void pd_task(void) break; case PD_STATE_SRC_DISCONNECTED: timeout = 10*MSEC; - cc1 = tcpm_get_cc(port, 0); - cc2 = tcpm_get_cc(port, 1); + tcpm_get_cc(port, &cc1, &cc2); /* Vnc monitoring */ - if ((cc1 == TYPEC_CC_SRC_RD || - cc2 == TYPEC_CC_SRC_RD) || - (cc1 == TYPEC_CC_SRC_RA && - cc2 == TYPEC_CC_SRC_RA)) { + if ((TYPEC_CC_IS_RD(cc1) || + TYPEC_CC_IS_RD(cc2)) || + (cc1 == TYPEC_CC_VOLT_RA && + cc2 == TYPEC_CC_VOLT_RA)) { #ifdef CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP /* Enable VBUS */ if (pd_set_power_supply_ready(port)) @@ -1391,18 +1390,17 @@ void pd_task(void) break; case PD_STATE_SRC_DISCONNECTED_DEBOUNCE: timeout = 20*MSEC; - cc1 = tcpm_get_cc(port, 0); - cc2 = tcpm_get_cc(port, 1); + tcpm_get_cc(port, &cc1, &cc2); - if (cc1 == TYPEC_CC_SRC_RD && cc2 == TYPEC_CC_SRC_RD) { + if (TYPEC_CC_IS_RD(cc1) && TYPEC_CC_IS_RD(cc2)) { /* Debug accessory */ new_cc_state = PD_CC_DEBUG_ACC; - } else if (cc1 == TYPEC_CC_SRC_RD || - cc2 == TYPEC_CC_SRC_RD) { + } else if (TYPEC_CC_IS_RD(cc1) || + TYPEC_CC_IS_RD(cc2)) { /* UFP attached */ new_cc_state = PD_CC_UFP_ATTACHED; - } else if (cc1 == TYPEC_CC_SRC_RA && - cc2 == TYPEC_CC_SRC_RA) { + } else if (cc1 == TYPEC_CC_VOLT_RA && + cc2 == TYPEC_CC_VOLT_RA) { /* Audio accessory */ new_cc_state = PD_CC_AUDIO_ACC; } else { @@ -1429,7 +1427,7 @@ void pd_task(void) /* Debounce complete */ /* UFP is attached */ if (new_cc_state == PD_CC_UFP_ATTACHED) { - pd[port].polarity = (cc2 == TYPEC_CC_SRC_RD); + pd[port].polarity = (TYPEC_CC_IS_RD(cc2)); tcpm_set_polarity(port, pd[port].polarity); /* initial data role for source is DFP */ @@ -1483,16 +1481,15 @@ void pd_task(void) /* Combined audio / debug accessory state */ timeout = 100*MSEC; - cc1 = tcpm_get_cc(port, 0); - cc2 = tcpm_get_cc(port, 1); + tcpm_get_cc(port, &cc1, &cc2); /* If accessory becomes detached */ if ((pd[port].cc_state == PD_CC_AUDIO_ACC && - (cc1 != TYPEC_CC_SRC_RA || - cc2 != TYPEC_CC_SRC_RA)) || + (cc1 != TYPEC_CC_VOLT_RA || + cc2 != TYPEC_CC_VOLT_RA)) || (pd[port].cc_state == PD_CC_DEBUG_ACC && - (cc1 != TYPEC_CC_SRC_RD || - cc2 != TYPEC_CC_SRC_RD))) { + (!TYPEC_CC_IS_RD(cc1) || + !TYPEC_CC_IS_RD(cc2)))) { set_state(port, PD_STATE_SRC_DISCONNECTED); #ifdef CONFIG_CASE_CLOSED_DEBUG ccd_set_mode(CCD_MODE_DISABLED); @@ -1798,10 +1795,11 @@ void pd_task(void) break; case PD_STATE_SNK_DISCONNECTED: timeout = 10*MSEC; + tcpm_get_cc(port, &cc1, &cc2); /* Source connection monitoring */ - if (tcpm_get_cc(port, 0) != TYPEC_CC_SNK_OPEN || - tcpm_get_cc(port, 1) != TYPEC_CC_SNK_OPEN) { + if (cc1 != TYPEC_CC_VOLT_OPEN || + cc2 != TYPEC_CC_VOLT_OPEN) { pd[port].cc_state = PD_CC_NONE; hard_reset_count = 0; new_cc_state = PD_CC_DFP_ATTACHED; @@ -1830,10 +1828,9 @@ void pd_task(void) } break; case PD_STATE_SNK_DISCONNECTED_DEBOUNCE: - cc1 = tcpm_get_cc(port, 0); - cc2 = tcpm_get_cc(port, 1); - if (cc1 == TYPEC_CC_SNK_OPEN && - cc2 == TYPEC_CC_SNK_OPEN) { + tcpm_get_cc(port, &cc1, &cc2); + if (cc1 == TYPEC_CC_VOLT_OPEN && + cc2 == TYPEC_CC_VOLT_OPEN) { /* No connection any more */ set_state(port, PD_STATE_SNK_DISCONNECTED); timeout = 5*MSEC; @@ -1848,7 +1845,7 @@ void pd_task(void) break; /* We are attached */ - pd[port].polarity = (cc2 != TYPEC_CC_SNK_OPEN); + pd[port].polarity = (cc2 != TYPEC_CC_VOLT_OPEN); tcpm_set_polarity(port, pd[port].polarity); /* reset message ID on connection */ pd[port].msg_id = 0; @@ -1962,7 +1959,9 @@ void pd_task(void) timeout = PD_T_SINK_ADJ - PD_T_DEBOUNCE; /* Check if CC pull-up has changed */ - cc1 = tcpm_get_cc(port, pd[port].polarity); + tcpm_get_cc(port, &cc1, &cc2); + if (pd[port].polarity) + cc1 = cc2; if (typec_curr != get_typec_current_limit(cc1)) { /* debounce signal by requiring two reads */ if (typec_curr_change) { @@ -2318,8 +2317,10 @@ void pd_task(void) #endif if (pd[port].power_role == PD_ROLE_SOURCE) { /* Source: detect disconnect by monitoring CC */ - cc1 = tcpm_get_cc(port, pd[port].polarity); - if (cc1 == TYPEC_CC_SRC_OPEN) { + tcpm_get_cc(port, &cc1, &cc2); + if (pd[port].polarity) + cc1 = cc2; + if (cc1 == TYPEC_CC_VOLT_OPEN) { pd_power_supply_reset(port); set_state(port, PD_STATE_SRC_DISCONNECTED); /* Debouncing */ @@ -2350,18 +2351,18 @@ void tcpc_alert(void) /* loop over ports and check alert status */ for (i = 0; i < PD_PORT_COUNT; i++) { - status = tcpm_alert_status(i, TCPC_ALERT0); - if (status & TCPC_ALERT0_CC_STATUS) { + tcpm_alert_status(i, TCPC_REG_ALERT1, (uint8_t *)&status); + if (status & TCPC_REG_ALERT1_CC_STATUS) { /* CC status changed, wake task */ task_set_event(PORT_TO_TASK_ID(i), PD_EVENT_CC, 0); - } else if (status & TCPC_ALERT0_RX_STATUS) { + } else if (status & TCPC_REG_ALERT1_RX_STATUS) { /* message received */ task_set_event(PORT_TO_TASK_ID(i), PD_EVENT_RX, 0); - } else if (status & TCPC_ALERT0_RX_HARD_RST) { + } else if (status & TCPC_REG_ALERT1_RX_HARD_RST) { /* hard reset received */ execute_hard_reset(i); task_wake(PORT_TO_TASK_ID(i)); - } else if (status & TCPC_ALERT0_TX_COMPLETE) { + } else if (status & TCPC_REG_ALERT1_TX_COMPLETE) { /* transmit complete */ pd_transmit_complete(i, status); } 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) { diff --git a/common/usb_pd_tcpm.c b/common/usb_pd_tcpm.c new file mode 100644 index 0000000000..43f0bb8b98 --- /dev/null +++ b/common/usb_pd_tcpm.c @@ -0,0 +1,145 @@ +/* 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. + */ + +/* Type-C port manager */ + +#include "i2c.h" +#include "usb_pd.h" +#include "usb_pd_tcpc.h" +#include "usb_pd_tcpm.h" +#include "util.h" + +#include "console.h" + + +/* Convert port number to tcpc i2c address */ +#define I2C_ADDR_TCPC(p) (CONFIG_TCPC_I2C_BASE_ADDR + 2*(p)) + +static int tcpc_polarity, tcpc_vconn; + +int tcpm_get_cc(int port, int *cc1, int *cc2) +{ + int status; + int rv; + + rv = i2c_read16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + TCPC_REG_CC1_STATUS, &status); + + /* If i2c read fails, return error */ + if (rv < 0) + return rv; + + *cc1 = TCPC_REG_CC_STATUS_VOLT(status & 0xff); + *cc2 = TCPC_REG_CC_STATUS_VOLT((status >> 8) & 0xff); + + return rv; +} + +int tcpm_set_cc(int port, int pull) +{ + /* + * Set manual control of Rp/Rd, and set both CC lines to the same + * pull. + */ + /* TODO: set desired Rp strength */ + return i2c_write8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + TCPC_REG_ROLE_CTRL, + TCPC_REG_ROLE_CTRL_SET(0, 0, pull, pull)); +} + +int tcpm_set_polarity(int port, int polarity) +{ + /* Write new polarity, leave vconn enable flag untouched */ + tcpc_polarity = polarity; + return i2c_write8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + TCPC_REG_POWER_CTRL, + TCPC_REG_POWER_CTRL_SET(tcpc_polarity, tcpc_vconn)); +} + +int tcpm_set_vconn(int port, int enable) +{ + /* Write new vconn enable flag, leave polarity untouched */ + tcpc_vconn = enable; + return i2c_write8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + TCPC_REG_POWER_CTRL, + TCPC_REG_POWER_CTRL_SET(tcpc_polarity, tcpc_vconn)); +} + +int tcpm_set_msg_header(int port, int power_role, int data_role) +{ + return i2c_write8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + TCPC_REG_MSG_HDR_INFO, + TCPC_REG_MSG_HDR_INFO_SET(data_role, power_role)); +} + +int tcpm_alert_status(int port, int alert_reg, uint8_t *alert) +{ + return i2c_read8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + alert_reg, (int *)alert); +} + +int tcpm_get_message(int port, uint32_t *payload, int *head) +{ + int rv, cnt, reg = TCPC_REG_RX_DATA; + + /* TODO: need to first read TCPC_REG_RX_STATUS to check if SOP */ + + rv = i2c_read8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + TCPC_REG_RX_BYTE_CNT, &cnt); + + rv |= i2c_read16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + TCPC_REG_RX_HDR, (int *)head); + + /* If i2c read fails, return error */ + if (rv < 0) + return rv; + + if (cnt > 0) { + i2c_lock(I2C_PORT_TCPC, 1); + rv = i2c_xfer(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + (uint8_t *)®, 1, (uint8_t *)payload, + cnt, I2C_XFER_SINGLE); + i2c_lock(I2C_PORT_TCPC, 0); + } + + /* TODO: need to write to alert reg to clear status */ + + return rv; +} + +int tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header, + const uint32_t *data) +{ + int reg = TCPC_REG_TX_DATA; + int rv, cnt = 4*PD_HEADER_CNT(header); + + rv = i2c_write8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + TCPC_REG_TX_BYTE_CNT, cnt); + + rv |= i2c_write16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + TCPC_REG_TX_HDR, header); + + /* If i2c read fails, return error */ + if (rv < 0) + return rv; + + if (cnt > 0) { + i2c_lock(I2C_PORT_TCPC, 1); + rv = i2c_xfer(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + (uint8_t *)®, 1, NULL, 0, I2C_XFER_START); + rv |= i2c_xfer(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + (uint8_t *)data, cnt, NULL, 0, I2C_XFER_STOP); + i2c_lock(I2C_PORT_TCPC, 0); + } + + /* If i2c read fails, return error */ + if (rv < 0) + return rv; + + rv = i2c_write8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + TCPC_REG_TRANSMIT, TCPC_REG_TRANSMIT_SET(type)); + + return rv; +} diff --git a/common/usb_pd_tcpm_stub.c b/common/usb_pd_tcpm_stub.c index 8dd972bfec..10882db920 100644 --- a/common/usb_pd_tcpm_stub.c +++ b/common/usb_pd_tcpm_stub.c @@ -3,57 +3,59 @@ * found in the LICENSE file. */ +/* TCPM for MCU also running TCPC */ + #include "usb_pd.h" #include "usb_pd_tcpm.h" -extern int tcpc_alert_status(int port, int alert_reg); -extern int tcpc_get_cc(int port, int polarity); -extern void tcpc_set_cc(int port, int pull); -extern void tcpc_set_polarity(int port, int polarity); -extern void tcpc_set_vconn(int port, int enable); -extern void tcpc_set_msg_header(int port, int power_role, int data_role); +extern int tcpc_alert_status(int port, int alert_reg, uint8_t *alert); +extern int tcpc_get_cc(int port, int *cc1, int *cc2); +extern int tcpc_set_cc(int port, int pull); +extern int tcpc_set_polarity(int port, int polarity); +extern int tcpc_set_vconn(int port, int enable); +extern int tcpc_set_msg_header(int port, int power_role, int data_role); -extern int tcpc_get_message(int port, uint32_t *payload); -extern void tcpc_transmit(int port, enum tcpm_transmit_type type, - uint16_t header, const uint32_t *data); +extern int tcpc_get_message(int port, uint32_t *payload, int *head); +extern int tcpc_transmit(int port, enum tcpm_transmit_type type, + uint16_t header, const uint32_t *data); -int tcpm_get_cc(int port, int polarity) +int tcpm_get_cc(int port, int *cc1, int *cc2) { - return tcpc_get_cc(port, polarity); + return tcpc_get_cc(port, cc1, cc2); } -void tcpm_set_cc(int port, int pull) +int tcpm_set_cc(int port, int pull) { return tcpc_set_cc(port, pull); } -void tcpm_set_polarity(int port, int polarity) +int tcpm_set_polarity(int port, int polarity) { return tcpc_set_polarity(port, polarity); } -void tcpm_set_vconn(int port, int enable) +int tcpm_set_vconn(int port, int enable) { return tcpc_set_vconn(port, enable); } -void tcpm_set_msg_header(int port, int power_role, int data_role) +int tcpm_set_msg_header(int port, int power_role, int data_role) { return tcpc_set_msg_header(port, power_role, data_role); } -int tcpm_alert_status(int port, int alert_reg) +int tcpm_alert_status(int port, int alert_reg, uint8_t *alert) { - return tcpc_alert_status(port, alert_reg); + return tcpc_alert_status(port, alert_reg, alert); } -int tcpm_get_message(int port, uint32_t *payload) +int tcpm_get_message(int port, uint32_t *payload, int *head) { - return tcpc_get_message(port, payload); + return tcpc_get_message(port, payload, head); } -void tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header, - const uint32_t *data) +int tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header, + const uint32_t *data) { return tcpc_transmit(port, type, header, data); } diff --git a/include/usb_pd_tcpc.h b/include/usb_pd_tcpc.h new file mode 100644 index 0000000000..b954c6f7ae --- /dev/null +++ b/include/usb_pd_tcpc.h @@ -0,0 +1,26 @@ +/* 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 controller */ + +#ifndef __USB_PD_TCPC_H +#define __USB_PD_TCPC_H + +#ifndef CONFIG_TCPC_I2C_BASE_ADDR +#define CONFIG_TCPC_I2C_BASE_ADDR 0x9c +#endif + +/** + * Process incoming TCPCI I2C command + * + * @param read This is a read request. If 0, this is a write request. + * @param len Length of incoming payload + * @param payload Pointer to incoming and outgoing data + * @param send_response Function to call to send response if necessary + */ +void tcpc_i2c_process(int read, int port, int len, uint8_t *payload, + void (*send_response)(int)); + +#endif /* __USB_PD_TCPC_H */ diff --git a/include/usb_pd_tcpm.h b/include/usb_pd_tcpm.h index c598869593..8e935e155f 100644 --- a/include/usb_pd_tcpm.h +++ b/include/usb_pd_tcpm.h @@ -5,21 +5,8 @@ /* USB Power delivery port management */ -#define TCPC_ALERT0 0 -#define TCPC_ALERT0_I2C_WAKE (1<<7) -#define TCPC_ALERT0_POWER_STATUS (1<<6) -#define TCPC_ALERT0_CC_STATUS (1<<5) -#define TCPC_ALERT0_RX_STATUS (1<<4) -#define TCPC_ALERT0_RX_HARD_RST (1<<3) -#define TCPC_ALERT0_TX_SUCCESS (1<<2) -#define TCPC_ALERT0_TX_DISCARDED (1<<1) -#define TCPC_ALERT0_TX_FAILED (1<<0) -#define TCPC_ALERT0_TX_COMPLETE (TCPC_ALERT0_TX_SUCCESS | \ - TCPC_ALERT0_TX_FAILED | \ - TCPC_ALERT0_TX_FAILED) - -#define TCPC_ALERT1 1 -#define TCPC_ALERT1_GPIO_CHANGE (1<<3) +#ifndef __USB_PD_TCPM_H +#define __USB_PD_TCPM_H /* Default retry count for transmitting */ #define PD_RETRY_COUNT 3 @@ -27,6 +14,117 @@ /* Time to wait for TCPC to complete transmit */ #define PD_T_TCPC_TX_TIMEOUT (100*MSEC) +#define TCPC_REG_VENDOR_ID 0x0 +#define TCPC_REG_PRODUCT_ID 0x2 +#define TCPC_REG_BCD_DEV 0x4 +#define TCPC_REG_TC_REV 0x6 +#define TCPC_REG_PD_REV 0x8 +#define TCPC_REG_PD_INT_REV 0xa +#define TCPC_REG_DEV_CAP_1 0xc +#define TCPC_REG_DEV_CAP_2 0xd +#define TCPC_REG_DEV_CAP_3 0xe +#define TCPC_REG_DEV_CAP_4 0xf +#define TCPC_REG_ALERT1 0x10 +#define TCPC_REG_ALERT1_SLEEP_EXITED (1<<7) +#define TCPC_REG_ALERT1_POWER_STATUS (1<<6) +#define TCPC_REG_ALERT1_CC_STATUS (1<<5) +#define TCPC_REG_ALERT1_RX_STATUS (1<<4) +#define TCPC_REG_ALERT1_RX_HARD_RST (1<<3) +#define TCPC_REG_ALERT1_TX_SUCCESS (1<<2) +#define TCPC_REG_ALERT1_TX_DISCARDED (1<<1) +#define TCPC_REG_ALERT1_TX_FAILED (1<<0) +#define TCPC_REG_ALERT1_TX_COMPLETE (TCPC_REG_ALERT1_TX_SUCCESS | \ + TCPC_REG_ALERT1_TX_FAILED | \ + TCPC_REG_ALERT1_TX_FAILED) + +#define TCPC_REG_ALERT2 0x11 +#define TCPC_REG_ALERT3 0x12 +#define TCPC_REG_ALERT4 0x13 +#define TCPC_REG_ALERT_MASK_1 0x14 +#define TCPC_REG_ALERT_MASK_2 0x15 +#define TCPC_REG_POWER_STATUS_MASK 0x16 +#define TCPC_REG_CC1_STATUS 0x18 +#define TCPC_REG_CC2_STATUS 0x19 +#define TCPC_REG_CC_STATUS_SET(term, volt) \ + ((term) << 3 | volt) +#define TCPC_REG_CC_STATUS_TERM(reg) (((reg) & 0x38) >> 3) +#define TCPC_REG_CC_STATUS_VOLT(reg) ((reg) & 0x7) +enum tcpc_cc_termination_status { + TYPEC_CC_TERM_RA = 0, + TYPEC_CC_TERM_RP_DEF = 1, + TYPEC_CC_TERM_RP_1_5 = 2, + TYPEC_CC_TERM_RP_3_0 = 3, + TYPEC_CC_TERM_RD = 4, + TYPEC_CC_TERM_VCONN = 5, + TYPEC_CC_TERM_OPEN = 6 +}; +enum tcpc_cc_voltage_status { + TYPEC_CC_VOLT_RA = 0, + TYPEC_CC_VOLT_SNK_DEF = 1, + TYPEC_CC_VOLT_SNK_1_5 = 2, + TYPEC_CC_VOLT_SNK_3_0 = 3, + TYPEC_CC_VOLT_SRC_DEF = 4, + TYPEC_CC_VOLT_SRC_1_5 = 5, + TYPEC_CC_VOLT_SRC_3_0 = 6, + TYPEC_CC_VOLT_OPEN = 7 +}; +/* Check if CC voltage is within Rd */ +#define TYPEC_CC_IS_RD(cc) ((cc) >= TYPEC_CC_VOLT_SNK_DEF && \ + (cc) <= TYPEC_CC_VOLT_SNK_3_0) + +#define TCPC_REG_POWER_STATUS 0x1a +#define TCPC_REG_ROLE_CTRL 0x1b +#define TCPC_REG_ROLE_CTRL_SET(drp, rp, cc2, cc1) \ + ((drp) << 6 | (rp) << 4 | (cc2) << 2 | (cc1)) +#define TCPC_REG_ROLE_CTRL_CC2(reg) (((reg) & 0xc) >> 2) +#define TCPC_REG_ROLE_CTRL_CC1(reg) ((reg) & 0x3) +enum tcpc_cc_pull { + TYPEC_CC_RA = 0, + TYPEC_CC_RP = 1, + TYPEC_CC_RD = 2, + TYPEC_CC_OPEN = 3, +}; + +#define TCPC_REG_POWER_PATH_CTRL 0x1c +#define TCPC_REG_POWER_CTRL 0x1d +#define TCPC_REG_POWER_CTRL_SET(polarity, vconn) \ + ((polarity) << 4 | (vconn)) +#define TCPC_REG_POWER_CTRL_POLARITY(reg) (((reg) & 0x10) >> 4) +#define TCPC_REG_POWER_CTRL_VCONN(reg) ((reg) & 0x1) + +#define TCPC_REG_COMMAND 0x23 +#define TCPC_REG_MSG_HDR_INFO 0x2e +#define TCPC_REG_MSG_HDR_INFO_SET(drole, prole) \ + ((drole) << 3 | (PD_REV20 << 1) | (prole)) +#define TCPC_REG_MSG_HDR_INFO_DROLE(reg) (((reg) & 0x8) >> 3) +#define TCPC_REG_MSG_HDR_INFO_PROLE(reg) ((reg) & 0x1) + +#define TCPC_REG_RX_BYTE_CNT 0x2f +#define TCPC_REG_RX_STATUS 0x30 +#define TCPC_REG_RX_DETECT 0x31 +#define TCPC_REG_RX_HDR 0x32 +#define TCPC_REG_RX_DATA 0x34 /* through 0x4f */ + +#define TCPC_REG_TRANSMIT 0x50 +#define TCPC_REG_TRANSMIT_SET(type) \ + (PD_RETRY_COUNT << 4 | (type)) +#define TCPC_REG_TRANSMIT_RETRY(reg) (((reg) & 0x30) >> 4) +#define TCPC_REG_TRANSMIT_TYPE(reg) ((reg) & 0x7) +enum tcpm_transmit_type { + TRANSMIT_SOP = 0, + TRANSMIT_SOP_PRIME = 1, + TRANSMIT_SOP_PRIME_PRIME = 2, + TRANSMIT_SOP_DEBUG_PRIME = 3, + TRANSMIT_SOP_DEBUG_PRIME_PRIME = 4, + TRANSMIT_HARD_RESET = 5, + TRANSMIT_CABLE_RESET = 6, + TRANSMIT_BIST_MODE_2 = 7 +}; + +#define TCPC_REG_TX_BYTE_CNT 0x51 +#define TCPC_REG_TX_HDR 0x52 +#define TCPC_REG_TX_DATA 0x54 /* through 0x6f */ + /** * TCPC is asserting alert */ @@ -53,59 +151,53 @@ int tcpc_run(int port, int evt); * * @param port Type-C port number * @param alert_reg Alert register to read + * @param alert Pointer to location to store alert status + * + * @return EC_SUCCESS or error */ -int tcpm_alert_status(int port, int alert_reg); +int tcpm_alert_status(int port, int alert_reg, uint8_t *alert); /** * Read the CC line status. * * @param port Type-C port number - * @param polarity Polarity of the CC line to read + * @param cc1 pointer to CC status for CC1 + * @param cc2 pointer to CC status for CC2 * - * @return CC status from enum tcpc_cc_status + * @return EC_SUCCESS or error */ -enum tcpc_cc_status { -/* CC status when we are a source (we expose Rp) */ - TYPEC_CC_SRC_RA, - TYPEC_CC_SRC_RD, - TYPEC_CC_SRC_OPEN, -/* CC status when we are a sink (we expose Rd) */ - TYPEC_CC_SNK_PWR_3_0, - TYPEC_CC_SNK_PWR_1_5, - TYPEC_CC_SNK_PWR_DEFAULT, - TYPEC_CC_SNK_OPEN -}; -int tcpm_get_cc(int port, int polarity); +int tcpm_get_cc(int port, int *cc1, int *cc2); /** * 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 */ -enum tcpc_cc_pull { - TYPEC_CC_RP, - TYPEC_CC_RD, - TYPEC_CC_OPEN -}; -void tcpm_set_cc(int port, int pull); +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 */ -void tcpm_set_polarity(int port, int polarity); +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 */ -void tcpm_set_vconn(int port, int enable); +int tcpm_set_vconn(int port, int enable); /** * Set PD message header to use for goodCRC @@ -113,18 +205,21 @@ void tcpm_set_vconn(int port, int enable); * @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 */ -void tcpm_set_msg_header(int port, int power_role, int data_role); +int tcpm_set_msg_header(int port, int power_role, int data_role); /** * 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 header of message + * @return EC_SUCCESS or error */ -int tcpm_get_message(int port, uint32_t *payload); +int tcpm_get_message(int port, uint32_t *payload, int *head); /** * Transmit PD message @@ -134,17 +229,10 @@ int tcpm_get_message(int port, uint32_t *payload); * @param header Packet header * @param cnt Number of bytes in payload * @param data Payload + * + * @return EC_SUCCESS or error */ -enum tcpm_transmit_type { - TRANSMIT_SOP, - TRANSMIT_SOP_PRIME, - TRANSMIT_SOP_PRIME_PRIME, - TRANSMIT_SOP_DEBUG_PRIME, - TRANSMIT_SOP_DEBUG_PRIME_PRIME, - TRANSMIT_HARD_RESET, - TRANSMIT_CABLE_RESET, - TRANSMIT_BIST_MODE_2 -}; -void tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header, +int tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header, const uint32_t *data); +#endif /* __USB_PD_TCPM_H */ |