summaryrefslogtreecommitdiff
path: root/common
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 /common
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
Diffstat (limited to 'common')
-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
4 files changed, 392 insertions, 96 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);
}