summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott <scollyer@chromium.org>2015-07-30 15:10:11 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-08-21 08:10:03 +0000
commitd862dd05982198a2f5d450820a02172e0ba6396b (patch)
treec9c96e5ea91537a3aae99370952eb2a13156a1af
parent646760bb20fde94683d3be2fa4b046d47933d85f (diff)
downloadchrome-ec-d862dd05982198a2f5d450820a02172e0ba6396b.tar.gz
pd: Enable detection of VBUS via the TCPM/TCPCI interface
Modified TCPC layer to utilize the Power_Status and Power_Status_Mask registers. VBUS status is stored in Power_Status and when a change is detected, it's communicated to the TCPM via the ALERT# line. BUG=chrome-os-partner:43440 BRANCH=none TEST=Tested the feature on Glados and Oak connecting to both Ziger and Samus. Verfied that VBUS status is communicated via the TCPCI and that PD contracts are established without using the VBUS_WAKE GPIO lines on Glados and Oak. Change-Id: Ie5aa32eecc887f3cb00880a285f1e710b7064384 Signed-off-by: Scott Collyer <scollyer@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/289931 Reviewed-by: Rong Chang <rongchang@chromium.org> Reviewed-by: Alec Berg <alecaberg@chromium.org> Commit-Queue: Rong Chang <rongchang@chromium.org> Tested-by: Rong Chang <rongchang@chromium.org>
-rw-r--r--common/usb_pd_protocol.c20
-rw-r--r--common/usb_pd_tcpc.c51
-rw-r--r--driver/tcpm/stub.c6
-rw-r--r--driver/tcpm/tcpci.c73
-rw-r--r--driver/tcpm/tcpci.h1
-rw-r--r--include/config.h7
-rw-r--r--include/usb_pd_tcpc.h8
-rw-r--r--include/usb_pd_tcpm.h19
8 files changed, 167 insertions, 18 deletions
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 3b5cd5b445..b124ad0245 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -20,6 +20,7 @@
#include "task.h"
#include "timer.h"
#include "util.h"
+#include "usb_charge.h"
#include "usb_mux.h"
#include "usb_pd.h"
#include "usb_pd_tcpm.h"
@@ -211,6 +212,15 @@ int pd_is_connected(int port)
}
#ifdef CONFIG_USB_PD_DUAL_ROLE
+static inline int pd_is_vbus_present(int port)
+{
+#ifdef CONFIG_USB_PD_TCPM_VBUS
+ return tcpm_get_vbus_level(port);
+#else
+ return pd_snk_is_vbus_provided(port);
+#endif
+}
+
static int pd_snk_debug_acc_toggle(int port)
{
#ifdef CONFIG_CASE_CLOSED_DEBUG
@@ -219,7 +229,7 @@ static int pd_snk_debug_acc_toggle(int port)
* (without having seen Rp before), that might be a powered debug
* accessory, let's toggle to source to try to detect it.
*/
- return pd_snk_is_vbus_provided(port);
+ return pd_is_vbus_present(port);
#else
/* Debug accessories not supported, never toggle */
return 0;
@@ -1966,7 +1976,7 @@ void pd_task(void)
/* Wait for CC debounce and VBUS present */
if (get_time().val < pd[port].cc_debounce ||
- !pd_snk_is_vbus_provided(port))
+ !pd_is_vbus_present(port))
break;
if (pd_try_src_enable &&
@@ -2044,7 +2054,7 @@ void pd_task(void)
PD_STATE_SNK_DISCOVERY);
}
- if (!pd_snk_is_vbus_provided(port) &&
+ if (!pd_is_vbus_present(port) &&
!snk_hard_reset_vbus_off) {
/* VBUS has gone low, reset timeout */
snk_hard_reset_vbus_off = 1;
@@ -2054,7 +2064,7 @@ void pd_task(void)
PD_T_SRC_TURN_ON,
PD_STATE_SNK_DISCONNECTED);
}
- if (pd_snk_is_vbus_provided(port) &&
+ if (pd_is_vbus_present(port) &&
snk_hard_reset_vbus_off) {
/* VBUS went high again */
set_state(port, PD_STATE_SNK_DISCOVERY);
@@ -2503,7 +2513,7 @@ void pd_task(void)
* a hard reset.
*/
if (pd[port].power_role == PD_ROLE_SINK &&
- !pd_snk_is_vbus_provided(port) &&
+ !pd_is_vbus_present(port) &&
pd[port].task_state != PD_STATE_SNK_HARD_RESET_RECOVER &&
pd[port].task_state != PD_STATE_HARD_RESET_EXECUTE) {
/* Sink: detect disconnect by monitoring VBUS */
diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c
index d7314b1a53..5c42640d5b 100644
--- a/common/usb_pd_tcpc.c
+++ b/common/usb_pd_tcpc.c
@@ -227,6 +227,9 @@ static struct pd_port_controller {
uint8_t rx_enabled;
/* TCPC flags */
uint8_t flags;
+ /* Power status */
+ uint8_t power_status;
+ uint8_t power_status_mask;
/* Last received */
int rx_head[RX_BUFFER_SIZE+1];
@@ -958,6 +961,29 @@ int tcpc_set_polarity(int port, int polarity)
return EC_SUCCESS;
}
+#ifdef CONFIG_USB_PD_TCPM_VBUS
+static int tcpc_set_power_status(int port, int vbus_present)
+{
+ /* Update VBUS present bit */
+ if (vbus_present)
+ pd[port].power_status |= TCPC_REG_POWER_VBUS_PRES;
+ else
+ pd[port].power_status &= ~TCPC_REG_POWER_VBUS_PRES;
+
+ /* Set bit Port Power Status bit in Alert register */
+ if (pd[port].power_status_mask & TCPC_REG_POWER_VBUS_PRES)
+ alert(port, TCPC_REG_ALERT_POWER_STATUS);
+
+ return EC_SUCCESS;
+}
+#endif /* CONFIG_USB_PD_TCPM_VBUS */
+
+int tcpc_set_power_status_mask(int port, uint8_t mask)
+{
+ pd[port].power_status_mask = mask;
+ return EC_SUCCESS;
+}
+
int tcpc_set_vconn(int port, int enable)
{
#ifdef CONFIG_USBC_VCONN
@@ -1011,6 +1037,25 @@ int tcpc_get_message(int port, uint32_t *payload, int *head)
return EC_SUCCESS;
}
+
+#ifdef CONFIG_USB_PD_TCPM_VBUS
+void pd_vbus_evt_p0(enum gpio_signal signal)
+{
+ tcpc_set_power_status(TASK_ID_TO_PD_PORT(TASK_ID_PD_C0),
+ !gpio_get_level(GPIO_USB_C0_VBUS_WAKE_L));
+ task_wake(TASK_ID_PD_C0);
+}
+
+#if CONFIG_USB_PD_PORT_COUNT >= 2
+void pd_vbus_evt_p1(enum gpio_signal signal)
+{
+ tcpc_set_power_status(TASK_ID_TO_PD_PORT(TASK_ID_PD_C1),
+ !gpio_get_level(GPIO_USB_C1_VBUS_WAKE_L));
+ task_wake(TASK_ID_PD_C1);
+}
+#endif /* PD_PORT_COUNT >= 2 */
+#endif /* CONFIG_USB_PD_TCPM_VBUS */
+
#ifndef CONFIG_USB_POWER_DELIVERY
static void tcpc_i2c_write(int port, int reg, int len, uint8_t *payload)
{
@@ -1044,6 +1089,9 @@ static void tcpc_i2c_write(int port, int reg, int len, uint8_t *payload)
tcpc_set_rx_enable(port, payload[1] &
TCPC_REG_RX_DETECT_SOP_HRST_MASK);
break;
+ case TCPC_REG_POWER_STATUS_MASK:
+ tcpc_set_power_status_mask(port, payload[1]);
+ break;
case TCPC_REG_TX_HDR:
pd[port].tx_head = (payload[2] << 8) | payload[1];
break;
@@ -1114,6 +1162,9 @@ static int tcpc_i2c_read(int port, int reg, uint8_t *payload)
memcpy(payload, pd[port].rx_payload[pd[port].rx_buf_tail],
sizeof(pd[port].rx_payload[pd[port].rx_buf_tail]));
return sizeof(pd[port].rx_payload[pd[port].rx_buf_tail]);
+ case TCPC_REG_POWER_STATUS:
+ payload[0] = pd[port].power_status;
+ return 1;
case TCPC_REG_TX_BYTE_CNT:
payload[0] = PD_HEADER_CNT(pd[port].tx_head);
return 1;
diff --git a/driver/tcpm/stub.c b/driver/tcpm/stub.c
index 444730fc18..35f0329484 100644
--- a/driver/tcpm/stub.c
+++ b/driver/tcpm/stub.c
@@ -16,6 +16,7 @@ extern int tcpc_alert_mask_set(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);
+extern int tcpc_set_power_status_mask(int port, uint8_t mask);
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_set_rx_enable(int port, int enable);
@@ -63,6 +64,11 @@ int tcpm_set_polarity(int port, int polarity)
return tcpc_set_polarity(port, polarity);
}
+int tcpm_set_power_status_mask(int port, uint8_t mask)
+{
+ return tcpc_set_power_status_mask(port, mask);
+}
+
int tcpm_set_vconn(int port, int enable)
{
return tcpc_set_vconn(port, enable);
diff --git a/driver/tcpm/tcpci.c b/driver/tcpm/tcpci.c
index 606b84e31c..939ab9ccf2 100644
--- a/driver/tcpm/tcpci.c
+++ b/driver/tcpm/tcpci.c
@@ -9,6 +9,7 @@
#include "task.h"
#include "tcpci.h"
#include "timer.h"
+#include "usb_charge.h"
#include "usb_pd.h"
#include "usb_pd_tcpc.h"
#include "usb_pd_tcpm.h"
@@ -17,7 +18,7 @@
/* 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;
+static int tcpc_polarity, tcpc_vconn, tcpc_vbus[CONFIG_USB_PD_PORT_COUNT];
static int init_alert_mask(int port)
{
@@ -30,13 +31,30 @@ static int init_alert_mask(int port)
*/
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;
+ TCPC_REG_ALERT_RX_HARD_RST | TCPC_REG_ALERT_CC_STATUS
+#ifdef CONFIG_USB_PD_TCPM_VBUS
+ | TCPC_REG_ALERT_POWER_STATUS
+#endif
+ ;
/* Set the alert mask in TCPC */
rv = tcpm_alert_mask_set(port, mask);
return rv;
}
+#ifdef CONFIG_USB_PD_TCPM_VBUS
+static int init_power_status_mask(int port)
+{
+ uint8_t mask;
+ int rv;
+
+ mask = TCPC_REG_POWER_VBUS_PRES;
+ rv = tcpm_set_power_status_mask(port, mask);
+
+ return rv;
+}
+#endif
+
int tcpm_init(int port)
{
int rv, err = 0;
@@ -52,6 +70,10 @@ int tcpm_init(int port)
if (rv == EC_SUCCESS && !(err & TCPC_REG_ERROR_STATUS_UNINIT)) {
i2c_write16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port),
TCPC_REG_ALERT, 0xff);
+#ifdef CONFIG_USB_PD_TCPM_VBUS
+ /* Initialize power_status_mask */
+ init_power_status_mask(port);
+#endif
return init_alert_mask(port);
}
msleep(10);
@@ -85,6 +107,12 @@ int tcpm_get_cc(int port, int *cc1, int *cc2)
return rv;
}
+int tcpm_get_power_status(int port, int *status)
+{
+ return i2c_read8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port),
+ TCPC_REG_POWER_STATUS, status);
+}
+
int tcpm_set_cc(int port, int pull)
{
/*
@@ -124,11 +152,9 @@ int tcpm_set_msg_header(int port, int power_role, int data_role)
int tcpm_alert_status(int port, int *alert)
{
- int rv;
/* Read TCPC Alert register */
- rv = i2c_read16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port),
- TCPC_REG_ALERT, alert);
- return rv;
+ return i2c_read16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port),
+ TCPC_REG_ALERT, alert);
}
int tcpm_set_rx_enable(int port, int enable)
@@ -139,18 +165,26 @@ 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, uint16_t mask)
+int tcpm_set_power_status_mask(int port, uint8_t mask)
{
- int rv;
/* write to the Alert Mask register */
- rv = i2c_write16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port),
- TCPC_REG_ALERT_MASK, mask);
+ return i2c_write8(I2C_PORT_TCPC, I2C_ADDR_TCPC(port),
+ TCPC_REG_POWER_STATUS_MASK , mask);
+}
- if (rv)
- return rv;
+int tcpm_alert_mask_set(int port, uint16_t mask)
+{
+ /* write to the Alert Mask register */
+ return i2c_write16(I2C_PORT_TCPC, I2C_ADDR_TCPC(port),
+ TCPC_REG_ALERT_MASK, mask);
+}
- return rv;
+#ifdef CONFIG_USB_PD_TCPM_VBUS
+int tcpm_get_vbus_level(int port)
+{
+ return tcpc_vbus[port];
}
+#endif
int tcpm_get_message(int port, uint32_t *payload, int *head)
{
@@ -215,6 +249,7 @@ int tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header,
void tcpc_alert(int port)
{
int status;
+ int power_status;
/* Read the Alert register from the TCPC */
tcpm_alert_status(port, &status);
@@ -231,6 +266,18 @@ void tcpc_alert(int port)
/* CC status changed, wake task */
task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0);
}
+ if (status & TCPC_REG_ALERT_POWER_STATUS) {
+ /* Read Power Status register */
+ tcpm_get_power_status(port, &power_status);
+ /* Update VBUS status */
+ tcpc_vbus[port] = power_status &
+ TCPC_REG_POWER_VBUS_PRES ? 1 : 0;
+#if defined(CONFIG_USB_PD_TCPM_VBUS) && defined(CONFIG_USB_CHARGER)
+ /* Update charge manager with new VBUS state */
+ usb_charger_vbus_change(port, tcpc_vbus[port]);
+#endif /* CONFIG_USB_PD_TCPM_VBUS && CONFIG_USB_CHARGER */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ }
if (status & TCPC_REG_ALERT_RX_STATUS) {
/* message received */
task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_RX, 0);
diff --git a/driver/tcpm/tcpci.h b/driver/tcpm/tcpci.h
index 18f9a75e95..cbfac0380c 100644
--- a/driver/tcpm/tcpci.h
+++ b/driver/tcpm/tcpci.h
@@ -45,6 +45,7 @@
#define TCPC_REG_CC_STATUS_CC1(reg) ((reg) & 0x3)
#define TCPC_REG_POWER_STATUS 0x19
+#define TCPC_REG_POWER_VBUS_PRES (1<<5)
#define TCPC_REG_ERROR_STATUS 0x1a
#define TCPC_REG_ERROR_STATUS_UNINIT (1<<7)
#define TCPC_REG_ROLE_CTRL 0x1b
diff --git a/include/config.h b/include/config.h
index 7172f54697..a7555c4acc 100644
--- a/include/config.h
+++ b/include/config.h
@@ -1628,6 +1628,13 @@
#undef CONFIG_USB_PD_TCPM_STUB
#undef CONFIG_USB_PD_TCPM_TCPCI
+/*
+ * Use this option if the TCPC port controller is on a seperate chip from
+ * the TCPM layer and if VUBS detect GPIO is not available on the TCPM
+ * mcu.
+ */
+#undef CONFIG_USB_PD_TCPM_VBUS
+
/* Define the type-c port controller I2C base address. */
#undef CONFIG_TCPC_I2C_BASE_ADDR
diff --git a/include/usb_pd_tcpc.h b/include/usb_pd_tcpc.h
index 64d68f1f3a..a640714c4f 100644
--- a/include/usb_pd_tcpc.h
+++ b/include/usb_pd_tcpc.h
@@ -35,4 +35,12 @@
void tcpc_i2c_process(int read, int port, int len, uint8_t *payload,
void (*send_response)(int));
+/**
+ * Handle VBUS wake interrupts
+ *
+ * @param signal The VBUS wake interrupt signal
+ */
+void pd_vbus_evt_p0(enum gpio_signal signal);
+void pd_vbus_evt_p1(enum gpio_signal signal);
+
#endif /* __CROS_EC_USB_PD_TCPC_H */
diff --git a/include/usb_pd_tcpm.h b/include/usb_pd_tcpm.h
index bd4803bf03..8d446bcae8 100644
--- a/include/usb_pd_tcpm.h
+++ b/include/usb_pd_tcpm.h
@@ -113,6 +113,15 @@ int tcpm_alert_mask_set(int port, uint16_t mask);
int tcpm_get_cc(int port, int *cc1, int *cc2);
/**
+ * Read VBUS
+ *
+ * @param port Type-C port number
+ *
+ * @return 0 => VBUS not detected, 1 => VBUS detected
+ */
+int tcpm_get_vbus_level(int port);
+
+/**
* Set the CC pull resistor. This sets our role as either source or sink.
*
* @param port Type-C port number
@@ -133,6 +142,16 @@ int tcpm_set_cc(int port, int pull);
int tcpm_set_polarity(int port, int polarity);
/**
+ * Set TCPC Power Status Mask
+ *
+ * @param port Type-C port number
+ * @param mask => new mask value
+ *
+ * @return EC_SUCCESS or error
+ */
+int tcpm_set_power_status_mask(int port, uint8_t mask);
+
+/**
* Set Vconn.
*
* @param port Type-C port number