summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
}