From 44e0fb6162b738b98a09d918d61331e7aa1dd341 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Wed, 25 Jan 2017 18:27:44 -0800 Subject: [PATCH] usb: pd: Ensure VBUS is below 0.8V before turning it on The Type-C spec requires that VBUS must not be enabled as a source if there is already a voltage > VSafe0V. To address this, add a polling loop that checks PROP_VOLTAGE_NOW to ensure it has fallen below 0.8V before enabling the VBUS regulator. Change-Id: Idd3ebd185d6bbed15b6ac700cb2cf4af428210ee Signed-off-by: Jack Pham --- drivers/usb/pd/policy_engine.c | 60 ++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index d0ee0c9d6430..9b5c2a4e5c8a 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -253,6 +253,9 @@ static void *usbpd_ipc_log; #define ID_HDR_VID 0x05c6 /* qcom */ #define PROD_VDO_PID 0x0a00 /* TBD */ +static bool check_vsafe0v = true; +module_param(check_vsafe0v, bool, S_IRUSR | S_IWUSR); + static int min_sink_current = 900; module_param(min_sink_current, int, S_IRUSR | S_IWUSR); @@ -1390,6 +1393,41 @@ static void vconn_swap(struct usbpd *pd) } } +static int enable_vbus(struct usbpd *pd) +{ + union power_supply_propval val = {0}; + int count = 100; + int ret; + + if (!check_vsafe0v) + goto enable_reg; + + /* + * Check to make sure there's no lingering charge on + * VBUS before enabling it as a source. If so poll here + * until it goes below VSafe0V (0.8V) before proceeding. + */ + while (count--) { + ret = power_supply_get_property(pd->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + if (ret || val.intval <= 800000) + break; + usleep_range(20000, 30000); + } + + if (count < 99) + msleep(100); /* need to wait an additional tCCDebounce */ + +enable_reg: + ret = regulator_enable(pd->vbus); + if (ret) + usbpd_err(&pd->dev, "Unable to enable vbus (%d)\n", ret); + else + pd->vbus_enabled = true; + + return ret; +} + static inline void rx_msg_cleanup(struct usbpd *pd) { struct rx_msg *msg, *tmp; @@ -1541,12 +1579,7 @@ static void usbpd_sm(struct work_struct *w) if (pd->current_pr == PR_SINK) { usbpd_set_state(pd, PE_SNK_STARTUP); } else if (pd->current_pr == PR_SRC) { - ret = regulator_enable(pd->vbus); - if (ret) - usbpd_err(&pd->dev, "Unable to enable vbus\n"); - else - pd->vbus_enabled = true; - + enable_vbus(pd); if (!pd->vconn_enabled && pd->typec_mode == POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE) { @@ -1718,11 +1751,7 @@ static void usbpd_sm(struct work_struct *w) msleep(SRC_RECOVER_TIME); pd->vbus_enabled = false; - ret = regulator_enable(pd->vbus); - if (ret) - usbpd_err(&pd->dev, "Unable to enable vbus\n"); - else - pd->vbus_enabled = true; + enable_vbus(pd); if (pd->vconn_enabled) { ret = regulator_enable(pd->vconn); @@ -2145,12 +2174,7 @@ static void usbpd_sm(struct work_struct *w) /* fall-through */ case PE_PRS_SNK_SRC_SOURCE_ON: - ret = regulator_enable(pd->vbus); - if (ret) - usbpd_err(&pd->dev, "Unable to enable vbus\n"); - else - pd->vbus_enabled = true; - + enable_vbus(pd); msleep(200); /* allow time VBUS ramp-up, must be < tNewSrc */ ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG);