summaryrefslogtreecommitdiff
path: root/drivers/usb/chipidea/otg.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/chipidea/otg.c')
-rw-r--r--drivers/usb/chipidea/otg.c46
1 files changed, 42 insertions, 4 deletions
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index fbfb02e05c97..a6ed9e3dc38d 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -2,7 +2,8 @@
/*
* otg.c - ChipIdea USB IP core OTG driver
*
- * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
*
* Author: Peter Chen
*/
@@ -20,6 +21,7 @@
#include "bits.h"
#include "otg.h"
#include "otg_fsm.h"
+#include "host.h"
/**
* hw_read_otgsc returns otgsc register bits value.
@@ -126,6 +128,20 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
return role;
}
+void ci_handle_vbus_connected(struct ci_hdrc *ci)
+{
+ /*
+ * TODO: if the platform does not supply 5v to udc, or use other way
+ * to supply 5v, it needs to use other conditions to call
+ * usb_gadget_vbus_connect.
+ */
+ if (!ci->is_otg)
+ return;
+
+ if (hw_read_otgsc(ci, OTGSC_BSV))
+ usb_gadget_vbus_connect(&ci->gadget);
+}
+
void ci_handle_vbus_change(struct ci_hdrc *ci)
{
if (!ci->is_otg)
@@ -151,21 +167,33 @@ static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
u32 mask = OTGSC_BSV;
while (hw_read_otgsc(ci, mask)) {
+
+ msleep(20);
+
+ /*
+ * If the vbus is higher than AVV after 20ms,
+ * we will think this vbus is from remote and
+ * don't wait any longer.
+ */
+ if (hw_read_otgsc(ci, OTGSC_AVV))
+ return 0;
+
if (time_after(jiffies, elapse)) {
dev_err(ci->dev, "timeout waiting for %08x in OTGSC\n",
mask);
return -ETIMEDOUT;
}
- msleep(20);
}
return 0;
}
-static void ci_handle_id_switch(struct ci_hdrc *ci)
+void ci_handle_id_switch(struct ci_hdrc *ci)
{
- enum ci_role role = ci_otg_role(ci);
+ enum ci_role role;
+ mutex_lock(&ci->mutex);
+ role = ci_otg_role(ci);
if (role != ci->role) {
dev_dbg(ci->dev, "switching from %s to %s\n",
ci_role(ci)->name, ci->roles[role]->name);
@@ -182,12 +210,22 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
* external connector status.
*/
hw_wait_vbus_lower_bsv(ci);
+ else if (ci->vbus_active)
+ /*
+ * If the role switch happens(e.g. during
+ * system sleep), and we lose vbus drop
+ * event, disconnect gadget for it before
+ * start host.
+ */
+ usb_gadget_vbus_disconnect(&ci->gadget);
ci_role_start(ci, role);
/* vbus change may have already occurred */
if (role == CI_ROLE_GADGET)
ci_handle_vbus_change(ci);
+
}
+ mutex_unlock(&ci->mutex);
}
/**
* ci_otg_work - perform otg (vbus/id) event handle