summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlec Berg <alecaberg@chromium.org>2015-05-04 09:22:42 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-05-22 18:12:02 +0000
commit37a23855f7bba522f4cb300d8f4547764e5e3578 (patch)
tree5c17c6562bfb1231e164c75800e0d6ef3f3de5d0
parent608fa5efd868325ce85fca1f777a89c0909ea032 (diff)
downloadchrome-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
-rw-r--r--common/usb_pd_protocol.c81
-rw-r--r--common/usb_pd_tcpc.c218
-rw-r--r--common/usb_pd_tcpm.c145
-rw-r--r--common/usb_pd_tcpm_stub.c44
-rw-r--r--include/usb_pd_tcpc.h26
-rw-r--r--include/usb_pd_tcpm.h192
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 *)&reg, 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 *)&reg, 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 */