diff options
author | Scott <scollyer@chromium.org> | 2015-06-04 16:03:28 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-06-23 19:18:44 +0000 |
commit | 99e964c018eec1cba83022361866dd0b14d47610 (patch) | |
tree | 67cb7b386101bbd2abf2f45fc66ffdfd8d1bceb9 /common | |
parent | 0e2176304f3af2b78e8e0b12dab8feb82abccd8f (diff) | |
download | chrome-ec-99e964c018eec1cba83022361866dd0b14d47610.tar.gz |
pd: Add support for TCPC Alert and Alert_Mask registers
Changed the alert function to hold the ec_int line until
all of the alert bits are cleared. Added support for the
alert_mask register. In addition, created ec_int_status
variable to distinguish which of 3 ec_int sources is
driving the pd_mcu_int line.
BUG=none
BRANCH=tot
TEST=Tested Zinger to Glados and Zinger to Samus and verified
that it established a power contract in both cases. Did not
test Oak, but put exact same changes in board.c as in glados.
Change-Id: I372e75b8fd5d66a0c01db18b46100b86fd9ac064
Signed-off-by: Scott Collyer <scollyer@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/278256
Reviewed-by: Alec Berg <alecaberg@chromium.org>
Diffstat (limited to 'common')
-rw-r--r-- | common/host_command_pd.c | 58 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 83 | ||||
-rw-r--r-- | common/usb_pd_tcpc.c | 94 | ||||
-rw-r--r-- | common/usb_pd_tcpm.c | 27 | ||||
-rw-r--r-- | common/usb_pd_tcpm_stub.c | 21 |
5 files changed, 206 insertions, 77 deletions
diff --git a/common/host_command_pd.c b/common/host_command_pd.c index 33bdb52745..a10a591637 100644 --- a/common/host_command_pd.c +++ b/common/host_command_pd.c @@ -8,6 +8,7 @@ #include "charge_state.h" #include "common.h" #include "console.h" +#include "gpio.h" #include "host_command.h" #include "lightbar.h" #include "panic.h" @@ -45,11 +46,31 @@ void host_command_pd_send_status(enum pd_charge_state new_chg_state) task_set_event(TASK_ID_PDCMD, TASK_EVENT_EXCHANGE_PD_STATUS, 0); } +static int pd_send_host_command(struct ec_params_pd_status *ec_status, + struct ec_response_pd_status *pd_status) +{ + int rv; + + rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 1, ec_status, + sizeof(struct ec_params_pd_status), pd_status, + sizeof(struct ec_response_pd_status)); + + /* If PD doesn't support new command version, try old version */ + if (rv == -EC_RES_INVALID_VERSION) + rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 0, ec_status, + sizeof(struct ec_params_pd_status), pd_status, + sizeof(struct ec_response_pd_status)); + return rv; +} + static void pd_exchange_status(void) { struct ec_params_pd_status ec_status; struct ec_response_pd_status pd_status; int rv = 0; +#ifdef CONFIG_USB_PD_TCPM_TCPCI + int loop_count; +#endif #ifdef CONFIG_HOSTCMD_PD_PANIC static int pd_in_rw; #endif @@ -63,15 +84,7 @@ static void pd_exchange_status(void) else ec_status.batt_soc = -1; - rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 1, &ec_status, - sizeof(struct ec_params_pd_status), &pd_status, - sizeof(struct ec_response_pd_status)); - - /* If PD doesn't support new command version, try old version */ - if (rv == -EC_RES_INVALID_VERSION) - rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 0, &ec_status, - sizeof(struct ec_params_pd_status), &pd_status, - sizeof(struct ec_response_pd_status)); + rv = pd_send_host_command(&ec_status, &pd_status); if (rv < 0) { CPRINTS("Host command to PD MCU failed"); @@ -124,7 +137,32 @@ static void pd_exchange_status(void) host_set_single_event(EC_HOST_EVENT_PD_MCU); #ifdef CONFIG_USB_PD_TCPM_TCPCI - tcpc_alert(); + /* + * Loop here until all Alerts from either port have been handled. + * This is necessary to prevent the case where Alert bits are set + * and the GPIO line is held low, which would prevent a new edge + * event which prevents tcpc_alert() from being called and that + * in turn prevents the GPIO line from being released. + */ + while (!gpio_get_level(GPIO_PD_MCU_INT)) { + /* + * If TCPC is not present on this MCU, then check + * to see if either PD port is signallng an + * Alert# to the TCPM. + */ + if (pd_status.status & PD_STATUS_TCPC_ALERT_0) + tcpc_alert(0); + if (pd_status.status & PD_STATUS_TCPC_ALERT_1) + tcpc_alert(1); + if (loop_count++) { + usleep(50*MSEC); + rv = pd_send_host_command(&ec_status, &pd_status); + if (rv < 0) { + CPRINTS("Host command to PD MCU failed"); + return; + } + } + } #endif } diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index f87e5010a5..deeb683630 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -304,7 +304,7 @@ static void inc_id(int port) static void pd_transmit_complete(int port, int status) { - if (status & TCPC_REG_ALERT1_TX_SUCCESS) + if (status & TCPC_REG_ALERT_TX_SUCCESS) inc_id(port); pd[port].tx_status = status; @@ -329,7 +329,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_REG_ALERT1_TX_SUCCESS ? 1 : -1; + return pd[port].tx_status & TCPC_REG_ALERT_TX_SUCCESS ? 1 : -1; } static void pd_update_roles(int port) @@ -1299,6 +1299,25 @@ void pd_set_new_power_request(int port) #error "Backwards compatible DFP does not support USB" #endif +int tcpm_init_alert_mask(int port) +{ + uint16_t mask; + int rv; + + /* + * Create mask of alert events that will cause the TCPC to + * signal the TCPM via the Alert# gpio line. + */ + mask = TCPC_REG_ALERT_TX_SUCCESS | TCPC_REG_ALERT_TX_FAILED | + TCPC_REG_ALERT_TX_DISCARDED | TCPC_REG_ALERT_RX_STATUS | + TCPC_REG_ALERT_RX_HARD_RST | TCPC_REG_ALERT_CC_STATUS; + /* Set the alert mask in TCPC */ + rv = tcpm_alert_mask_set(port, TCPC_REG_ALERT_MASK, mask); + + return rv; +} + + void pd_task(void) { int head; @@ -1330,6 +1349,9 @@ void pd_task(void) tcpm_init(port); CPRINTF("[%T TCPC p%d ready]\n", port); + /* Initialize TCPC alert mask register via the TCPM */ + tcpm_init_alert_mask(port); + /* Disable TCPC RX until connection is established */ tcpm_set_rx_enable(port, 0); @@ -2389,39 +2411,38 @@ void pd_task(void) } } -void tcpc_alert(void) +void tcpc_alert(int port) { - int status, i; + int status; - /* loop over ports and check alert status */ - for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++) { - 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(PD_PORT_TO_TASK_ID(i), PD_EVENT_CC, 0); - } - if (status & TCPC_REG_ALERT1_RX_STATUS) { - /* message received */ - /* - * If TCPC is compiled in, then we will have already - * received PD_EVENT_RX from phy layer in - * pd_rx_event(), so we don't need to set another - * event. If TCPC is not running on this MCU, then - * this needs to wake the PD task. - */ + /* Read the Alert register from the TCPC */ + tcpm_alert_status(port, TCPC_REG_ALERT, (uint16_t *)&status); + + if (status & TCPC_REG_ALERT_CC_STATUS) { + /* CC status changed, wake task */ + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0); + } + if (status & TCPC_REG_ALERT_RX_STATUS) { + /* message received */ + /* + * If TCPC is compiled in, then we will have already + * received PD_EVENT_RX from phy layer in + * pd_rx_event(), so we don't need to set another + * event. If TCPC is not running on this MCU, then + * this needs to wake the PD task. + */ #ifndef CONFIG_USB_PD_TCPC - task_set_event(PD_PORT_TO_TASK_ID(i), PD_EVENT_RX, 0); + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_RX, 0); #endif - } - if (status & TCPC_REG_ALERT1_RX_HARD_RST) { - /* hard reset received */ - execute_hard_reset(i); - task_wake(PD_PORT_TO_TASK_ID(i)); - } - if (status & TCPC_REG_ALERT1_TX_COMPLETE) { - /* transmit complete */ - pd_transmit_complete(i, status); - } + } + if (status & TCPC_REG_ALERT_RX_HARD_RST) { + /* hard reset received */ + execute_hard_reset(port); + task_wake(PD_PORT_TO_TASK_ID(port)); + } + if (status & TCPC_REG_ALERT_TX_COMPLETE) { + /* transmit complete */ + pd_transmit_complete(port, status); } } diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c index c9cbf03896..307f7c7c0e 100644 --- a/common/usb_pd_tcpc.c +++ b/common/usb_pd_tcpc.c @@ -187,7 +187,7 @@ static const uint8_t dec4b5b[] = { #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) +#define ALERT_REG_TO_INDEX(reg) (reg - TCPC_REG_ALERT) /* PD transmit errors */ enum pd_tx_errors { @@ -209,7 +209,8 @@ static struct pd_port_controller { /* CC status */ uint8_t cc_status[2]; /* TCPC alert status */ - uint8_t alert[2]; + uint16_t alert; + uint16_t alert_mask; /* RX enabled */ uint8_t rx_enabled; @@ -696,10 +697,16 @@ static int cc_voltage_to_status(int port, int cc_volt) return 0; } -static void alert(int port, int reg, int mask) +static void alert(int port, int mask) { - pd[port].alert[ALERT_REG_TO_INDEX(reg)] |= mask; - tcpc_alert(); + /* Always update the Alert status register */ + pd[port].alert |= mask; + /* + * Only send interrupt to TCPM if corresponding + * bit in the alert_enable register is set. + */ + if (pd[port].alert_mask & mask) + tcpc_alert(port); } void tcpc_init(int port) @@ -726,10 +733,9 @@ int tcpc_run(int port, int evt) handle_request(port, pd[port].rx_head, pd[port].rx_payload); - alert(port, TCPC_REG_ALERT1, TCPC_REG_ALERT1_RX_STATUS); + alert(port, TCPC_REG_ALERT_RX_STATUS); } else if (pd[port].rx_head == PD_RX_ERR_HARD_RESET) { - alert(port, TCPC_REG_ALERT1, - TCPC_REG_ALERT1_RX_HARD_RST); + alert(port, TCPC_REG_ALERT_RX_HARD_RST); } } @@ -754,14 +760,11 @@ int tcpc_run(int port, int evt) /* send appropriate alert for tx completion */ if (res >= 0) - alert(port, TCPC_REG_ALERT1, - TCPC_REG_ALERT1_TX_SUCCESS); + alert(port, TCPC_REG_ALERT_TX_SUCCESS); else if (res == PD_TX_ERR_GOODCRC) - alert(port, TCPC_REG_ALERT1, - TCPC_REG_ALERT1_TX_FAILED); + alert(port, TCPC_REG_ALERT_TX_FAILED); else - alert(port, TCPC_REG_ALERT1, - TCPC_REG_ALERT1_TX_DISCARDED); + alert(port, TCPC_REG_ALERT_TX_DISCARDED); } else { /* If we have nothing to transmit, then sample CC lines */ @@ -778,8 +781,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_REG_ALERT1, - TCPC_REG_ALERT1_CC_STATUS); + alert(port, TCPC_REG_ALERT_CC_STATUS); } } } @@ -820,17 +822,33 @@ void pd_rx_event(int port) task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_RX, 0); } -int tcpc_alert_status(int port, int alert_reg, uint8_t *alert) +int tcpc_alert_status(int port, uint16_t *alert) { - 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_TO_INDEX(alert_reg)] = 0; + /* return the value of the TCPC Alert register */ + uint16_t ret = pd[port].alert; *alert = ret; return EC_SUCCESS; } +int tcpc_alert_status_clear(int port, uint16_t mask) +{ + /* clear only the bits specified by the TCPM */ + pd[port].alert &= ~mask; +#ifndef CONFIG_USB_POWER_DELIVERY + /* Set Alert# inactive if all alert bits clear */ + if (!pd[port].alert) + tcpc_alert_clear(port); +#endif + return EC_SUCCESS; +} + +int tcpc_alert_mask_update(int port, uint16_t mask) +{ + /* Update the alert mask as specificied by the TCPM */ + pd[port].alert_mask = mask; + return EC_SUCCESS; +} + int tcpc_set_cc(int port, int pull) { /* If CC pull resistor not changing, then nothing to do */ @@ -929,6 +947,7 @@ int tcpc_get_message(int port, uint32_t *payload, int *head) #ifndef CONFIG_USB_POWER_DELIVERY static void tcpc_i2c_write(int port, int reg, int len, uint8_t *payload) { + uint16_t alert; switch (reg) { case TCPC_REG_ROLE_CTRL: tcpc_set_cc(port, TCPC_REG_ROLE_CTRL_CC1(payload[1])); @@ -943,9 +962,16 @@ static void tcpc_i2c_write(int port, int reg, int len, uint8_t *payload) 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 */ + case TCPC_REG_ALERT: + alert = payload[1]; + alert |= (payload[2] << 8); + /* clear alert bits specified by the TCPM */ + tcpc_alert_status_clear(port, alert); + break; + case TCPC_REG_ALERT_MASK: + alert = payload[1]; + alert |= (payload[2] << 8); + tcpc_alert_mask_update(port, alert); break; case TCPC_REG_RX_DETECT: tcpc_set_rx_enable(port, payload[1] & @@ -967,6 +993,7 @@ static void tcpc_i2c_write(int port, int reg, int len, uint8_t *payload) static int tcpc_i2c_read(int port, int reg, uint8_t *payload) { int cc1, cc2; + uint16_t alert; switch (reg) { case TCPC_REG_VENDOR_ID: @@ -995,14 +1022,19 @@ static int tcpc_i2c_read(int port, int reg, uint8_t *payload) 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_DETECT: payload[0] = pd[port].rx_enabled ? TCPC_REG_RX_DETECT_SOP_HRST_MASK : 0; return 1; + case TCPC_REG_ALERT: + tcpc_alert_status(port, &alert); + payload[0] = alert & 0xff; + payload[1] = (alert >> 8) & 0xff; + return 2; + case TCPC_REG_ALERT_MASK: + payload[0] = pd[port].alert_mask & 0xff; + payload[1] = (pd[port].alert_mask >> 8) & 0xff; + return 2; case TCPC_REG_RX_BYTE_CNT: payload[0] = 4*PD_HEADER_CNT(pd[port].rx_head); return 1; @@ -1113,11 +1145,11 @@ static int command_tcpc(int argc, char **argv) return EC_SUCCESS; } else if (!strncasecmp(argv[2], "state", 5)) { ccprintf("Port C%d, %s - CC:%d, CC0:%d, CC1:%d, " - "Alert: 0x%02x 0x%02x\n", port, + "Alert: 0x%02x\n", port, pd[port].rx_enabled ? "Ena" : "Dis", pd[port].cc_pull, pd[port].cc_status[0], pd[port].cc_status[1], - pd[port].alert[0], pd[port].alert[1]); + pd[port].alert); } return EC_SUCCESS; diff --git a/common/usb_pd_tcpm.c b/common/usb_pd_tcpm.c index d7458fabfe..7d2c38774b 100644 --- a/common/usb_pd_tcpm.c +++ b/common/usb_pd_tcpm.c @@ -92,10 +92,20 @@ int tcpm_set_msg_header(int port, int power_role, int data_role) TCPC_REG_MSG_HDR_INFO_SET(data_role, power_role)); } -int tcpm_alert_status(int port, int alert_reg, uint8_t *alert) +int tcpm_alert_status(int port, int alert_reg, uint16_t *alert) { - return i2c_read8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + int rv; + /* Read TCPC Alert register */ + rv = i2c_read16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), alert_reg, (int *)alert); + /* + * The PD protocol layer will process all alert bits + * returned by this function. Therefore, these bits + * can now be cleared from the TCPC register. + */ + i2c_write16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + alert_reg, *alert); + return rv; } int tcpm_set_rx_enable(int port, int enable) @@ -106,6 +116,19 @@ int tcpm_set_rx_enable(int port, int enable) enable ? TCPC_REG_RX_DETECT_SOP_HRST_MASK : 0); } +int tcpm_alert_mask_set(int port, int reg, uint16_t mask) +{ + int rv; + /* write to the Alert Mask register */ + rv = i2c_write16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port), + reg, mask); + + if (rv) + return rv; + + return rv; +} + int tcpm_get_message(int port, uint32_t *payload, int *head) { int rv, cnt, reg = TCPC_REG_RX_DATA; diff --git a/common/usb_pd_tcpm_stub.c b/common/usb_pd_tcpm_stub.c index dc0843fa10..60651670a5 100644 --- a/common/usb_pd_tcpm_stub.c +++ b/common/usb_pd_tcpm_stub.c @@ -8,7 +8,11 @@ #include "usb_pd.h" #include "usb_pd_tcpm.h" -extern int tcpc_alert_status(int port, int alert_reg, uint8_t *alert); +#include "console.h" + +extern int tcpc_alert_status(int port, uint16_t *alert); +extern int tcpc_alert_status_clear(int port, uint16_t mask); +extern int tcpc_alert_mask_update(int port, uint16_t mask); 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); @@ -51,9 +55,15 @@ 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, uint8_t *alert) +int tcpm_alert_status(int port, int alert_reg, uint16_t *alert) { - return tcpc_alert_status(port, alert_reg, alert); + int rv; + + /* Read TCPC Alert register */ + rv = tcpc_alert_status(port, alert); + /* Clear all bits being processed by the protocol layer */ + tcpc_alert_status_clear(port, *alert); + return rv; } int tcpm_set_rx_enable(int port, int enable) @@ -61,6 +71,11 @@ int tcpm_set_rx_enable(int port, int enable) return tcpc_set_rx_enable(port, enable); } +int tcpm_alert_mask_set(int port, int reg, uint16_t mask) +{ + return tcpc_alert_mask_update(port, mask); +} + int tcpm_get_message(int port, uint32_t *payload, int *head) { return tcpc_get_message(port, payload, head); |