|
--- a/drivers/usb/dwc3/dwc3_otg.c 2021-02-08 15:44:20.000000000 +0800 |
|
+++ b/drivers/usb/dwc3/dwc3_otg.c 2021-02-08 15:45:03.000000000 +0800 |
|
@@ -33,6 +33,18 @@ |
|
static int max_chgr_retry_count = MAX_INVALID_CHRGR_RETRY; |
|
module_param(max_chgr_retry_count, int, S_IRUGO | S_IWUSR); |
|
MODULE_PARM_DESC(max_chgr_retry_count, "Max invalid charger retry count"); |
|
+ |
|
+//Module param for aca enabling |
|
+static bool aca_enable = 0; |
|
+module_param(aca_enable, bool, S_IRUGO | S_IWUSR); |
|
+MODULE_PARM_DESC(aca_enable, "Enable ACA host mode to allow charging and host"); |
|
+ |
|
+//Define absent ID_A flag (from msm_otg module) |
|
+#define ID_A 2 |
|
+ |
|
+//Flag for choosing either ID(host w/ vbus) or ID_A (host w/ charge) based on aca_enable toggle |
|
+int ID_MODE; |
|
+ |
|
static void dwc3_otg_reset(struct dwc3_otg *dotg); |
|
|
|
static void dwc3_otg_notify_host_mode(struct usb_otg *otg, int host_mode); |
|
@@ -198,38 +210,42 @@ static int dwc3_otg_start_host(struct us |
|
if (!dwc->xhci) |
|
return -EINVAL; |
|
|
|
- if (!dotg->vbus_otg) { |
|
- dotg->vbus_otg = devm_regulator_get(dwc->dev->parent, |
|
- "vbus_dwc3"); |
|
- if (IS_ERR(dotg->vbus_otg)) { |
|
- dev_err(dwc->dev, "Failed to get vbus regulator\n"); |
|
- ret = PTR_ERR(dotg->vbus_otg); |
|
- dotg->vbus_otg = 0; |
|
- return ret; |
|
+ if (ID_MODE == ID) { |
|
+ if (!dotg->vbus_otg) { |
|
+ dotg->vbus_otg = devm_regulator_get(dwc->dev->parent, |
|
+ "vbus_dwc3"); |
|
+ if (IS_ERR(dotg->vbus_otg)) { |
|
+ dev_err(dwc->dev, "Failed to get vbus regulator\n"); |
|
+ ret = PTR_ERR(dotg->vbus_otg); |
|
+ dotg->vbus_otg = 0; |
|
+ return ret; |
|
+ } |
|
} |
|
- } |
|
+ } |
|
|
|
if (on) { |
|
dev_dbg(otg->phy->dev, "%s: turn on host\n", __func__); |
|
|
|
- dwc3_otg_notify_host_mode(otg, on); |
|
+ if (ID_MODE == ID) { |
|
+ dwc3_otg_notify_host_mode(otg, on); |
|
|
|
- /* register ocp notification */ |
|
- if (ext_xceiv && ext_xceiv->otg_capability && |
|
- ext_xceiv->ext_ocp_notification.notify) { |
|
- ret = regulator_register_ocp_notification( |
|
- dotg->vbus_otg, |
|
- &ext_xceiv->ext_ocp_notification); |
|
- if (ret) |
|
- dev_err(otg->phy->dev, |
|
- "unable to register ocp\n"); |
|
- } |
|
+ /* register ocp notification */ |
|
+ if (ext_xceiv && ext_xceiv->otg_capability && |
|
+ ext_xceiv->ext_ocp_notification.notify) { |
|
+ ret = regulator_register_ocp_notification( |
|
+ dotg->vbus_otg, |
|
+ &ext_xceiv->ext_ocp_notification); |
|
+ if (ret) |
|
+ dev_err(otg->phy->dev, |
|
+ "unable to register ocp\n"); |
|
+ } |
|
|
|
- ret = regulator_enable(dotg->vbus_otg); |
|
- if (ret) { |
|
- dev_err(otg->phy->dev, "unable to enable vbus_otg\n"); |
|
- dwc3_otg_notify_host_mode(otg, 0); |
|
- return ret; |
|
+ ret = regulator_enable(dotg->vbus_otg); |
|
+ if (ret) { |
|
+ dev_err(otg->phy->dev, "unable to enable vbus_otg\n"); |
|
+ dwc3_otg_notify_host_mode(otg, 0); |
|
+ return ret; |
|
+ } |
|
} |
|
|
|
/* The delay between enabling regulator and adding the |
|
@@ -262,8 +278,10 @@ static int dwc3_otg_start_host(struct us |
|
dev_err(otg->phy->dev, |
|
"%s: failed to add XHCI pdev ret=%d\n", |
|
__func__, ret); |
|
- regulator_disable(dotg->vbus_otg); |
|
- dwc3_otg_notify_host_mode(otg, 0); |
|
+ if (ID_MODE == ID) { |
|
+ regulator_disable(dotg->vbus_otg); |
|
+ dwc3_otg_notify_host_mode(otg, 0); |
|
+ } |
|
return ret; |
|
} |
|
|
|
@@ -273,24 +291,24 @@ static int dwc3_otg_start_host(struct us |
|
} else { |
|
dev_dbg(otg->phy->dev, "%s: turn off host\n", __func__); |
|
|
|
- ret = regulator_disable(dotg->vbus_otg); |
|
- if (ret) { |
|
- dev_err(otg->phy->dev, "unable to disable vbus_otg\n"); |
|
- return ret; |
|
- } |
|
+ if (ID_MODE == ID) { |
|
+ ret = regulator_disable(dotg->vbus_otg); |
|
+ if (ret) { |
|
+ dev_err(otg->phy->dev, "unable to disable vbus_otg\n"); |
|
+ return ret; |
|
+ } |
|
|
|
- /* unregister ocp notification */ |
|
- if (ext_xceiv && ext_xceiv->otg_capability && |
|
- ext_xceiv->ext_ocp_notification.notify) { |
|
- ret = regulator_register_ocp_notification( |
|
- dotg->vbus_otg, NULL); |
|
- if (ret) |
|
- dev_err(otg->phy->dev, |
|
- "unable to unregister ocp\n"); |
|
+ /* unregister ocp notification */ |
|
+ if (ext_xceiv && ext_xceiv->otg_capability && |
|
+ ext_xceiv->ext_ocp_notification.notify) { |
|
+ ret = regulator_register_ocp_notification( |
|
+ dotg->vbus_otg, NULL); |
|
+ if (ret) |
|
+ dev_err(otg->phy->dev, |
|
+ "unable to unregister ocp\n"); |
|
+ } |
|
+ dwc3_otg_notify_host_mode(otg, on); |
|
} |
|
- |
|
- dwc3_otg_notify_host_mode(otg, on); |
|
- |
|
platform_device_del(dwc->xhci); |
|
/* |
|
* Perform USB hardware RESET (both core reset and DBM reset) |
|
@@ -334,8 +352,10 @@ static int dwc3_otg_set_host(struct usb_ |
|
* required for XHCI controller before setting OTG Port Power |
|
* TODO: Tune this delay |
|
*/ |
|
- msleep(300); |
|
- dwc3_otg_set_host_power(dotg); |
|
+ if (ID_MODE == ID) { |
|
+ msleep(300); |
|
+ dwc3_otg_set_host_power(dotg); |
|
+ } |
|
} else { |
|
otg->host = NULL; |
|
} |
|
@@ -499,11 +519,14 @@ static void dwc3_ext_event_notify(struct |
|
dev_warn(phy->dev, "pm_runtime_get failed!!\n"); |
|
} |
|
if (ext_xceiv->id == DWC3_ID_FLOAT) { |
|
- dev_info(phy->dev, "XCVR: ID set\n"); |
|
+ dev_info(phy->dev, "XCVR: ID/ID_A set\n"); |
|
set_bit(ID, &dotg->inputs); |
|
- } else { |
|
- dev_info(phy->dev, "XCVR: ID clear\n"); |
|
- clear_bit(ID, &dotg->inputs); |
|
+ set_bit(ID_A, &dotg->inputs); |
|
+ } else if (phy->state != OTG_STATE_A_HOST) { |
|
+ dev_dbg(phy->dev, "XCVR: ID_MODE clear\n"); |
|
+ ID_MODE = (!aca_enable) ? ID : ID_A; |
|
+ set_bit((ID_MODE == ID) ? ID_A : ID, &dotg->inputs); |
|
+ clear_bit(ID_MODE, &dotg->inputs); |
|
} |
|
|
|
if (ext_xceiv->bsv) { |
|
@@ -681,17 +704,20 @@ static irqreturn_t dwc3_otg_interrupt(in |
|
if ((oevt_reg & DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT) || |
|
(oevt_reg & DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT)) { |
|
/* |
|
- * ID sts has changed, set inputs later, in the workqueue |
|
+ * ID_MODE sts has changed, set inputs later, in the workqueue |
|
* function, switch from A to B or from B to A. |
|
*/ |
|
|
|
if (oevt_reg & DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT) { |
|
if (osts & DWC3_OTG_OSTS_CONIDSTS) { |
|
- dev_info(phy->dev, "ID set\n"); |
|
+ dev_info(phy->dev, "ID/ID_A set\n"); |
|
set_bit(ID, &dotg->inputs); |
|
- } else { |
|
- dev_info(phy->dev, "ID clear\n"); |
|
- clear_bit(ID, &dotg->inputs); |
|
+ set_bit(ID_A, &dotg->inputs); |
|
+ } else if (phy->state != OTG_STATE_A_HOST) { |
|
+ dev_dbg(phy->dev, "ID_MODE clear\n"); |
|
+ ID_MODE = (!aca_enable) ? ID : ID_A; |
|
+ set_bit((ID_MODE == ID) ? ID_A : ID, &dotg->inputs); |
|
+ clear_bit(ID_MODE, &dotg->inputs); |
|
} |
|
handled_irqs |= DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT; |
|
} |
|
@@ -741,15 +767,20 @@ void dwc3_otg_init_sm(struct dwc3_otg *d |
|
dev_err(phy->dev, "%s: completion timeout\n", __func__); |
|
/* We can safely assume no cable connected */ |
|
set_bit(ID, &dotg->inputs); |
|
+ set_bit(ID_A, &dotg->inputs); |
|
} |
|
|
|
ext_xceiv = dotg->ext_xceiv; |
|
dwc3_otg_reset(dotg); |
|
+ ID_MODE = (!aca_enable) ? ID : ID_A; |
|
if (ext_xceiv && !ext_xceiv->otg_capability) { |
|
- if (osts & DWC3_OTG_OSTS_CONIDSTS) |
|
+ if (osts & DWC3_OTG_OSTS_CONIDSTS) { |
|
set_bit(ID, &dotg->inputs); |
|
- else |
|
- clear_bit(ID, &dotg->inputs); |
|
+ set_bit(ID_A, &dotg->inputs); |
|
+ } else { |
|
+ set_bit((ID_MODE == ID) ? ID_A : ID, &dotg->inputs); |
|
+ clear_bit(ID_MODE, &dotg->inputs); |
|
+ } |
|
|
|
if (osts & DWC3_OTG_OSTS_BSESVALID) |
|
set_bit(B_SESS_VLD, &dotg->inputs); |
|
@@ -775,6 +806,8 @@ static void dwc3_otg_sm_work(struct work |
|
bool work = 0; |
|
int ret = 0; |
|
unsigned long delay = 0; |
|
+ /* Flag to determine the current ACA host mode status */ |
|
+ static bool acaenabled; |
|
|
|
pm_runtime_resume(phy->dev); |
|
dev_info(phy->dev, "%s state\n", otg_state_string(phy->state)); |
|
@@ -791,8 +824,8 @@ static void dwc3_otg_sm_work(struct work |
|
"couldn't get usb power supply\n"); |
|
} |
|
|
|
- /* Switch to A or B-Device according to ID / BSV */ |
|
- if (!test_bit(ID, &dotg->inputs)) { |
|
+ /* Switch to A or B-Device according to ID_MODE / BSV */ |
|
+ if (!test_bit(ID_MODE, &dotg->inputs)) { |
|
dev_dbg(phy->dev, "!id\n"); |
|
phy->state = OTG_STATE_A_IDLE; |
|
work = 1; |
|
@@ -808,7 +841,7 @@ static void dwc3_otg_sm_work(struct work |
|
break; |
|
|
|
case OTG_STATE_B_IDLE: |
|
- if (!test_bit(ID, &dotg->inputs)) { |
|
+ if (!test_bit(ID_MODE, &dotg->inputs)) { |
|
dev_dbg(phy->dev, "!id\n"); |
|
phy->state = OTG_STATE_A_IDLE; |
|
work = 1; |
|
@@ -921,7 +954,7 @@ static void dwc3_otg_sm_work(struct work |
|
|
|
case OTG_STATE_B_PERIPHERAL: |
|
if (!test_bit(B_SESS_VLD, &dotg->inputs) || |
|
- !test_bit(ID, &dotg->inputs)) { |
|
+ !test_bit(ID_MODE, &dotg->inputs)) { |
|
dev_dbg(phy->dev, "!id || !bsv\n"); |
|
dwc3_otg_start_peripheral(&dotg->otg, 0); |
|
phy->state = OTG_STATE_B_IDLE; |
|
@@ -933,7 +966,7 @@ static void dwc3_otg_sm_work(struct work |
|
|
|
case OTG_STATE_A_IDLE: |
|
/* Switch to A-Device*/ |
|
- if (test_bit(ID, &dotg->inputs)) { |
|
+ if (test_bit(ID_MODE, &dotg->inputs)) { |
|
dev_dbg(phy->dev, "id\n"); |
|
phy->state = OTG_STATE_B_IDLE; |
|
dotg->vbus_retry_count = 0; |
|
@@ -947,36 +980,46 @@ static void dwc3_otg_sm_work(struct work |
|
/* staying on here until exit from A-Device */ |
|
} else { |
|
phy->state = OTG_STATE_A_HOST; |
|
- ret = dwc3_otg_start_host(&dotg->otg, 1); |
|
- if ((ret == -EPROBE_DEFER) && |
|
- dotg->vbus_retry_count < 3) { |
|
- /* |
|
- * Get regulator failed as regulator driver is |
|
- * not up yet. Will try to start host after 1sec |
|
- */ |
|
- phy->state = OTG_STATE_A_IDLE; |
|
- dev_dbg(phy->dev, "Unable to get vbus regulator. Retrying...\n"); |
|
- delay = VBUS_REG_CHECK_DELAY; |
|
- work = 1; |
|
- dotg->vbus_retry_count++; |
|
- } else if (ret) { |
|
- /* |
|
- * Probably set_host was not called yet. |
|
- * We will re-try as soon as it will be called |
|
- */ |
|
- dev_dbg(phy->dev, "enter lpm as\n" |
|
- "unable to start A-device\n"); |
|
- phy->state = OTG_STATE_A_IDLE; |
|
- pm_runtime_put_sync(phy->dev); |
|
- return; |
|
+ /* Wait, as host must be enabled after power */ |
|
+ if (ID_MODE == ID_A) { |
|
+ acaenabled = 0; |
|
+ /* Ensure there's no charger before suspending */ |
|
+ msleep(200); |
|
+ if (!dotg->ext_xceiv->bsv) |
|
+ pm_runtime_put_sync(phy->dev); |
|
+ } else { |
|
+ ret = dwc3_otg_start_host(&dotg->otg, 1); |
|
+ if ((ret == -EPROBE_DEFER) && |
|
+ dotg->vbus_retry_count < 3) { |
|
+ /* |
|
+ * Get regulator failed as regulator driver is |
|
+ * not up yet. Will try to start host after 1sec |
|
+ */ |
|
+ phy->state = OTG_STATE_A_IDLE; |
|
+ dev_dbg(phy->dev, "Unable to get vbus regulator. Retrying...\n"); |
|
+ delay = VBUS_REG_CHECK_DELAY; |
|
+ work = 1; |
|
+ dotg->vbus_retry_count++; |
|
+ } else if (ret) { |
|
+ /* |
|
+ * Probably set_host was not called yet. |
|
+ * We will re-try as soon as it will be called |
|
+ */ |
|
+ dev_dbg(phy->dev, "enter lpm as\n" |
|
+ "unable to start A-device\n"); |
|
+ phy->state = OTG_STATE_A_IDLE; |
|
+ pm_runtime_put_sync(phy->dev); |
|
+ return; |
|
+ } |
|
} |
|
} |
|
break; |
|
|
|
case OTG_STATE_A_HOST: |
|
- if (test_bit(ID, &dotg->inputs)) { |
|
+ if (test_bit(ID_MODE, &dotg->inputs)) { |
|
dev_dbg(phy->dev, "id\n"); |
|
- dwc3_otg_start_host(&dotg->otg, 0); |
|
+ if (ID_MODE == ID || acaenabled) |
|
+ dwc3_otg_start_host(&dotg->otg, 0); |
|
phy->state = OTG_STATE_B_IDLE; |
|
dotg->vbus_retry_count = 0; |
|
work = 1; |
|
@@ -992,6 +1035,82 @@ static void dwc3_otg_sm_work(struct work |
|
#ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION |
|
host_send_uevent(USB_HOST_EXT_EVENT_VBUS_DROP); |
|
#endif |
|
+ } else if (test_bit(B_SESS_VLD, &dotg->inputs)) { |
|
+ if (ID_MODE == ID_A && !acaenabled) { |
|
+ dev_dbg(phy->dev, "b_sess_vld\n"); |
|
+ /* Has charger been detected? If no detect it */ |
|
+ switch (charger->chg_type) { |
|
+ case DWC3_DCP_CHARGER: |
|
+ case DWC3_CDP_CHARGER: |
|
+ case DWC3_PROPRIETARY_CHARGER: |
|
+ dwc3_otg_set_power(phy, |
|
+ DWC3_IDEV_CHG_MAX); |
|
+ break; |
|
+ case DWC3_SDP_CHARGER: |
|
+ break; |
|
+ case DWC3_FLOATED_CHARGER: |
|
+ if (dotg->charger_retry_count < |
|
+ max_chgr_retry_count) |
|
+ dotg->charger_retry_count++; |
|
+ /* |
|
+ * In case of floating charger, if |
|
+ * retry count equal to max retry count |
|
+ * notify PMIC about floating charger |
|
+ * and put Hw in low power mode. Else |
|
+ * perform charger detection again by |
|
+ * calling start_detection() with false |
|
+ * and then with true argument. |
|
+ */ |
|
+ if (dotg->charger_retry_count == |
|
+ max_chgr_retry_count) { |
|
+ charger->pulldown_dp(charger, |
|
+ false); |
|
+ dwc3_otg_set_power(phy, 0); |
|
+ qpnp_chg_notify_invalid_usb(); |
|
+ pm_runtime_put_sync(phy->dev); |
|
+ break; |
|
+ } |
|
+ charger->start_detection(dotg->charger, |
|
+ false); |
|
+ |
|
+ charger->pulldown_dp(charger, true); |
|
+ delay = msecs_to_jiffies(100 * |
|
+ dotg->charger_retry_count); |
|
+ work = 1; |
|
+ break; |
|
+ default: |
|
+ dev_dbg(phy->dev, "chg_det started\n"); |
|
+ charger->start_detection(charger, true); |
|
+ return; |
|
+ } |
|
+ |
|
+ ret = dwc3_otg_start_host(&dotg->otg, 1); |
|
+ if (!ret) |
|
+ acaenabled = 1; |
|
+ else { |
|
+ /* |
|
+ * Probably set_host was not called yet. |
|
+ * We will re-try as soon as it will be called |
|
+ */ |
|
+ |
|
+ dev_dbg(phy->dev, "enter lpm as\n" |
|
+ "unable to start A-device\n"); |
|
+ pm_runtime_put_sync(phy->dev); |
|
+ return; |
|
+ } |
|
+ } |
|
+ } else if (ID_MODE == ID_A) { |
|
+ /* Charger has been removed */ |
|
+ dev_dbg(phy->dev, "Charger removed, trying to suspend\n"); |
|
+ if (acaenabled) { |
|
+ dwc3_otg_start_host(&dotg->otg, 0); |
|
+ acaenabled = 0; |
|
+ } |
|
+ charger->start_detection(dotg->charger, false); |
|
+ |
|
+ dotg->charger_retry_count = 0; |
|
+ dwc3_otg_set_power(phy, 0); |
|
+ pm_runtime_put_sync(phy->dev); |
|
} |
|
break; |
|
|
|
@@ -1041,7 +1160,7 @@ static void dwc3_otg_reset(struct dwc3_o |
|
/* Clear all otg events (interrupts) indications */ |
|
dwc3_writel(dotg->regs, DWC3_OEVT, 0xFFFF); |
|
|
|
- /* Enable ID/BSV StsChngEn event*/ |
|
+ /* Enable ID_MODE/BSV StsChngEn event*/ |
|
if (ext_xceiv && !ext_xceiv->otg_capability) |
|
dwc3_writel(dotg->regs, DWC3_OEVTEN, |
|
DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT | |
|
@@ -1084,7 +1203,7 @@ int dwc3_otg_init(struct dwc3 *dwc) |
|
return -ENOMEM; |
|
} |
|
|
|
- /* DWC3 has separate IRQ line for OTG events (ID/BSV etc.) */ |
|
+ /* DWC3 has separate IRQ line for OTG events (ID_MODE/BSV etc.) */ |
|
dotg->irq = platform_get_irq_byname(to_platform_device(dwc->dev), |
|
"otg_irq"); |
|
if (dotg->irq < 0) { |