summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott <scollyer@chromium.org>2017-02-23 14:23:09 -0800
committerchrome-bot <chrome-bot@chromium.org>2017-03-24 19:38:12 -0700
commit577fb7faa0fd8479921dfdf344a4d8bd44022e82 (patch)
treeadfdf6c45cef063bf471247d7d3d7939c2f45f17
parentd8cbf0dc402619cc7e027534c196abd9059ab98a (diff)
downloadchrome-ec-577fb7faa0fd8479921dfdf344a4d8bd44022e82.tar.gz
servo_v4: Enable VBUS passthrough from CHG to DUT port
This CL adds support for the DUT port to advertise different current levels via the Rp resistor selection. The default setting is USB (500 mA). The board_manage_chg_port function relies on the off->on transitions only occurring when the CHG port is in a steady state condition. When the charger being used supports PD messaging, the entry point pd_set_input_current_limit() which is called after receiving a PS_RDY message. For TypeC only chargers, the entry point is typec_set_input_current_limit() which is called from SNK_DISOVERY after only after the max number of hard reset attempts have been attempted. This is intended to prevent this entry point from being called when a USB PD charger is connected and the CHG port enters SNK_DISCOVERY. When the CHG port Vbus transitions from off->on, a src_pdo is updated to reflect the current contract on the CHG port and this src_pdo can then be used by the DUT port when either a new connection is done or to update it's existing contract. BUG=b:35586526 BRANCH=servo TEST=Manual Connected servo_v4 DUT port to Reef with no charger connected. Verified that it connects, and CCD mode in Reef is enabled (H1 console is available) and that only 500 mA charging current is advertised. Then connected a 20V and 15V USB PD charger to the CHG port. In each case verified that the DUT renegotiates to the 20 and 15V level respectively. Repated the test with Guppy and verifed that VBUS is at 5V 3A. When Guppy is removed, then the DUT connection reverts back to the host as the VBUS source. Change-Id: I1a5eb346bbe1f0d586cb8b7bb24d77ff713fbf3c Signed-off-by: Scott <scollyer@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/449954 Commit-Ready: Scott Collyer <scollyer@chromium.org> Tested-by: Scott Collyer <scollyer@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--board/servo_v4/board.h5
-rw-r--r--board/servo_v4/usb_pd_config.h100
-rw-r--r--board/servo_v4/usb_pd_policy.c234
3 files changed, 271 insertions, 68 deletions
diff --git a/board/servo_v4/board.h b/board/servo_v4/board.h
index 52bd17941f..eb5de84dbd 100644
--- a/board/servo_v4/board.h
+++ b/board/servo_v4/board.h
@@ -80,16 +80,19 @@
#define CONFIG_USB_POWER_DELIVERY
#define CONFIG_USB_PD_DTS
#define CONFIG_USB_PD_DUAL_ROLE
+#define CONFIG_USB_PD_DYNAMIC_SRC_CAP
#define CONFIG_USB_PD_INTERNAL_COMP
#define CONFIG_USB_PD_PORT_COUNT 2
#define CONFIG_USB_PD_TCPC
#define CONFIG_USB_PD_TCPM_STUB
+#undef CONFIG_USB_PD_PULLUP
+#define CONFIG_USB_PD_PULLUP TYPEC_RP_USB
/* Override PD_ROLE_DEFAULT in usb_pd.h */
#define PD_ROLE_DEFAULT(port) ((port) ? PD_ROLE_SOURCE : PD_ROLE_SINK)
/* 3.0A Standard-current Rp */
-#define PD_SRC_VNC PD_SRC_DEF_VNC_MV
+#define PD_SRC_VNC PD_SRC_3_0_VNC_MV
#define PD_SRC_RD_THRESHOLD PD_SRC_DEF_RD_THRESH_MV
/*
diff --git a/board/servo_v4/usb_pd_config.h b/board/servo_v4/usb_pd_config.h
index c9c21726e2..838ead71d7 100644
--- a/board/servo_v4/usb_pd_config.h
+++ b/board/servo_v4/usb_pd_config.h
@@ -8,6 +8,7 @@
#include "console.h"
#include "gpio.h"
#include "ec_commands.h"
+#include "usb_pd_tcpm.h"
/* USB Power delivery board configuration */
@@ -20,6 +21,8 @@
* C1 DUT
*
*/
+#define CHG 0
+#define DUT 1
/* Timer selection for baseband PD communication */
#define TIM_CLOCK_PD_TX_CHG 16
@@ -217,6 +220,62 @@ static inline void pd_tx_init(void)
gpio_config_module(MODULE_USB_PD, 1);
}
+static inline int pd_set_rp_rd(int port, int cc_pull, int rp_value)
+{
+ /* Set Rd for CC1/CC2 to High-Z. */
+ gpio_set_flags(GPIO_USB_DUT_CC1_RD, GPIO_INPUT);
+ gpio_set_flags(GPIO_USB_DUT_CC2_RD, GPIO_INPUT);
+ /* Set Rp for CC1/CC2 to High-Z. */
+ gpio_set_flags(GPIO_USB_DUT_CC1_RP3A0, GPIO_INPUT);
+ gpio_set_flags(GPIO_USB_DUT_CC2_RP3A0, GPIO_INPUT);
+ gpio_set_flags(GPIO_USB_DUT_CC1_RP1A5, GPIO_INPUT);
+ gpio_set_flags(GPIO_USB_DUT_CC2_RP1A5, GPIO_INPUT);
+ gpio_set_flags(GPIO_USB_DUT_CC1_RPUSB, GPIO_INPUT);
+ gpio_set_flags(GPIO_USB_DUT_CC2_RPUSB, GPIO_INPUT);
+
+ if (cc_pull == TYPEC_CC_RP) {
+ /*
+ * CC values for Debug sources (DTS)
+ *
+ * Source type Mode of Operation CC1 CC2
+ * ---------------------------------------------
+ * DTS Default USB Power Rp3A0 Rp1A5
+ * DTS USB-C @ 1.5 A Rp1A5 RpUSB
+ * DTS USB-C @ 3 A Rp3A0 RpUSB
+ */
+ switch (rp_value) {
+ case TYPEC_RP_USB:
+ gpio_set_flags(GPIO_USB_DUT_CC1_RP3A0, GPIO_OUT_HIGH);
+ gpio_set_flags(GPIO_USB_DUT_CC2_RP1A5, GPIO_OUT_HIGH);
+ break;
+ case TYPEC_RP_1A5:
+ gpio_set_flags(GPIO_USB_DUT_CC1_RP1A5, GPIO_OUT_HIGH);
+ gpio_set_flags(GPIO_USB_DUT_CC2_RPUSB, GPIO_OUT_HIGH);
+ break;
+ case TYPEC_RP_3A0:
+ gpio_set_flags(GPIO_USB_DUT_CC1_RP3A0, GPIO_OUT_HIGH);
+ gpio_set_flags(GPIO_USB_DUT_CC2_RPUSB, GPIO_OUT_HIGH);
+ break;
+ case TYPEC_RP_RESERVED:
+ /*
+ * This case can be used to force a detach event since
+ * all values are set to inputs above. Nothing else to
+ * set.
+ */
+ break;
+ default:
+ return EC_ERROR_INVAL;
+ }
+
+ } else if (cc_pull == TYPEC_CC_RD) {
+ /* DTS in SNK mode presents Rd on CC1/CC2 */
+ gpio_set_flags(GPIO_USB_DUT_CC1_RD, GPIO_OUT_LOW);
+ gpio_set_flags(GPIO_USB_DUT_CC2_RD, GPIO_OUT_LOW);
+ }
+
+ return EC_SUCCESS;
+}
+
static inline void pd_set_host_mode(int port, int enable)
{
/*
@@ -231,42 +290,21 @@ static inline void pd_set_host_mode(int port, int enable)
/*
* Servo_v4 in SRC mode acts as a DTS (debug test
* accessory) and needs to present Rp on both CC
- * lines. In order to support orientation detection, the
- * values of Rp1/Rp2 need to asymmetric with Rp1 > Rp2.
+ * lines. In order to support orientation detection, and
+ * advertise the correct TypeC current level, the
+ * values of Rp1/Rp2 need to asymmetric with Rp1 > Rp2. This
+ * function is called without a specified Rp value so assume the
+ * servo_v4 default of USB level current. If a higher current
+ * can be supported, then the Rp value will get adjusted when
+ * VBUS is enabled.
*/
+ pd_set_rp_rd(port, TYPEC_CC_RP, TYPEC_RP_USB);
- /* Set Rd for CC1/CC2 to High-Z. */
- gpio_set_flags(GPIO_USB_DUT_CC1_RD, GPIO_INPUT);
- gpio_set_flags(GPIO_USB_DUT_CC2_RD, GPIO_INPUT);
-
- /*
- * TODO(crosbug.com/p/60794): Currently always advertising
- * 1.5A. If CHG port is attached then can advertise 3A
- * instead. Additionally, if CHG port is not attached and having
- * to use host provided vbus, can 1.5A be advertised?
- */
- /* Pull up for host mode */
- gpio_set_flags(GPIO_USB_DUT_CC1_RP1A5, GPIO_OUTPUT);
- gpio_set_level(GPIO_USB_DUT_CC1_RP1A5, 1);
- gpio_set_flags(GPIO_USB_DUT_CC2_RPUSB, GPIO_OUTPUT);
- gpio_set_level(GPIO_USB_DUT_CC2_RPUSB, 1);
- /* Set TX Hi-Z */
gpio_set_flags(GPIO_USB_DUT_CC1_TX_DATA, GPIO_INPUT);
gpio_set_flags(GPIO_USB_DUT_CC2_TX_DATA, GPIO_INPUT);
} else {
- /* Set Rp for CC1/CC2 to High-Z. */
- gpio_set_flags(GPIO_USB_DUT_CC1_RP3A0, GPIO_INPUT);
- gpio_set_flags(GPIO_USB_DUT_CC2_RP3A0, GPIO_INPUT);
- gpio_set_flags(GPIO_USB_DUT_CC1_RP1A5, GPIO_INPUT);
- gpio_set_flags(GPIO_USB_DUT_CC2_RP1A5, GPIO_INPUT);
- gpio_set_flags(GPIO_USB_DUT_CC1_RPUSB, GPIO_INPUT);
- gpio_set_flags(GPIO_USB_DUT_CC2_RPUSB, GPIO_INPUT);
-
- /* DTS in SNK mode presents Rd on CC1/CC2 */
- gpio_set_flags(GPIO_USB_DUT_CC1_RD, GPIO_OUTPUT);
- gpio_set_flags(GPIO_USB_DUT_CC2_RD, GPIO_OUTPUT);
- gpio_set_level(GPIO_USB_DUT_CC1_RD, 0);
- gpio_set_level(GPIO_USB_DUT_CC2_RD, 0);
+ /* Select Rd, the Rp value is a don't care */
+ pd_set_rp_rd(port, TYPEC_CC_RD, TYPEC_RP_RESERVED);
}
}
diff --git a/board/servo_v4/usb_pd_policy.c b/board/servo_v4/usb_pd_policy.c
index 48cce3519b..9ba5356791 100644
--- a/board/servo_v4/usb_pd_policy.c
+++ b/board/servo_v4/usb_pd_policy.c
@@ -14,34 +14,180 @@
#include "registers.h"
#include "system.h"
#include "task.h"
+#include "tcpm.h"
#include "timer.h"
#include "util.h"
#include "usb_mux.h"
#include "usb_pd.h"
+#include "usb_pd_config.h"
+#include "usb_pd_tcpm.h"
#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
-/* Define typical operating power and max power */
-/*#define OPERATING_POWER_MW 15000 */
-/*#define MAX_POWER_MW 60000 */
-/*#define MAX_CURRENT_MA 3000 */
+#define DUT_PDO_FIXED_FLAGS (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |\
+ PDO_FIXED_COMM_CAP)
-#define PDO_FIXED_FLAGS (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |\
- PDO_FIXED_COMM_CAP)
+#define CHG_PDO_FIXED_FLAGS (PDO_FIXED_DATA_SWAP)
-const uint32_t pd_src_pdo[] = {
- PDO_FIXED(5000, 900, PDO_FIXED_FLAGS),
-};
-const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo);
+#define VBUS_UNCHANGED(curr, pend, new) (curr == new && pend == new)
+/*
+ * Dynamic PDO that reflects capabilities present on the CHG port. Allow for two
+ * entries so that can offer greater than 5V charging. The 1st entry will be
+ * fixed 5V, but its current value may change based on the CHG port vbus
+ * info. The 2nd entry is used for when offering vbus greater than 5V.
+ */
+static uint32_t pd_src_chg_pdo[2];
+static uint8_t chg_pdo_cnt;
+static const uint32_t pd_src_host_pdo[] = {
+ PDO_FIXED(5000, 500, DUT_PDO_FIXED_FLAGS),
+};
const uint32_t pd_snk_pdo[] = {
- PDO_FIXED(5000, 500, PDO_FIXED_FLAGS),
+ PDO_FIXED(5000, 500, CHG_PDO_FIXED_FLAGS),
PDO_BATT(4750, 21000, 15000),
PDO_VAR(4750, 21000, 3000),
};
const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo);
+struct vbus_prop {
+ int mv;
+ int ma;
+};
+static struct vbus_prop vbus[CONFIG_USB_PD_PORT_COUNT];
+static struct vbus_prop vbus_pend;
+static uint8_t vbus_rp = TYPEC_RP_RESERVED;
+
+static void board_manage_dut_port(void)
+{
+ int rp;
+
+ /*
+ * This function is called by the CHG port whenever there has been a
+ * change in its vbus voltage or current. That change may necessitate
+ * that the DUT port present a different Rp value or renogiate its PD
+ * contract if it is connected.
+ */
+
+ /* Assume the default value of Rp */
+ rp = TYPEC_RP_USB;
+ if (vbus[CHG].mv == 5000) {
+ /* Only advertise higher current via Rp if vbus == 5V */
+ if (vbus[CHG].ma >= 3000)
+ /* CHG port is connected and DUt can advertise 3A */
+ rp = TYPEC_RP_3A0;
+ else if (vbus[CHG].ma >= 1500)
+ rp = TYPEC_RP_1A5;
+ }
+
+ /* Check if Rp setting needs to change from current value */
+ if (vbus_rp != rp) {
+ /* Present new Rp value */
+ tcpm_select_rp_value(DUT, rp);
+ /* Save new Rp value for DUT port */
+ vbus_rp = rp;
+ }
+
+ /*
+ * If CHG vbus voltage/current doesn't match the DUT port value, then
+ * update the contract. If DUT port is not in the correct state, then
+ * this call does nothing.
+ */
+ if (vbus[CHG].mv != vbus[DUT].mv || vbus[CHG].ma != vbus[DUT].ma)
+ /*
+ * Update PD contract to reflect new available CHG
+ * voltage/current values.
+ */
+ pd_update_contract(DUT);
+}
+
+static void board_manage_chg_port(void)
+{
+ /* Update the voltage/current values for CHG port */
+ vbus[CHG].mv = vbus_pend.mv;
+ vbus[CHG].ma = vbus_pend.ma;
+
+ /*
+ * CHG Vbus has changed states, update PDO that reflects CHG port
+ * state
+ */
+ if (!vbus[CHG].mv) {
+ /* CHG Vbus has dropped, so always source DUT Vbus from host */
+ gpio_set_level(GPIO_HOST_OR_CHG_CTL, 0);
+ chg_pdo_cnt = 0;
+ } else {
+ int v5_ma = vbus[CHG].mv > 5000 ? 500 : vbus[CHG].ma;
+
+ pd_src_chg_pdo[0] = PDO_FIXED_VOLT(5000) |
+ PDO_FIXED_CURR(v5_ma) | DUT_PDO_FIXED_FLAGS |
+ PDO_FIXED_EXTERNAL;
+
+ chg_pdo_cnt = 1;
+ if (vbus[CHG].mv > 5000) {
+ /*
+ * CHG vbus is > 5V so need an entry for vSafe5V and an
+ * entry that reflects CHG VBUS
+ */
+ pd_src_chg_pdo[1] = PDO_FIXED_VOLT(vbus[CHG].mv) |
+ PDO_FIXED_CURR(vbus[CHG].ma) |
+ DUT_PDO_FIXED_FLAGS | PDO_FIXED_EXTERNAL;
+ chg_pdo_cnt = 2;
+ }
+ }
+
+ /* Call DUT port manager to update Rp and possible PD contract */
+ board_manage_dut_port();
+}
+DECLARE_DEFERRED(board_manage_chg_port);
+
+static void board_update_chg_vbus(int max_ma, int vbus_mv)
+{
+ /*
+ * Determine if vbus from CHG port has changed values and if the current
+ * state of CHG vbus is on or off. If the change is on, then schedule a
+ * deferred callback. If the change is off, then act immediately.
+ */
+ if (VBUS_UNCHANGED(vbus[CHG].mv, vbus_pend.mv, vbus_mv) &&
+ VBUS_UNCHANGED(vbus[CHG].ma, vbus_pend.ma, max_ma))
+ /* No change in CHG VBUS detected, nothing else to do. */
+ return;
+
+ /* CHG port voltage and current values are now pending */
+ vbus_pend.mv = vbus_mv;
+ vbus_pend.ma = max_ma;
+
+ if (vbus_mv)
+ /* Wait enough time for PD contract to be established */
+ hook_call_deferred(&board_manage_chg_port_data,
+ PD_T_SINK_WAIT_CAP * 3);
+ else
+ /* Update CHG port status now since vbus is off */
+ hook_call_deferred(&board_manage_chg_port_data, 0);
+}
+
+int board_select_rp_value(int port, int rp)
+{
+ return pd_set_rp_rd(port, TYPEC_CC_RP, rp);
+}
+
+int charge_manager_get_source_pdo(const uint32_t **src_pdo)
+{
+ int pdo_cnt;
+ /*
+ * If CHG is providing VBUS, then advertise what's available on the CHG
+ * port, otherwise used the fixed value that matches host capabilities.
+ */
+ if (vbus[CHG].mv) {
+ *src_pdo = pd_src_chg_pdo;
+ pdo_cnt = chg_pdo_cnt;
+ } else {
+ *src_pdo = pd_src_host_pdo;
+ pdo_cnt = ARRAY_SIZE(pd_src_host_pdo);
+ }
+
+ return pdo_cnt;
+}
+
int pd_is_valid_input_voltage(int mv)
{
/* Any voltage less than the max is allowed */
@@ -51,58 +197,73 @@ int pd_is_valid_input_voltage(int mv)
void pd_transition_voltage(int idx)
{
/*
- * TODO(crosbug.com/p/60794): Most likely this function is a don't care
- * for servo_v4 since VBUS provided to the DUT port has just an on/off
- * control. For now leave it as a no-op.
+ * Up to this point, VBUS will have been supplied by host. If
+ * vbus[CHG].mv is set, then that means the CHG port is in a steady
+ * state condition and its voltage/current values have been communicated
+ * to the DUT in the SRC_CAP message. If CHG vbus > 5V then 2
+ * chg_src_pdo entries will have been sent. Only allow pass through
+ * charging from CHG vbus if the pdo idx requested by the DUT matches
+ * the number of chg_src_pdo entries.
+ *
*/
+ if (vbus[CHG].mv && idx == chg_pdo_cnt) {
+ gpio_set_level(GPIO_HOST_OR_CHG_CTL, 1);
+ /*
+ * VBUS being provided by CHG port now. Update DUT port's vbus
+ * info with the CHG port's values.
+ */
+ vbus[DUT].mv = vbus[CHG].mv;
+ vbus[DUT].ma = vbus[CHG].ma;
+ }
}
int pd_set_power_supply_ready(int port)
{
/* Port 0 can never provide vbus. */
- if (!port)
+ if (port == CHG)
return EC_ERROR_INVAL;
- /*
- * TODO(crosbug.com/p/60794): For now always assume VBUS is supplied by
- * host. No support yet for using CHG VBUS passthru mode.
- */
-
- /*
- * Select Host as source for VBUS.
- * To select host, set GPIO_HOST_OR_CHG_CTL low. To select CHG as VBUS
- * source, then set GPIO_HOST_OR_CHG_CTL high.
- */
+ /* Only ever allow host vbus at this point */
gpio_set_level(GPIO_HOST_OR_CHG_CTL, 0);
- /* Enable VBUS from the source selected above. */
+ /* Enable VBUS */
gpio_set_level(GPIO_DUT_CHG_EN, 1);
+ /* Host vbus is always 5V/500mA */
+ vbus[DUT].mv = 5000;
+ vbus[DUT].ma = 500;
+
return EC_SUCCESS; /* we are ready */
}
void pd_power_supply_reset(int port)
{
+ /* Port 0 can never provide vbus. */
+ if (port == CHG)
+ return;
+
/* Disable VBUS */
gpio_set_level(GPIO_DUT_CHG_EN, 0);
+ /* Set default VBUS source to Host */
+ gpio_set_level(GPIO_HOST_OR_CHG_CTL, 0);
+
+ /* Host vbus is always 5V/500mA */
+ vbus[DUT].mv = 0;
+ vbus[DUT].ma = 0;
}
void pd_set_input_current_limit(int port, uint32_t max_ma,
uint32_t supply_voltage)
{
- /*
- * TODO(crosbug.com/p/60794): Placeholder for now so that can compile
- * with USB PD support.
- */
+ if (port == CHG)
+ board_update_chg_vbus(max_ma, supply_voltage);
}
void typec_set_input_current_limit(int port, uint32_t max_ma,
uint32_t supply_voltage)
{
- /*
- * TODO(crosbug.com/p/60794): Placeholder for now so that can compile
- * with USB PD support.
- */
+ if (port == CHG)
+ board_update_chg_vbus(max_ma, supply_voltage);
}
int pd_snk_is_vbus_provided(int port)
@@ -121,8 +282,9 @@ int pd_check_power_swap(int port)
{
/*
* TODO(crosbug.com/p/60792): CHG port can't do a power swap as it's SNK
- * only. DUT port should be able to support a power role swap, but VBUS
- * will need to be present. For now, don't allow swaps on either port.
+ * only. Don't allow DUT port to accept a power role swap request. More
+ * support still needs to be added so that servo_v4 DUT port behaves
+ * properly when acting as a SNK device.
*/
return 0;
}