summaryrefslogtreecommitdiff
path: root/common/usb_pd_tcpc.c
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/usb_pd_tcpc.c
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/usb_pd_tcpc.c')
-rw-r--r--common/usb_pd_tcpc.c218
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)
{