/** * dwc3_otg_init_sm - initialize OTG statemachine input * @dotg: Pointer to the dwc3_otg structure * */ void dwc3_otg_init_sm(struct dwc3_otg *dotg) { u32 osts = dwc3_readl(dotg->regs, DWC3_OSTS); struct usb_phy *phy = dotg->otg.phy; struct dwc3_ext_xceiv *ext_xceiv; int ret; dev_dbg(phy->dev, "Initialize OTG inputs, osts: 0x%x\n", osts); /* * VBUS initial state is reported after PMIC * driver initialization. Wait for it. */ ret = wait_for_completion_timeout(&dotg->dwc3_xcvr_vbus_init, HZ * 5); if (!ret) { dev_err(phy->dev, "%s: completion timeout\n", __func__); /* We can safely assume no cable connected */ set_bit(ID, &dotg->inputs); } ext_xceiv = dotg->ext_xceiv; dwc3_otg_reset(dotg); if (ext_xceiv && !ext_xceiv->otg_capability) { if (osts & DWC3_OTG_OSTS_CONIDSTS) set_bit(ID, &dotg->inputs); else clear_bit(ID, &dotg->inputs); if (osts & DWC3_OTG_OSTS_BSESVALID) set_bit(B_SESS_VLD, &dotg->inputs); else clear_bit(B_SESS_VLD, &dotg->inputs); } }
/** * dwc3_otg_init_sm - initialize OTG statemachine input * @dotg: Pointer to the dwc3_otg structure * */ void dwc3_otg_init_sm(struct dwc3_otg *dotg) { u32 osts = dwc3_readl(dotg->regs, DWC3_OSTS); struct usb_phy *phy = dotg->otg.phy; struct dwc3_ext_xceiv *ext_xceiv; struct dwc3 *dwc = dotg->dwc; int ret; dev_dbg(phy->dev, "Initialize OTG inputs, osts: 0x%x\n", osts); /* * VBUS initial state is reported after PMIC * driver initialization. Wait for it. */ ret = wait_for_completion_timeout(&dotg->dwc3_xcvr_vbus_init, HZ * 5); if (!ret) { dev_err(phy->dev, "%s: completion timeout\n", __func__); /* We can safely assume no cable connected */ set_bit(ID, &dotg->inputs); } ext_xceiv = dotg->ext_xceiv; dwc3_otg_reset(dotg); /* * If vbus-present property was set then set BSV to 1. * This is needed for emulation platforms as PMIC ID * interrupt is not available. */ if (dwc->vbus_active) set_bit(B_SESS_VLD, &dotg->inputs); if (ext_xceiv && !ext_xceiv->otg_capability) { if (osts & DWC3_OTG_OSTS_CONIDSTS) set_bit(ID, &dotg->inputs); else clear_bit(ID, &dotg->inputs); if (osts & DWC3_OTG_OSTS_BSESVALID) set_bit(B_SESS_VLD, &dotg->inputs); else clear_bit(B_SESS_VLD, &dotg->inputs); } }
/** * dwc3_otg_start * @dwc: pointer to our controller context structure */ int dwc3_otg_start(struct dwc3 *dwc) { struct dwc3_otg *dotg = dwc->dotg; struct otg_fsm *fsm = &dotg->fsm; int ret; if (dotg->ext_otg_ops) { ret = dwc3_ext_otg_start(dotg); if (ret) { dev_err(dwc->dev, "failed to start external OTG\n"); return ret; } } else { dotg->regs = dwc->regs; dwc3_otg_reset(dotg); dotg->fsm.id = dwc3_otg_get_id_state(dotg); dotg->fsm.b_sess_vld = dwc3_otg_get_b_sess_state(dotg); dotg->irq = platform_get_irq(to_platform_device(dwc->dev), 0); ret = devm_request_threaded_irq(dwc->dev, dotg->irq, dwc3_otg_interrupt, dwc3_otg_thread_interrupt, IRQF_SHARED, "dwc3-otg", dotg); if (ret) { dev_err(dwc->dev, "failed to request irq #%d --> %d\n", dotg->irq, ret); return ret; } dwc3_otg_enable_irq(dotg); } dotg->ready = 1; dwc3_otg_run_sm(fsm); return 0; }
/** * dwc3_otg_start_peripheral - bind/unbind the peripheral controller. * * @otg: Pointer to the otg_transceiver structure. * @gadget: pointer to the usb_gadget structure. * * Returns 0 on success otherwise negative errno. */ static int dwc3_otg_start_peripheral(struct usb_otg *otg, int on) { struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv; struct dwc3 *dwc = dotg->dwc; if (!otg->gadget) return -EINVAL; if (on) { dev_dbg(otg->phy->dev, "%s: turn on gadget %s\n", __func__, otg->gadget->name); /* * Hardware reset is required to support below scenarios: * 1. Host <-> peripheral switching * 2. Once an endpoint is configured in DBM (BAM) mode, it * can be unconfigured only after RESET */ if (ext_xceiv && ext_xceiv->otg_capability && ext_xceiv->ext_block_reset) ext_xceiv->ext_block_reset(); /* re-init core and OTG registers as block reset clears these */ dwc3_post_host_reset_core_init(dwc); if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_otg_reset(dotg); dwc3_otg_set_peripheral_regs(dotg); usb_gadget_vbus_connect(otg->gadget); } else { dev_dbg(otg->phy->dev, "%s: turn off gadget %s\n", __func__, otg->gadget->name); usb_gadget_vbus_disconnect(otg->gadget); } return 0; }
/** * dwc3_otg_start_host - helper function for starting/stoping the host controller driver. * * @otg: Pointer to the otg_transceiver structure. * @on: start / stop the host controller driver. * * Returns 0 on success otherwise negative errno. */ static int dwc3_otg_start_host(struct usb_otg *otg, int on) { struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv; struct dwc3 *dwc = dotg->dwc; int ret = 0; 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 (on) { dev_dbg(otg->phy->dev, "%s: turn on host\n", __func__); dwc3_otg_notify_host_mode(otg, on); 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; } /* * This should be revisited for more testing post-silicon. * In worst case we may need to disconnect the root hub * before stopping the controller so that it does not * interfere with runtime pm/system pm. * We can also consider registering and unregistering xhci * platform device. It is almost similar to add_hcd and * remove_hcd, But we may not use standard set_host method * anymore. */ dwc3_otg_set_hsphy_auto_suspend(dotg, true); dwc3_otg_set_host_regs(dotg); /* * FIXME If micro A cable is disconnected during system suspend, * xhci platform device will be removed before runtime pm is * enabled for xhci device. Due to this, disable_depth becomes * greater than one and runtimepm is not enabled for next microA * connect. Fix this by calling pm_runtime_init for xhci device. */ pm_runtime_init(&dwc->xhci->dev); ret = platform_device_add(dwc->xhci); if (ret) { 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); return ret; } /* re-init OTG EVTEN register as XHCI reset clears it */ if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_otg_reset(dotg); } 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; } dwc3_otg_notify_host_mode(otg, on); platform_device_del(dwc->xhci); /* * Perform USB hardware RESET (both core reset and DBM reset) * when moving from host to peripheral. This is required for * peripheral mode to work. */ if (ext_xceiv && ext_xceiv->otg_capability && ext_xceiv->ext_block_reset) ext_xceiv->ext_block_reset(ext_xceiv, true); dwc3_otg_set_hsphy_auto_suspend(dotg, false); dwc3_otg_set_peripheral_regs(dotg); /* re-init core and OTG registers as block reset clears these */ dwc3_post_host_reset_core_init(dwc); if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_otg_reset(dotg); } return 0; }
static int dwc3_otg_set_power(struct usb_phy *phy, unsigned mA) { static int power_supply_type; struct dwc3_otg *dotg = container_of(phy->otg, struct dwc3_otg, otg); #if defined(CONFIG_DWC3_MSM_BC_12_VZW_SUPPORT) && defined(CONFIG_LGE_PM) static bool chglogo_check = false; #endif if (!dotg->psy || !dotg->charger) { dev_err(phy->dev, "no usb power supply/charger registered\n"); return 0; } if (dotg->charger->charging_disabled) return 0; #ifdef CONFIG_LGE_PM if (dotg->charger->chg_type == DWC3_SDP_CHARGER || dotg->charger->chg_type == DWC3_FLOATED_CHARGER) #else if (dotg->charger->chg_type == DWC3_SDP_CHARGER) #endif power_supply_type = POWER_SUPPLY_TYPE_USB; else if (dotg->charger->chg_type == DWC3_CDP_CHARGER) power_supply_type = POWER_SUPPLY_TYPE_USB_CDP; else if (dotg->charger->chg_type == DWC3_DCP_CHARGER || dotg->charger->chg_type == DWC3_PROPRIETARY_CHARGER) power_supply_type = POWER_SUPPLY_TYPE_USB_DCP; else power_supply_type = POWER_SUPPLY_TYPE_UNKNOWN; #ifndef CONFIG_LGE_PM power_supply_set_supply_type(dotg->psy, power_supply_type); #endif #if defined (CONFIG_TOUCHSCREEN_SYNAPTICS_G3) && defined (CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) update_status(1, dotg->charger->chg_type); #endif #if defined(CONFIG_DWC3_MSM_BC_12_VZW_SUPPORT) && defined(CONFIG_LGE_PM) if (!chglogo_check && lge_get_boot_mode() == LGE_BOOT_MODE_CHARGERLOGO && dotg->charger->chg_type == DWC3_SDP_CHARGER) { if (mA > IUNIT) chglogo_check = true; else if (mA <= 2) { dotg->charger->max_power = mA; return 0; } } if (mA > 2 && lge_pm_get_cable_type() != NO_INIT_CABLE) { if (dotg->charger->chg_type == DWC3_DCP_CHARGER) mA = lge_pm_get_ta_current(); } #elif defined(CONFIG_LGE_PM) if (mA > 2 && lge_pm_get_cable_type() != NO_INIT_CABLE) { if (dotg->charger->chg_type == DWC3_SDP_CHARGER) { if (dotg->dwc->gadget.speed == USB_SPEED_SUPER) { if (dotg->charger->max_power > 2) dotg->charger->max_power = 0; mA = DWC3_USB30_CHG_CURRENT; } else { mA = lge_pm_get_usb_current(); } #ifdef CONFIG_QPNP_CHARGER /* For MST, boost current up over 900mA in spite of USB */ if (pseudo_batt_info.mode && mA == 500 ) mA = DWC3_USB30_CHG_CURRENT; #endif } else if (dotg->charger->chg_type == DWC3_DCP_CHARGER) { mA = lge_pm_get_ta_current(); } else if (dotg->charger->chg_type == DWC3_FLOATED_CHARGER) { mA = lge_pm_get_usb_current(); } } #endif if (dotg->charger->chg_type == DWC3_CDP_CHARGER) mA = DWC3_IDEV_CHG_MAX; if (dotg->charger->max_power == mA) return 0; dev_info(phy->dev, "Avail curr from USB = %u\n", mA); /* */ #ifdef CONFIG_LGE_PM #ifndef CONFIG_USB_DWC3_LGE_SINGLE_PSY if (dwc3_otg_get_psy(phy) < 0) goto psy_error; #else if (strcmp(dotg->psy->name, "usb")) { pr_info("%s psy name is %s, so change psy to usb.\n", __func__, dotg->psy->name); dotg->psy = power_supply_get_by_name("usb"); if (!dotg->psy) goto psy_error; } #endif power_supply_set_supply_type(dotg->psy, power_supply_type); #endif #if defined(CONFIG_DWC3_MSM_BC_12_VZW_SUPPORT) && defined(CONFIG_LGE_PM) if (dotg->charger->max_power <= IUNIT && mA > 2) { #else if (dotg->charger->max_power <= 2 && mA > 2) { #endif /* Enable charging */ if (power_supply_set_online(dotg->psy, true)) goto psy_error; if (power_supply_set_current_limit(dotg->psy, 1000*mA)) goto psy_error; #ifdef CONFIG_QPNP_CHARGER if (!strncmp(dotg->psy->name, "ac", 2)) { dotg->psy = power_supply_get_by_name("usb"); if (!dotg->psy) goto psy_error; if (power_supply_set_online(dotg->psy, true)) goto psy_error; if (power_supply_set_supply_type(dotg->psy, power_supply_type)) goto psy_error; if (power_supply_set_current_limit(dotg->psy, 1000*mA)) goto psy_error; dotg->psy = power_supply_get_by_name("ac"); if (!dotg->psy) goto psy_error; } #endif } else if (dotg->charger->max_power > 0 && (mA == 0 || mA == 2)) { /* Disable charging */ if (power_supply_set_online(dotg->psy, false)) goto psy_error; /* Set max current limit */ if (power_supply_set_current_limit(dotg->psy, 0)) goto psy_error; #ifdef CONFIG_QPNP_CHARGER if (!strncmp(dotg->psy->name, "ac", 2)) { dotg->psy = power_supply_get_by_name("usb"); if (!dotg->psy) goto psy_error; if (power_supply_set_online(dotg->psy, false)) goto psy_error; if (power_supply_set_supply_type(dotg->psy, power_supply_type)) goto psy_error; if (power_supply_set_current_limit(dotg->psy, 0)) goto psy_error; dotg->psy = power_supply_get_by_name("ac"); if (!dotg->psy) goto psy_error; } #endif #ifndef CONFIG_USB_DWC3_LGE_SINGLE_PSY dotg->charger->chg_type = DWC3_INVALID_CHARGER; #endif } power_supply_changed(dotg->psy); dotg->charger->max_power = mA; #if defined (CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) #if defined (CONFIG_TOUCHSCREEN_SYNAPTICS_G2) || defined (CONFIG_MACH_MSM8974_TIGERS) queue_work(touch_otg_wq, &dotg->touch_work); #endif #endif return 0; psy_error: dev_dbg(phy->dev, "power supply error when setting property\n"); return -ENXIO; } /* IRQs which OTG driver is interested in handling */ #define DWC3_OEVT_MASK (DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT | \ DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT) /** * dwc3_otg_interrupt - interrupt handler for dwc3 otg events. * @_dotg: Pointer to out controller context structure * * Returns IRQ_HANDLED on success otherwise IRQ_NONE. */ static irqreturn_t dwc3_otg_interrupt(int irq, void *_dotg) { struct dwc3_otg *dotg = (struct dwc3_otg *)_dotg; u32 osts, oevt_reg; int ret = IRQ_NONE; int handled_irqs = 0; struct usb_phy *phy = dotg->otg.phy; oevt_reg = dwc3_readl(dotg->regs, DWC3_OEVT); if (!(oevt_reg & DWC3_OEVT_MASK)) return IRQ_NONE; osts = dwc3_readl(dotg->regs, DWC3_OSTS); if ((oevt_reg & DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT) || (oevt_reg & DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT)) { /* * ID 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_dbg(phy->dev, "ID set\n"); set_bit(ID, &dotg->inputs); } else { dev_dbg(phy->dev, "ID clear\n"); clear_bit(ID, &dotg->inputs); } handled_irqs |= DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT; } if (oevt_reg & DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT) { if (osts & DWC3_OTG_OSTS_BSESVALID) { dev_dbg(phy->dev, "BSV set\n"); set_bit(B_SESS_VLD, &dotg->inputs); } else { dev_dbg(phy->dev, "BSV clear\n"); clear_bit(B_SESS_VLD, &dotg->inputs); } handled_irqs |= DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT; } queue_delayed_work(system_nrt_wq, &dotg->sm_work, 0); ret = IRQ_HANDLED; /* Clear the interrupts we handled */ dwc3_writel(dotg->regs, DWC3_OEVT, handled_irqs); } return ret; } /** * dwc3_otg_init_sm - initialize OTG statemachine input * @dotg: Pointer to the dwc3_otg structure * */ void dwc3_otg_init_sm(struct dwc3_otg *dotg) { u32 osts = dwc3_readl(dotg->regs, DWC3_OSTS); struct usb_phy *phy = dotg->otg.phy; struct dwc3_ext_xceiv *ext_xceiv; int ret; dev_dbg(phy->dev, "Initialize OTG inputs, osts: 0x%x\n", osts); /* * VBUS initial state is reported after PMIC * driver initialization. Wait for it. */ ret = wait_for_completion_timeout(&dotg->dwc3_xcvr_vbus_init, HZ * 5); if (!ret) { dev_err(phy->dev, "%s: completion timeout\n", __func__); /* We can safely assume no cable connected */ set_bit(ID, &dotg->inputs); } ext_xceiv = dotg->ext_xceiv; dwc3_otg_reset(dotg); if (ext_xceiv && !ext_xceiv->otg_capability) { if (osts & DWC3_OTG_OSTS_CONIDSTS) set_bit(ID, &dotg->inputs); else clear_bit(ID, &dotg->inputs); if (osts & DWC3_OTG_OSTS_BSESVALID) set_bit(B_SESS_VLD, &dotg->inputs); else clear_bit(B_SESS_VLD, &dotg->inputs); } }
static int dwc3_otg_start_host(struct usb_otg *otg, int on) { struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv; struct dwc3 *dwc = dotg->dwc; int ret = 0; #ifdef CONFIG_PANTECH_QUALCOMM_OTG_MODE_OVP_BUG //xsemiyas_debug int value; #endif #if defined(CONFIG_PANTECH_USB_SMB_OTG_DISABLE_LOW_BATTERY) || defined(CONFIG_PANTECH_USB_TI_OTG_DISABLE_LOW_BATTERY) int level; #endif if (!dwc->xhci) return -EINVAL; #ifdef FEATURE_PANTECH_USB_CHARGER_INIT_ERROR if(!charger_ready_status()) return -ENODEV; #endif #if !defined(CONFIG_PANTECH_PMIC_CHARGER_SMB347) && !defined(CONFIG_PANTECH_PMIC_CHARGER_SMB349) && !defined(CONFIG_PANTECH_PMIC_CHARGER_BQ2419X) 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; } } #endif /* !defined(CONFIG_PANTECH_PMIC_CHARGER_SMB347) && !defined(CONFIG_PANTECH_PMIC_CHARGER_SMB349) && !defined(CONFIG_PANTECH_CHARGER_BQ2419X) */ #if defined(CONFIG_PANTECH_USB_SMB_OTG_DISABLE_LOW_BATTERY) && defined(CONFIG_PANTECH_PMIC_OTG_UVLO) level = max17058_get_soc_for_otg(); #elif defined(CONFIG_PANTECH_USB_TI_OTG_DISABLE_LOW_BATTERY) && defined(CONFIG_PANTECH_EF63_PMIC_FUELGAUGE_MAX17058) level = max17058_get_soc_for_led(); #else level = 50; #endif if (on) { dev_dbg(otg->phy->dev, "%s: turn on host\n", __func__); #if defined(CONFIG_PANTECH_PMIC_CHARGER_SMB347) || defined(CONFIG_PANTECH_PMIC_CHARGER_SMB349) //xsemiyas_debug:warmup_time smb349_otg_power(1); usleep_range(60, 100); #ifdef CONFIG_PANTECH_PMIC_OTG smb349_otg_power_current(3); #endif /* CONFIG_PANTECH_PMIC_OTG */ smb349_otg_power(0); #endif /* defined(CONFIG_PANTECH_PMIC_CHARGER_SMB347) || defined(CONFIG_PANTECH_PMIC_CHARGER_SMB349) */ /* * This should be revisited for more testing post-silicon. * In worst case we may need to disconnect the root hub * before stopping the controller so that it does not * interfere with runtime pm/system pm. * We can also consider registering and unregistering xhci * platform device. It is almost similar to add_hcd and * remove_hcd, But we may not use standard set_host method * anymore. */ dwc3_otg_set_hsphy_auto_suspend(dotg, true); dwc3_otg_set_host_regs(dotg); /* * FIXME If micro A cable is disconnected during system suspend, * xhci platform device will be removed before runtime pm is * enabled for xhci device. Due to this, disable_depth becomes * greater than one and runtimepm is not enabled for next microA * connect. Fix this by calling pm_runtime_init for xhci device. */ pm_runtime_init(&dwc->xhci->dev); ret = platform_device_add(dwc->xhci); if (ret) { dev_err(otg->phy->dev, "%s: failed to add XHCI pdev ret=%d\n", __func__, ret); return ret; } dwc3_otg_notify_host_mode(otg, on); #ifdef CONFIG_PANTECH_QUALCOMM_OTG_MODE_OVP_BUG //xsemiyas_debug while((value = get_pantech_chg_otg_mode()) != 1){ if(value == -1){ set_pantech_chg_otg_mode(0); dev_err(otg->phy->dev, "unable to enable qpnp_otg_mode\n"); return -ENXIO; } msleep(10); } #endif #if defined(CONFIG_PANTECH_USB_SMB_OTG_DISABLE_LOW_BATTERY) if (level < 30) { dev_err(dwc->dev, "Low Battery!!, OTG power not set!\n"); #ifdef CONFIG_ANDROID_PANTECH_USB_OTG_INTENT set_otg_dev_state(2); #endif return 0; } #elif defined(CONFIG_PANTECH_USB_TI_OTG_DISABLE_LOW_BATTERY) if (level < 10) { dev_err(dwc->dev, "Battery is not enough!, OTG power remove!\n"); #ifdef CONFIG_ANDROID_PANTECH_USB_OTG_INTENT set_otg_dev_state(2); #endif #if defined(CONFIG_PANTECH_PMIC_CHARGER_BQ2419X) //chg_force_switching_scope(1); //otg power remove dwc3_otg_notify_host_mode(otg, 0); //otg power remove #endif return 0; } #endif #ifdef CONFIG_PANTECH_USB_TUNE_SIGNALING_PARAM pantech_set_otg_signaling_param(on); #endif #if defined(CONFIG_PANTECH_PMIC_CHARGER_SMB347) || defined(CONFIG_PANTECH_PMIC_CHARGER_SMB349) smb349_otg_power(on); #ifdef CONFIG_PANTECH_PMIC_OTG usleep_range(60, 100);//xsemiyas_debug: change 60uS smb349_otg_power_current(3); //750mA current setting #endif /* CONFIG_PANTECH_PMIC_OTG */ #elif !defined(CONFIG_PANTECH_PMIC_CHARGER_BQ2419X) ret = regulator_enable(dotg->vbus_otg); if (ret) { dev_err(otg->phy->dev, "unable to enable vbus_otg\n"); platform_device_del(dwc->xhci); return ret; } #endif /* defined(CONFIG_PANTECH_PMIC_CHARGER_SMB347) || defined(CONFIG_PANTECH_PMIC_CHARGER_SMB349) */ /* re-init OTG EVTEN register as XHCI reset clears it */ if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_otg_reset(dotg); #if defined(CONFIG_PANTECH_USB_SMB_OTG_DISABLE_LOW_BATTERY) || defined(CONFIG_PANTECH_USB_TI_OTG_DISABLE_LOW_BATTERY) is_otg_enabled = 1; #endif } else { dev_dbg(otg->phy->dev, "%s: turn off host\n", __func__); #ifdef CONFIG_PANTECH_USB_SMB_OTG_DISABLE_LOW_BATTERY if (level >= 30) { #endif #if defined(CONFIG_PANTECH_PMIC_CHARGER_SMB347) || defined(CONFIG_PANTECH_PMIC_CHARGER_SMB349) smb349_otg_power(on); #ifdef CONFIG_PANTECH_PMIC_OTG smb349_otg_power_current(1); //250mA current setting #endif /* CONFIG_PANTECH_PMIC_OTG */ #ifdef CONFIG_PANTECH_USB_SMB_OTG_DISABLE_LOW_BATTERY } #endif #elif !defined(CONFIG_PANTECH_PMIC_CHARGER_BQ2419X) ret = regulator_disable(dotg->vbus_otg); if (ret) { dev_err(otg->phy->dev, "unable to disable vbus_otg\n"); return ret; } #endif /* defined(CONFIG_PANTECH_PMIC_CHARGER_SMB347) || defined(CONFIG_PANTECH_PMIC_CHARGER_SMB349) */ #ifdef CONFIG_PANTECH_USB_TUNE_SIGNALING_PARAM pantech_set_otg_signaling_param(on); #endif #if defined(CONFIG_PANTECH_USB_SMB_OTG_DISABLE_LOW_BATTERY) || defined(CONFIG_PANTECH_USB_TI_OTG_DISABLE_LOW_BATTERY) is_otg_enabled = 0; #endif dwc3_otg_notify_host_mode(otg, on); #ifdef CONFIG_PANTECH_SIO_BUG_FIX /* FIXME : Sometimes sm_work is executed twice in abnormal state. * So platform_device_del is executed twice. * And this code is occurred NULL pointer exception at seconds procedure. * We are fixed below code only executed one. * from LS4-USB tarial */ if (dwc->xhci) platform_device_del(dwc->xhci); #else platform_device_del(dwc->xhci); #endif /* * Perform USB hardware RESET (both core reset and DBM reset) * when moving from host to peripheral. This is required for * peripheral mode to work. */ if (ext_xceiv && ext_xceiv->otg_capability && ext_xceiv->ext_block_reset) ext_xceiv->ext_block_reset(ext_xceiv, true); dwc3_otg_set_hsphy_auto_suspend(dotg, false); dwc3_otg_set_peripheral_regs(dotg); /* re-init core and OTG registers as block reset clears these */ dwc3_post_host_reset_core_init(dwc); if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_otg_reset(dotg); } return 0; }
/** * dwc3_otg_init - Initializes otg related registers * @dwc: Pointer to out controller context structure * * Returns 0 on success otherwise negative errno. */ int dwc3_otg_init(struct dwc3 *dwc) { u32 reg; int ret = 0; struct dwc3_otg *dotg; dev_dbg(dwc->dev, "dwc3_otg_init\n"); /* * GHWPARAMS6[10] bit is SRPSupport. * This bit also reflects DWC_USB3_EN_OTG */ reg = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); if (!(reg & DWC3_GHWPARAMS6_SRP_SUPPORT)) { /* * No OTG support in the HW core. * We return 0 to indicate no error, since this is acceptable * situation, just continue probe the dwc3 driver without otg. */ dev_dbg(dwc->dev, "dwc3_otg address space is not supported\n"); return 0; } /* Allocate and init otg instance */ dotg = kzalloc(sizeof(struct dwc3_otg), GFP_KERNEL); if (!dotg) { dev_err(dwc->dev, "unable to allocate dwc3_otg\n"); return -ENOMEM; } /* DWC3 has separate IRQ line for OTG events (ID/BSV etc.) */ dotg->irq = platform_get_irq_byname(to_platform_device(dwc->dev), "otg_irq"); if (dotg->irq < 0) { dev_err(dwc->dev, "%s: missing OTG IRQ\n", __func__); ret = -ENODEV; goto err1; } dotg->regs = dwc->regs; dotg->otg.set_peripheral = dwc3_otg_set_peripheral; dotg->otg.set_host = dwc3_otg_set_host; /* This reference is used by dwc3 modules for checking otg existance */ dwc->dotg = dotg; dotg->otg.phy = kzalloc(sizeof(struct usb_phy), GFP_KERNEL); if (!dotg->otg.phy) { dev_err(dwc->dev, "unable to allocate dwc3_otg.phy\n"); ret = -ENOMEM; goto err1; } dotg->otg.phy->otg = &dotg->otg; dotg->otg.phy->dev = dwc->dev; ret = usb_set_transceiver(dotg->otg.phy); if (ret) { dev_err(dotg->otg.phy->dev, "%s: failed to set transceiver, already exists\n", __func__); goto err2; } dwc3_otg_reset(dotg); dotg->otg.phy->state = OTG_STATE_UNDEFINED; INIT_WORK(&dotg->sm_work, dwc3_otg_sm_work); ret = request_irq(dotg->irq, dwc3_otg_interrupt, IRQF_SHARED, "dwc3_otg", dotg); if (ret) { dev_err(dotg->otg.phy->dev, "failed to request irq #%d --> %d\n", dotg->irq, ret); goto err3; } return 0; err3: cancel_work_sync(&dotg->sm_work); usb_set_transceiver(NULL); err2: kfree(dotg->otg.phy); err1: dwc->dotg = NULL; kfree(dotg); return ret; }
/** * dwc3_otg_start_host - helper function for starting/stoping the host controller driver. * * @otg: Pointer to the otg_transceiver structure. * @on: start / stop the host controller driver. * * Returns 0 on success otherwise negative errno. */ static int dwc3_otg_start_host(struct usb_otg *otg, int on) { struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv; struct dwc3 *dwc = dotg->dwc; struct usb_hcd *hcd; int ret = 0; 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 (on) { dev_dbg(otg->phy->dev, "%s: turn on host\n", __func__); dwc3_otg_notify_host_mode(otg, on); usb_phy_notify_connect(dotg->dwc->usb2_phy, USB_SPEED_HIGH); 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; } dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); /* * FIXME If micro A cable is disconnected during system suspend, * xhci platform device will be removed before runtime pm is * enabled for xhci device. Due to this, disable_depth becomes * greater than one and runtimepm is not enabled for next microA * connect. Fix this by calling pm_runtime_init for xhci device. */ pm_runtime_init(&dwc->xhci->dev); ret = platform_device_add(dwc->xhci); if (ret) { 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); return ret; } hcd = platform_get_drvdata(dwc->xhci); dwc3_otg_set_host(otg, &hcd->self); /* re-init OTG EVTEN register as XHCI reset clears it */ if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_otg_reset(dotg); dwc3_gadget_usb3_phy_suspend(dwc, true); } 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; } dbg_event(0xFF, "StHost get", 0); pm_runtime_get(dwc->dev); usb_phy_notify_disconnect(dotg->dwc->usb2_phy, USB_SPEED_HIGH); dwc3_otg_notify_host_mode(otg, on); dwc3_otg_set_host(otg, NULL); platform_device_del(dwc->xhci); /* * Perform USB hardware RESET (both core reset and DBM reset) * when moving from host to peripheral. This is required for * peripheral mode to work. */ if (ext_xceiv && ext_xceiv->otg_capability && ext_xceiv->ext_block_reset) ext_xceiv->ext_block_reset(ext_xceiv, true); dwc3_gadget_usb3_phy_suspend(dwc, false); dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); /* re-init core and OTG registers as block reset clears these */ dwc3_post_host_reset_core_init(dwc); if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_otg_reset(dotg); dbg_event(0xFF, "StHost put", 0); pm_runtime_put(dwc->dev); } return 0; }
/** * dwc3_otg_start_host - helper function for starting/stoping the host controller driver. * * @otg: Pointer to the otg_transceiver structure. * @on: start / stop the host controller driver. * * Returns 0 on success otherwise negative errno. */ static int dwc3_otg_start_host(struct usb_otg *otg, int on) { struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv; struct dwc3 *dwc = dotg->dwc; int ret = 0; 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 (on) { dev_dbg(otg->phy->dev, "%s: turn on host\n", __func__); /* * This should be revisited for more testing post-silicon. * In worst case we may need to disconnect the root hub * before stopping the controller so that it does not * interfere with runtime pm/system pm. * We can also consider registering and unregistering xhci * platform device. It is almost similar to add_hcd and * remove_hcd, But we may not use standard set_host method * anymore. */ dwc3_otg_set_host_regs(dotg); ret = platform_device_add(dwc->xhci); if (ret) { dev_err(otg->phy->dev, "%s: failed to add XHCI pdev ret=%d\n", __func__, ret); return ret; } dwc3_otg_notify_host_mode(otg, on); ret = regulator_enable(dotg->vbus_otg); if (ret) { dev_err(otg->phy->dev, "unable to enable vbus_otg\n"); platform_device_del(dwc->xhci); return ret; } /* re-init OTG EVTEN register as XHCI reset clears it */ if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_otg_reset(dotg); } else { dev_dbg(otg->phy->dev, "%s: turn off host\n", __func__); platform_device_del(dwc->xhci); ret = regulator_disable(dotg->vbus_otg); if (ret) { dev_err(otg->phy->dev, "unable to disable vbus_otg\n"); return ret; } dwc3_otg_notify_host_mode(otg, on); } return 0; }