|
@@ -22,6 +22,7 @@
|
|
|
*/
|
|
|
|
|
|
#include <linux/clk.h>
|
|
|
+#include <linux/delay.h>
|
|
|
#include <linux/err.h>
|
|
|
#include <linux/extcon.h>
|
|
|
#include <linux/io.h>
|
|
@@ -309,7 +310,7 @@ static int sun4i_usb_phy_power_on(struct phy *_phy)
|
|
|
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;
|
|
@@ -330,7 +331,7 @@ static int sun4i_usb_phy_power_off(struct phy *_phy)
|
|
|
* 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;
|
|
@@ -359,7 +360,10 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
|
|
|
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);
|
|
|
|
|
@@ -369,6 +373,16 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
|
|
|
}
|
|
|
|
|
|
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;
|
|
@@ -382,9 +396,22 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
|
|
|
|
|
|
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);
|
|
@@ -495,9 +522,9 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
|
|
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;
|
|
|
}
|
|
|
|
|
@@ -565,7 +592,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
|
|
|
|
|
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) {
|