Loading Documentation/devicetree/bindings/usb/dwc3.txt +1 −6 Original line number Diff line number Diff line Loading @@ -7,9 +7,6 @@ Required properties: - reg : Address and length of the register set for the device - interrupts: Interrupts used by the dwc3 controller. - usb-phy : array of phandle for the PHY device - interrupt-names : Required interrupt resource entries are: "irq" : Interrupt for DWC3 core "otg_irq" : Interrupt for DWC3 core's OTG Events Optional properties: - tx-fifo-resize: determines if the FIFO *has* to be reallocated. Loading @@ -32,9 +29,7 @@ This is usually a subnode to DWC3 glue to which it is connected. dwc3@4a030000 { compatible = "synopsys,dwc3"; reg = <0x4a030000 0xcfff>; interrupts = <0 92 4>, <0 179 0>; interrupt-names = "irq", "otg_irq"; interrupts = <0 92 4>; usb-phy = <&usb2_phy>, <&usb3,phy>; tx-fifo-resize; snps,hsphy-auto-suspend-disable; }; Documentation/devicetree/bindings/usb/msm-ssusb.txt +0 −2 Original line number Diff line number Diff line Loading @@ -25,8 +25,6 @@ Optional properties : - qcom,msm_bus,vectors - interrupt-names : Optional interrupt resource entries are: "pmic_id_irq" : Interrupt from PMIC for external ID pin notification. - qcom,otg-capability: If present then depend on PMIC for VBUS notifications, otherwise depend on PHY. - qcom,charging-disabled: If present then battery charging using USB is disabled. - qcom,skip-charger-detection: If present then charger detection using BC1.2 Loading drivers/usb/dwc3/dwc3-msm.c +63 −77 Original line number Diff line number Diff line Loading @@ -2006,7 +2006,7 @@ static void dwc3_resume_work(struct work_struct *w) } pm_runtime_put_noidle(mdwc->dev); if (mdwc->otg_xceiv && (mdwc->ext_xceiv.otg_capability)) { if (mdwc->otg_xceiv) { dwc3_wait_for_ext_chg_done(mdwc); mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg, DWC3_EVENT_XCEIV_STATE); Loading Loading @@ -2274,7 +2274,6 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { static bool init; struct dwc3_msm *mdwc = container_of(psy, struct dwc3_msm, usb_psy); Loading @@ -2282,7 +2281,7 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy, case POWER_SUPPLY_PROP_USB_OTG: /* Let OTG know about ID detection */ mdwc->id_state = val->intval ? DWC3_ID_GROUND : DWC3_ID_FLOAT; if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability) if (mdwc->otg_xceiv) queue_work(system_nrt_wq, &mdwc->id_work); break; Loading @@ -2299,8 +2298,7 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy, /* Process PMIC notification in PRESENT prop */ case POWER_SUPPLY_PROP_PRESENT: dev_dbg(mdwc->dev, "%s: notify xceiv event\n", __func__); if (mdwc->otg_xceiv && !mdwc->ext_inuse && (mdwc->ext_xceiv.otg_capability || !init)) { if (mdwc->otg_xceiv && !mdwc->ext_inuse) { if (mdwc->ext_xceiv.bsv == val->intval) break; Loading @@ -2312,9 +2310,6 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy, dbg_event(0xFF, "Q RW (vbus)", val->intval); queue_delayed_work(system_nrt_wq, &mdwc->resume_work, 12); if (!init) init = true; } mdwc->vbus_active = val->intval; break; Loading Loading @@ -2946,8 +2941,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) clk_prepare_enable(mdwc->ref_clk); mdwc->id_state = mdwc->ext_xceiv.id = DWC3_ID_FLOAT; mdwc->ext_xceiv.otg_capability = of_property_read_bool(node, "qcom,otg-capability"); mdwc->charger.charging_disabled = of_property_read_bool(node, "qcom,charging-disabled"); Loading Loading @@ -3027,13 +3020,10 @@ static int dwc3_msm_probe(struct platform_device *pdev) } } if (mdwc->ext_xceiv.otg_capability) { mdwc->pmic_id_irq = platform_get_irq_byname(pdev, "pmic_id_irq"); mdwc->pmic_id_irq = platform_get_irq_byname(pdev, "pmic_id_irq"); if (mdwc->pmic_id_irq > 0) { /* check if PMIC MISC module monitors usb ID */ if (of_get_property(pdev->dev.of_node, "qcom,misc-ref", NULL)) if (of_get_property(pdev->dev.of_node, "qcom,misc-ref", NULL)) /* check if PMIC ID IRQ is supported */ ret = qpnp_misc_irqs_available(&pdev->dev); else Loading Loading @@ -3067,7 +3057,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) device_create_file(&pdev->dev, &dev_attr_adc_enable); mdwc->pmic_id_irq = 0; } } res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { Loading Loading @@ -3143,32 +3132,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) "unable to read platform data qdss tx fifo size\n"); dwc3_set_notifier(&dwc3_msm_notify_event); /* usb_psy required only for vbus_notifications or charging support */ if (mdwc->ext_xceiv.otg_capability || !mdwc->charger.charging_disabled) { mdwc->usb_psy.name = "usb"; mdwc->usb_psy.type = POWER_SUPPLY_TYPE_USB; mdwc->usb_psy.supplied_to = dwc3_msm_pm_power_supplied_to; mdwc->usb_psy.num_supplicants = ARRAY_SIZE( dwc3_msm_pm_power_supplied_to); mdwc->usb_psy.properties = dwc3_msm_pm_power_props_usb; mdwc->usb_psy.num_properties = ARRAY_SIZE(dwc3_msm_pm_power_props_usb); mdwc->usb_psy.get_property = dwc3_msm_power_get_property_usb; mdwc->usb_psy.set_property = dwc3_msm_power_set_property_usb; mdwc->usb_psy.external_power_changed = dwc3_msm_external_power_changed; mdwc->usb_psy.property_is_writeable = dwc3_msm_property_is_writeable; ret = power_supply_register(&pdev->dev, &mdwc->usb_psy); if (ret < 0) { dev_err(&pdev->dev, "%s:power_supply_register usb failed\n", __func__); goto disable_ref_clk; } } /* Assumes dwc3 is the only DT child of dwc3-msm */ dwc3_node = of_get_next_available_child(node, NULL); Loading Loading @@ -3196,6 +3159,32 @@ static int dwc3_msm_probe(struct platform_device *pdev) } } /* usb_psy required only for vbus_notifications */ if (!host_mode) { mdwc->usb_psy.name = "usb"; mdwc->usb_psy.type = POWER_SUPPLY_TYPE_USB; mdwc->usb_psy.supplied_to = dwc3_msm_pm_power_supplied_to; mdwc->usb_psy.num_supplicants = ARRAY_SIZE( dwc3_msm_pm_power_supplied_to); mdwc->usb_psy.properties = dwc3_msm_pm_power_props_usb; mdwc->usb_psy.num_properties = ARRAY_SIZE(dwc3_msm_pm_power_props_usb); mdwc->usb_psy.get_property = dwc3_msm_power_get_property_usb; mdwc->usb_psy.set_property = dwc3_msm_power_set_property_usb; mdwc->usb_psy.external_power_changed = dwc3_msm_external_power_changed; mdwc->usb_psy.property_is_writeable = dwc3_msm_property_is_writeable; ret = power_supply_register(&pdev->dev, &mdwc->usb_psy); if (ret < 0) { dev_err(&pdev->dev, "%s:power_supply_register usb failed\n", __func__); goto disable_ref_clk; } } /* Perform controller GCC reset */ dwc3_msm_link_clk_reset(mdwc, 1); msleep(20); Loading Loading @@ -3269,7 +3258,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) } } if (mdwc->ext_xceiv.otg_capability) mdwc->ext_xceiv.ext_block_reset = dwc3_msm_block_reset; ret = dwc3_set_ext_xceiv(mdwc->otg_xceiv->otg, &mdwc->ext_xceiv); Loading @@ -3289,7 +3277,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) goto put_dwc3; } if (mdwc->ext_xceiv.otg_capability && mdwc->charger.start_detection) { if (mdwc->charger.start_detection) { ret = dwc3_msm_setup_cdev(mdwc); if (ret) dev_err(&pdev->dev, "Fail to setup dwc3 setup cdev\n"); Loading Loading @@ -3498,9 +3486,7 @@ static int dwc3_msm_pm_resume(struct device *dev) dwc3_flush_event_buffers(mdwc); mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg, DWC3_EVENT_PHY_RESUME); if (mdwc->ext_xceiv.otg_capability) mdwc->ext_xceiv.notify_ext_events( mdwc->otg_xceiv->otg, mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg, DWC3_EVENT_XCEIV_STATE); } else if (mdwc->scope == POWER_SUPPLY_SCOPE_SYSTEM) { Loading drivers/usb/dwc3/dwc3_otg.c +4 −239 Original line number Diff line number Diff line Loading @@ -31,30 +31,7 @@ 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"); static void dwc3_otg_reset(struct dwc3_otg *dotg); static int dwc3_otg_set_host(struct usb_otg *otg, struct usb_bus *host); static void dwc3_otg_notify_host_mode(struct usb_otg *otg, int host_mode); static void dwc3_otg_reset(struct dwc3_otg *dotg); /** * dwc3_otg_set_host_power - Enable port power control for host operation * * This function enables the OTG Port Power required to operate in Host mode * This function should be called only after XHCI driver has set the port * power in PORTSC register. * * @w: Pointer to the dwc3 otg struct */ void dwc3_otg_set_host_power(struct dwc3_otg *dotg) { u32 osts; osts = dwc3_readl(dotg->regs, DWC3_OSTS); if (!(osts & 0x8)) dev_err(dotg->dwc->dev, "%s: xHCIPrtPower not set\n", __func__); dwc3_writel(dotg->regs, DWC3_OCTL, DWC3_OTG_OCTL_PRTPWRCTL); } /** * dwc3_otg_start_host - helper function for starting/stoping the host controller driver. Loading Loading @@ -125,11 +102,7 @@ static int dwc3_otg_start_host(struct usb_otg *otg, int on) pm_runtime_disable(&dwc->xhci->dev); 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); otg->host = &hcd->self; dwc3_gadget_usb3_phy_suspend(dwc, true); } else { Loading @@ -145,7 +118,7 @@ static int dwc3_otg_start_host(struct usb_otg *otg, int on) 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); otg->host = NULL; platform_device_del(dwc->xhci); /* Loading @@ -153,8 +126,7 @@ static int dwc3_otg_start_host(struct usb_otg *otg, int on) * 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) if (ext_xceiv && ext_xceiv->ext_block_reset) ext_xceiv->ext_block_reset(ext_xceiv, true); dwc3_gadget_usb3_phy_suspend(dwc, false); Loading @@ -162,8 +134,6 @@ static int dwc3_otg_start_host(struct usb_otg *otg, int on) /* 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); } Loading @@ -171,36 +141,6 @@ static int dwc3_otg_start_host(struct usb_otg *otg, int on) return 0; } /** * dwc3_otg_set_host - bind/unbind the host controller driver. * * @otg: Pointer to the otg_transceiver structure. * @host: Pointer to the usb_bus structure. * * Returns 0 on success otherwise negative errno. */ static int dwc3_otg_set_host(struct usb_otg *otg, struct usb_bus *host) { struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); if (host) { dev_dbg(otg->phy->dev, "%s: set host %s, portpower\n", __func__, host->bus_name); otg->host = host; /* * Though XHCI power would be set by now, but some delay is * required for XHCI controller before setting OTG Port Power * TODO: Tune this delay */ msleep(300); dwc3_otg_set_host_power(dotg); } else { otg->host = NULL; } return 0; } /** * dwc3_otg_start_peripheral - bind/unbind the peripheral controller. * Loading @@ -226,8 +166,7 @@ static int dwc3_otg_start_peripheral(struct usb_otg *otg, int on) /* Core reset is not required during start peripheral. Only * DBM reset is required, hence perform only DBM reset here */ if (ext_xceiv && ext_xceiv->otg_capability && ext_xceiv->ext_block_reset) if (ext_xceiv && ext_xceiv->ext_block_reset) ext_xceiv->ext_block_reset(ext_xceiv, false); dwc3_set_mode(dotg->dwc, DWC3_GCTL_PRTCAP_DEVICE); Loading Loading @@ -512,71 +451,6 @@ psy_error: 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 Loading @@ -584,14 +458,10 @@ static irqreturn_t dwc3_otg_interrupt(int irq, void *_dotg) */ 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. Loading @@ -603,9 +473,6 @@ void dwc3_otg_init_sm(struct dwc3_otg *dotg) 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 Loading @@ -613,18 +480,6 @@ void dwc3_otg_init_sm(struct dwc3_otg *dotg) */ 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); } } /** Loading Loading @@ -815,10 +670,6 @@ static void dwc3_otg_sm_work(struct work_struct *w) 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; Loading Loading @@ -862,50 +713,6 @@ static void dwc3_otg_sm_work(struct work_struct *w) queue_delayed_work(system_nrt_wq, &dotg->sm_work, delay); } /** * dwc3_otg_reset - reset dwc3 otg registers. * * @w: Pointer to the dwc3 otg workqueue */ static void dwc3_otg_reset(struct dwc3_otg *dotg) { static int once; struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv; /* * OCFG[2] - OTG-Version = 1 * OCFG[1] - HNPCap = 0 * OCFG[0] - SRPCap = 0 */ if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_writel(dotg->regs, DWC3_OCFG, 0x4); /* * OCTL[6] - PeriMode = 1 * OCTL[5] - PrtPwrCtl = 0 * OCTL[4] - HNPReq = 0 * OCTL[3] - SesReq = 0 * OCTL[2] - TermSelDLPulse = 0 * OCTL[1] - DevSetHNPEn = 0 * OCTL[0] - HstSetHNPEn = 0 */ if (!once) { if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_writel(dotg->regs, DWC3_OCTL, 0x40); once++; } /* Clear all otg events (interrupts) indications */ dwc3_writel(dotg->regs, DWC3_OEVT, 0xFFFF); /* Enable ID/BSV StsChngEn event*/ if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_writel(dotg->regs, DWC3_OEVTEN, DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT | DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT); } /** * dwc3_otg_init - Initializes otg related registers * @dwc: Pointer to out controller context structure Loading @@ -914,8 +721,6 @@ static void dwc3_otg_reset(struct dwc3_otg *dotg) */ int dwc3_otg_init(struct dwc3 *dwc) { u32 reg; int ret = 0; struct dwc3_otg *dotg; dev_dbg(dwc->dev, "dwc3_otg_init\n"); Loading @@ -938,33 +743,8 @@ int dwc3_otg_init(struct dwc3 *dwc) dotg->otg.phy->dev = dwc->dev; dotg->otg.phy->set_power = dwc3_otg_set_power; dotg->otg.set_peripheral = dwc3_otg_set_peripheral; dotg->otg.set_host = dwc3_otg_set_host; dotg->otg.phy->set_suspend = dwc3_otg_set_suspend; dotg->otg.phy->state = OTG_STATE_UNDEFINED; /* * 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. * Continue otg_init as currently we don't have Device only mode * support. */ dev_dbg(dwc->dev, "dwc3_otg address space is not supported\n"); } /* 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__); return -ENODEV; } dotg->regs = dwc->regs; /* This reference is used by dwc3 modules for checking otg existance */ Loading @@ -975,24 +755,10 @@ int dwc3_otg_init(struct dwc3 *dwc) init_completion(&dotg->dwc3_xcvr_vbus_init); INIT_DELAYED_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 err1; } dbg_event(0xFF, "OTGInit get", 0); pm_runtime_get(dwc->dev); return 0; err1: cancel_delayed_work_sync(&dotg->sm_work); dwc->dotg = NULL; return ret; } /** Loading @@ -1012,7 +778,6 @@ void dwc3_otg_exit(struct dwc3 *dwc) cancel_delayed_work_sync(&dotg->sm_work); dbg_event(0xFF, "OTGExit put", 0); pm_runtime_put(dwc->dev); free_irq(dotg->irq, dotg); dwc->dotg = NULL; } } drivers/usb/dwc3/dwc3_otg.h +1 −4 Original line number Diff line number Diff line /** * dwc3_otg.h - DesignWare USB3 DRD Controller OTG * * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -29,7 +29,6 @@ struct dwc3_charger; /** * struct dwc3_otg: OTG driver data. Shared by HCD and DCD. * @otg: USB OTG Transceiver structure. * @irq: IRQ number assigned for HSUSB controller. * @regs: ioremapped register base address. * @sm_work: OTG state machine work. * @charger: DWC3 external charger detector Loading @@ -37,7 +36,6 @@ struct dwc3_charger; */ struct dwc3_otg { struct usb_otg otg; int irq; struct dwc3 *dwc; void __iomem *regs; struct regulator *vbus_otg; Loading Loading @@ -109,7 +107,6 @@ enum dwc3_id_state { struct dwc3_ext_xceiv { enum dwc3_id_state id; bool bsv; bool otg_capability; /* to notify OTG about LPM exit event, provided by OTG */ void (*notify_ext_events)(struct usb_otg *otg, Loading Loading
Documentation/devicetree/bindings/usb/dwc3.txt +1 −6 Original line number Diff line number Diff line Loading @@ -7,9 +7,6 @@ Required properties: - reg : Address and length of the register set for the device - interrupts: Interrupts used by the dwc3 controller. - usb-phy : array of phandle for the PHY device - interrupt-names : Required interrupt resource entries are: "irq" : Interrupt for DWC3 core "otg_irq" : Interrupt for DWC3 core's OTG Events Optional properties: - tx-fifo-resize: determines if the FIFO *has* to be reallocated. Loading @@ -32,9 +29,7 @@ This is usually a subnode to DWC3 glue to which it is connected. dwc3@4a030000 { compatible = "synopsys,dwc3"; reg = <0x4a030000 0xcfff>; interrupts = <0 92 4>, <0 179 0>; interrupt-names = "irq", "otg_irq"; interrupts = <0 92 4>; usb-phy = <&usb2_phy>, <&usb3,phy>; tx-fifo-resize; snps,hsphy-auto-suspend-disable; };
Documentation/devicetree/bindings/usb/msm-ssusb.txt +0 −2 Original line number Diff line number Diff line Loading @@ -25,8 +25,6 @@ Optional properties : - qcom,msm_bus,vectors - interrupt-names : Optional interrupt resource entries are: "pmic_id_irq" : Interrupt from PMIC for external ID pin notification. - qcom,otg-capability: If present then depend on PMIC for VBUS notifications, otherwise depend on PHY. - qcom,charging-disabled: If present then battery charging using USB is disabled. - qcom,skip-charger-detection: If present then charger detection using BC1.2 Loading
drivers/usb/dwc3/dwc3-msm.c +63 −77 Original line number Diff line number Diff line Loading @@ -2006,7 +2006,7 @@ static void dwc3_resume_work(struct work_struct *w) } pm_runtime_put_noidle(mdwc->dev); if (mdwc->otg_xceiv && (mdwc->ext_xceiv.otg_capability)) { if (mdwc->otg_xceiv) { dwc3_wait_for_ext_chg_done(mdwc); mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg, DWC3_EVENT_XCEIV_STATE); Loading Loading @@ -2274,7 +2274,6 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { static bool init; struct dwc3_msm *mdwc = container_of(psy, struct dwc3_msm, usb_psy); Loading @@ -2282,7 +2281,7 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy, case POWER_SUPPLY_PROP_USB_OTG: /* Let OTG know about ID detection */ mdwc->id_state = val->intval ? DWC3_ID_GROUND : DWC3_ID_FLOAT; if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability) if (mdwc->otg_xceiv) queue_work(system_nrt_wq, &mdwc->id_work); break; Loading @@ -2299,8 +2298,7 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy, /* Process PMIC notification in PRESENT prop */ case POWER_SUPPLY_PROP_PRESENT: dev_dbg(mdwc->dev, "%s: notify xceiv event\n", __func__); if (mdwc->otg_xceiv && !mdwc->ext_inuse && (mdwc->ext_xceiv.otg_capability || !init)) { if (mdwc->otg_xceiv && !mdwc->ext_inuse) { if (mdwc->ext_xceiv.bsv == val->intval) break; Loading @@ -2312,9 +2310,6 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy, dbg_event(0xFF, "Q RW (vbus)", val->intval); queue_delayed_work(system_nrt_wq, &mdwc->resume_work, 12); if (!init) init = true; } mdwc->vbus_active = val->intval; break; Loading Loading @@ -2946,8 +2941,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) clk_prepare_enable(mdwc->ref_clk); mdwc->id_state = mdwc->ext_xceiv.id = DWC3_ID_FLOAT; mdwc->ext_xceiv.otg_capability = of_property_read_bool(node, "qcom,otg-capability"); mdwc->charger.charging_disabled = of_property_read_bool(node, "qcom,charging-disabled"); Loading Loading @@ -3027,13 +3020,10 @@ static int dwc3_msm_probe(struct platform_device *pdev) } } if (mdwc->ext_xceiv.otg_capability) { mdwc->pmic_id_irq = platform_get_irq_byname(pdev, "pmic_id_irq"); mdwc->pmic_id_irq = platform_get_irq_byname(pdev, "pmic_id_irq"); if (mdwc->pmic_id_irq > 0) { /* check if PMIC MISC module monitors usb ID */ if (of_get_property(pdev->dev.of_node, "qcom,misc-ref", NULL)) if (of_get_property(pdev->dev.of_node, "qcom,misc-ref", NULL)) /* check if PMIC ID IRQ is supported */ ret = qpnp_misc_irqs_available(&pdev->dev); else Loading Loading @@ -3067,7 +3057,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) device_create_file(&pdev->dev, &dev_attr_adc_enable); mdwc->pmic_id_irq = 0; } } res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { Loading Loading @@ -3143,32 +3132,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) "unable to read platform data qdss tx fifo size\n"); dwc3_set_notifier(&dwc3_msm_notify_event); /* usb_psy required only for vbus_notifications or charging support */ if (mdwc->ext_xceiv.otg_capability || !mdwc->charger.charging_disabled) { mdwc->usb_psy.name = "usb"; mdwc->usb_psy.type = POWER_SUPPLY_TYPE_USB; mdwc->usb_psy.supplied_to = dwc3_msm_pm_power_supplied_to; mdwc->usb_psy.num_supplicants = ARRAY_SIZE( dwc3_msm_pm_power_supplied_to); mdwc->usb_psy.properties = dwc3_msm_pm_power_props_usb; mdwc->usb_psy.num_properties = ARRAY_SIZE(dwc3_msm_pm_power_props_usb); mdwc->usb_psy.get_property = dwc3_msm_power_get_property_usb; mdwc->usb_psy.set_property = dwc3_msm_power_set_property_usb; mdwc->usb_psy.external_power_changed = dwc3_msm_external_power_changed; mdwc->usb_psy.property_is_writeable = dwc3_msm_property_is_writeable; ret = power_supply_register(&pdev->dev, &mdwc->usb_psy); if (ret < 0) { dev_err(&pdev->dev, "%s:power_supply_register usb failed\n", __func__); goto disable_ref_clk; } } /* Assumes dwc3 is the only DT child of dwc3-msm */ dwc3_node = of_get_next_available_child(node, NULL); Loading Loading @@ -3196,6 +3159,32 @@ static int dwc3_msm_probe(struct platform_device *pdev) } } /* usb_psy required only for vbus_notifications */ if (!host_mode) { mdwc->usb_psy.name = "usb"; mdwc->usb_psy.type = POWER_SUPPLY_TYPE_USB; mdwc->usb_psy.supplied_to = dwc3_msm_pm_power_supplied_to; mdwc->usb_psy.num_supplicants = ARRAY_SIZE( dwc3_msm_pm_power_supplied_to); mdwc->usb_psy.properties = dwc3_msm_pm_power_props_usb; mdwc->usb_psy.num_properties = ARRAY_SIZE(dwc3_msm_pm_power_props_usb); mdwc->usb_psy.get_property = dwc3_msm_power_get_property_usb; mdwc->usb_psy.set_property = dwc3_msm_power_set_property_usb; mdwc->usb_psy.external_power_changed = dwc3_msm_external_power_changed; mdwc->usb_psy.property_is_writeable = dwc3_msm_property_is_writeable; ret = power_supply_register(&pdev->dev, &mdwc->usb_psy); if (ret < 0) { dev_err(&pdev->dev, "%s:power_supply_register usb failed\n", __func__); goto disable_ref_clk; } } /* Perform controller GCC reset */ dwc3_msm_link_clk_reset(mdwc, 1); msleep(20); Loading Loading @@ -3269,7 +3258,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) } } if (mdwc->ext_xceiv.otg_capability) mdwc->ext_xceiv.ext_block_reset = dwc3_msm_block_reset; ret = dwc3_set_ext_xceiv(mdwc->otg_xceiv->otg, &mdwc->ext_xceiv); Loading @@ -3289,7 +3277,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) goto put_dwc3; } if (mdwc->ext_xceiv.otg_capability && mdwc->charger.start_detection) { if (mdwc->charger.start_detection) { ret = dwc3_msm_setup_cdev(mdwc); if (ret) dev_err(&pdev->dev, "Fail to setup dwc3 setup cdev\n"); Loading Loading @@ -3498,9 +3486,7 @@ static int dwc3_msm_pm_resume(struct device *dev) dwc3_flush_event_buffers(mdwc); mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg, DWC3_EVENT_PHY_RESUME); if (mdwc->ext_xceiv.otg_capability) mdwc->ext_xceiv.notify_ext_events( mdwc->otg_xceiv->otg, mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg, DWC3_EVENT_XCEIV_STATE); } else if (mdwc->scope == POWER_SUPPLY_SCOPE_SYSTEM) { Loading
drivers/usb/dwc3/dwc3_otg.c +4 −239 Original line number Diff line number Diff line Loading @@ -31,30 +31,7 @@ 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"); static void dwc3_otg_reset(struct dwc3_otg *dotg); static int dwc3_otg_set_host(struct usb_otg *otg, struct usb_bus *host); static void dwc3_otg_notify_host_mode(struct usb_otg *otg, int host_mode); static void dwc3_otg_reset(struct dwc3_otg *dotg); /** * dwc3_otg_set_host_power - Enable port power control for host operation * * This function enables the OTG Port Power required to operate in Host mode * This function should be called only after XHCI driver has set the port * power in PORTSC register. * * @w: Pointer to the dwc3 otg struct */ void dwc3_otg_set_host_power(struct dwc3_otg *dotg) { u32 osts; osts = dwc3_readl(dotg->regs, DWC3_OSTS); if (!(osts & 0x8)) dev_err(dotg->dwc->dev, "%s: xHCIPrtPower not set\n", __func__); dwc3_writel(dotg->regs, DWC3_OCTL, DWC3_OTG_OCTL_PRTPWRCTL); } /** * dwc3_otg_start_host - helper function for starting/stoping the host controller driver. Loading Loading @@ -125,11 +102,7 @@ static int dwc3_otg_start_host(struct usb_otg *otg, int on) pm_runtime_disable(&dwc->xhci->dev); 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); otg->host = &hcd->self; dwc3_gadget_usb3_phy_suspend(dwc, true); } else { Loading @@ -145,7 +118,7 @@ static int dwc3_otg_start_host(struct usb_otg *otg, int on) 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); otg->host = NULL; platform_device_del(dwc->xhci); /* Loading @@ -153,8 +126,7 @@ static int dwc3_otg_start_host(struct usb_otg *otg, int on) * 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) if (ext_xceiv && ext_xceiv->ext_block_reset) ext_xceiv->ext_block_reset(ext_xceiv, true); dwc3_gadget_usb3_phy_suspend(dwc, false); Loading @@ -162,8 +134,6 @@ static int dwc3_otg_start_host(struct usb_otg *otg, int on) /* 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); } Loading @@ -171,36 +141,6 @@ static int dwc3_otg_start_host(struct usb_otg *otg, int on) return 0; } /** * dwc3_otg_set_host - bind/unbind the host controller driver. * * @otg: Pointer to the otg_transceiver structure. * @host: Pointer to the usb_bus structure. * * Returns 0 on success otherwise negative errno. */ static int dwc3_otg_set_host(struct usb_otg *otg, struct usb_bus *host) { struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); if (host) { dev_dbg(otg->phy->dev, "%s: set host %s, portpower\n", __func__, host->bus_name); otg->host = host; /* * Though XHCI power would be set by now, but some delay is * required for XHCI controller before setting OTG Port Power * TODO: Tune this delay */ msleep(300); dwc3_otg_set_host_power(dotg); } else { otg->host = NULL; } return 0; } /** * dwc3_otg_start_peripheral - bind/unbind the peripheral controller. * Loading @@ -226,8 +166,7 @@ static int dwc3_otg_start_peripheral(struct usb_otg *otg, int on) /* Core reset is not required during start peripheral. Only * DBM reset is required, hence perform only DBM reset here */ if (ext_xceiv && ext_xceiv->otg_capability && ext_xceiv->ext_block_reset) if (ext_xceiv && ext_xceiv->ext_block_reset) ext_xceiv->ext_block_reset(ext_xceiv, false); dwc3_set_mode(dotg->dwc, DWC3_GCTL_PRTCAP_DEVICE); Loading Loading @@ -512,71 +451,6 @@ psy_error: 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 Loading @@ -584,14 +458,10 @@ static irqreturn_t dwc3_otg_interrupt(int irq, void *_dotg) */ 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. Loading @@ -603,9 +473,6 @@ void dwc3_otg_init_sm(struct dwc3_otg *dotg) 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 Loading @@ -613,18 +480,6 @@ void dwc3_otg_init_sm(struct dwc3_otg *dotg) */ 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); } } /** Loading Loading @@ -815,10 +670,6 @@ static void dwc3_otg_sm_work(struct work_struct *w) 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; Loading Loading @@ -862,50 +713,6 @@ static void dwc3_otg_sm_work(struct work_struct *w) queue_delayed_work(system_nrt_wq, &dotg->sm_work, delay); } /** * dwc3_otg_reset - reset dwc3 otg registers. * * @w: Pointer to the dwc3 otg workqueue */ static void dwc3_otg_reset(struct dwc3_otg *dotg) { static int once; struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv; /* * OCFG[2] - OTG-Version = 1 * OCFG[1] - HNPCap = 0 * OCFG[0] - SRPCap = 0 */ if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_writel(dotg->regs, DWC3_OCFG, 0x4); /* * OCTL[6] - PeriMode = 1 * OCTL[5] - PrtPwrCtl = 0 * OCTL[4] - HNPReq = 0 * OCTL[3] - SesReq = 0 * OCTL[2] - TermSelDLPulse = 0 * OCTL[1] - DevSetHNPEn = 0 * OCTL[0] - HstSetHNPEn = 0 */ if (!once) { if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_writel(dotg->regs, DWC3_OCTL, 0x40); once++; } /* Clear all otg events (interrupts) indications */ dwc3_writel(dotg->regs, DWC3_OEVT, 0xFFFF); /* Enable ID/BSV StsChngEn event*/ if (ext_xceiv && !ext_xceiv->otg_capability) dwc3_writel(dotg->regs, DWC3_OEVTEN, DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT | DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT); } /** * dwc3_otg_init - Initializes otg related registers * @dwc: Pointer to out controller context structure Loading @@ -914,8 +721,6 @@ static void dwc3_otg_reset(struct dwc3_otg *dotg) */ int dwc3_otg_init(struct dwc3 *dwc) { u32 reg; int ret = 0; struct dwc3_otg *dotg; dev_dbg(dwc->dev, "dwc3_otg_init\n"); Loading @@ -938,33 +743,8 @@ int dwc3_otg_init(struct dwc3 *dwc) dotg->otg.phy->dev = dwc->dev; dotg->otg.phy->set_power = dwc3_otg_set_power; dotg->otg.set_peripheral = dwc3_otg_set_peripheral; dotg->otg.set_host = dwc3_otg_set_host; dotg->otg.phy->set_suspend = dwc3_otg_set_suspend; dotg->otg.phy->state = OTG_STATE_UNDEFINED; /* * 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. * Continue otg_init as currently we don't have Device only mode * support. */ dev_dbg(dwc->dev, "dwc3_otg address space is not supported\n"); } /* 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__); return -ENODEV; } dotg->regs = dwc->regs; /* This reference is used by dwc3 modules for checking otg existance */ Loading @@ -975,24 +755,10 @@ int dwc3_otg_init(struct dwc3 *dwc) init_completion(&dotg->dwc3_xcvr_vbus_init); INIT_DELAYED_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 err1; } dbg_event(0xFF, "OTGInit get", 0); pm_runtime_get(dwc->dev); return 0; err1: cancel_delayed_work_sync(&dotg->sm_work); dwc->dotg = NULL; return ret; } /** Loading @@ -1012,7 +778,6 @@ void dwc3_otg_exit(struct dwc3 *dwc) cancel_delayed_work_sync(&dotg->sm_work); dbg_event(0xFF, "OTGExit put", 0); pm_runtime_put(dwc->dev); free_irq(dotg->irq, dotg); dwc->dotg = NULL; } }
drivers/usb/dwc3/dwc3_otg.h +1 −4 Original line number Diff line number Diff line /** * dwc3_otg.h - DesignWare USB3 DRD Controller OTG * * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -29,7 +29,6 @@ struct dwc3_charger; /** * struct dwc3_otg: OTG driver data. Shared by HCD and DCD. * @otg: USB OTG Transceiver structure. * @irq: IRQ number assigned for HSUSB controller. * @regs: ioremapped register base address. * @sm_work: OTG state machine work. * @charger: DWC3 external charger detector Loading @@ -37,7 +36,6 @@ struct dwc3_charger; */ struct dwc3_otg { struct usb_otg otg; int irq; struct dwc3 *dwc; void __iomem *regs; struct regulator *vbus_otg; Loading Loading @@ -109,7 +107,6 @@ enum dwc3_id_state { struct dwc3_ext_xceiv { enum dwc3_id_state id; bool bsv; bool otg_capability; /* to notify OTG about LPM exit event, provided by OTG */ void (*notify_ext_events)(struct usb_otg *otg, Loading