*/
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/extcon.h>
#include <linux/io.h>
phy->regulator_on = true;
/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
- if (phy->index == 0 && data->phy0_poll)
+ if (phy->index == 0 && data->vbus_det_gpio && data->phy0_poll)
mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
return 0;
* phy0 vbus typically slowly discharges, sometimes this causes the
* Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
*/
- if (phy->index == 0 && !data->phy0_poll)
+ if (phy->index == 0 && data->vbus_det_gpio && !data->phy0_poll)
mod_delayed_work(system_wq, &data->detect, POLL_TIME);
return 0;
int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
id_det = gpiod_get_value_cansleep(data->id_det_gpio);
- vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
+ if (data->vbus_det_gpio)
+ vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
+ else
+ vbus_det = 1; /* Report vbus as high */
mutex_lock(&phy0->mutex);
}
if (id_det != data->id_det) {
+ /*
+ * When a host cable (id == 0) gets plugged in on systems
+ * without vbus detection report vbus low for long enough for
+ * the musb-ip to end the current device session.
+ */
+ if (!data->vbus_det_gpio && id_det == 0) {
+ sun4i_usb_phy0_set_vbus_detect(phy0, 0);
+ msleep(200);
+ sun4i_usb_phy0_set_vbus_detect(phy0, 1);
+ }
sun4i_usb_phy0_set_id_detect(phy0, id_det);
data->id_det = id_det;
id_notify = 1;
mutex_unlock(&phy0->mutex);
- if (id_notify)
+ if (id_notify) {
extcon_set_cable_state_(data->extcon, EXTCON_USB_HOST,
!id_det);
+ /*
+ * When a host cable gets unplugged (id == 1) on systems
+ * without vbus detection report vbus low for long enough to
+ * the musb-ip to end the current host session.
+ */
+ if (!data->vbus_det_gpio && id_det == 1) {
+ mutex_lock(&phy0->mutex);
+ sun4i_usb_phy0_set_vbus_detect(phy0, 0);
+ msleep(1000);
+ sun4i_usb_phy0_set_vbus_detect(phy0, 1);
+ mutex_unlock(&phy0->mutex);
+ }
+ }
if (vbus_notify)
extcon_set_cable_state_(data->extcon, EXTCON_USB, vbus_det);
data->vbus_det_gpio = NULL;
}
- /* We either want both gpio pins or neither (when in host mode) */
- if (!data->id_det_gpio != !data->vbus_det_gpio) {
- dev_err(dev, "failed to get id or vbus detect pin\n");
+ /* vbus_det without id_det makes no sense, and is not supported */
+ if (data->vbus_det_gpio && !data->id_det_gpio) {
+ dev_err(dev, "usb0_id_det missing or invalid\n");
return -ENODEV;
}
data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
- if (data->id_det_irq < 0 || data->vbus_det_irq < 0)
+ if ((data->id_det_gpio && data->id_det_irq < 0) ||
+ (data->vbus_det_gpio && data->vbus_det_irq < 0))
data->phy0_poll = true;
if (data->id_det_irq >= 0) {