summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorJack Pham <jackp@codeaurora.org>2017-01-25 18:27:44 -0800
committerJack Pham <jackp@codeaurora.org>2017-01-25 18:27:57 -0800
commit44e0fb6162b738b98a09d918d61331e7aa1dd341 (patch)
treec4b6e6ba62dbc2bfc032164595dce8002482b27f /drivers/usb
parent314869eb56763d34f91d5483b0d510267894fadd (diff)
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 <jackp@codeaurora.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/pd/policy_engine.c60
1 files 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);