> > > > > USB role is fully controlled by usb role switch consumer(e.g. > > > > > typec), usb port either at host mode, or at device connected > > > > > mode, will not stay at USB_ROLE_NONE mode. > > > > > > > > > > > > > Then, if the Type-C cable is disconnected from PC host, the > > > > controller driver can't be notified?If that, how controller enters > > > > low power mode at this > > situation? > > > > > > The controller driver can be notified, but there are only role(host > > > or > > > device) Information, so in you mentioned case, controller driver > > > will get the same input before and after disconnect from host, can't > > > know detachment and enter low power mode accordingly, this is more > > > like something internal handling under device role. > > > > > > > If the Type-C can't pass "gadget disconnect" event, you may handle it > > at UDC driver itself, eg at ci_usb_role_switch_set. Otherwise, the > > gadget class driver can't be notified disconnection, and the controller itself can't > enter LPM as well. > > OK, I will enable VBUS irq to handle this in v2. > You may use connect bit at portsc since VBUS may not connect to SoC. Peter > Li Jun > > > > Peter > > > > > Li Jun > > > > > > > > > > > Peter > > > > > > > > > Signed-off-by: Li Jun <jun.li@xxxxxxx> > > > > > --- > > > > > drivers/usb/chipidea/ci.h | 2 + > > > > > drivers/usb/chipidea/core.c | 125 > > > > > ++++++++++++++++++++++++++++++++++---------- > > > > > drivers/usb/chipidea/otg.c | 13 +++++ > > > > > 3 files changed, 111 insertions(+), 29 deletions(-) > > > > > > > > > > diff --git a/drivers/usb/chipidea/ci.h > > > > > b/drivers/usb/chipidea/ci.h index 82b86cd..5e2f0bc 100644 > > > > > --- a/drivers/usb/chipidea/ci.h > > > > > +++ b/drivers/usb/chipidea/ci.h > > > > > @@ -212,6 +212,7 @@ struct ci_hdrc { > > > > > ktime_t > > hr_timeouts[NUM_OTG_FSM_TIMERS]; > > > > > unsigned enabled_otg_timer_bits; > > > > > enum otg_fsm_timer next_otg_timer; > > > > > + struct usb_role_switch *role_switch; > > > > > struct work_struct work; > > > > > struct workqueue_struct *wq; > > > > > > > > > > @@ -244,6 +245,7 @@ struct ci_hdrc { > > > > > struct dentry *debugfs; > > > > > bool id_event; > > > > > bool b_sess_valid_event; > > > > > + bool role_switch_event; > > > > > bool imx28_write_fix; > > > > > bool supports_runtime_pm; > > > > > bool in_lpm; > > > > > diff --git a/drivers/usb/chipidea/core.c > > > > > b/drivers/usb/chipidea/core.c index bc24c5b..1bcf6f6 100644 > > > > > --- a/drivers/usb/chipidea/core.c > > > > > +++ b/drivers/usb/chipidea/core.c > > > > > @@ -587,6 +587,47 @@ static irqreturn_t ci_irq(int irq, void *data) > > > > > return ret; > > > > > } > > > > > > > > > > +static int ci_usb_role_switch_set(struct device *dev, enum > > > > > +usb_role > > > > > +role) { > > > > > + struct ci_hdrc *ci = dev_get_drvdata(dev); > > > > > + unsigned long flags; > > > > > + > > > > > + if (ci->role == role || role == USB_ROLE_NONE) > > > > > + return 0; > > > > > + > > > > > + spin_lock_irqsave(&ci->lock, flags); > > > > > + ci->role_switch_event = true; > > > > > + if (ci->role == USB_ROLE_NONE) { > > > > > + if (role == USB_ROLE_DEVICE) > > > > > + ci->role = USB_ROLE_HOST; > > > > > + else > > > > > + ci->role = USB_ROLE_DEVICE; > > > > > + } > > > > > + spin_unlock_irqrestore(&ci->lock, flags); > > > > > + > > > > > + ci_otg_queue_work(ci); > > > > > + > > > > > + return 0; > > > > > +} > > > > > + > > > > > +static enum usb_role ci_usb_role_switch_get(struct device *dev) { > > > > > + struct ci_hdrc *ci = dev_get_drvdata(dev); > > > > > + unsigned long flags; > > > > > + enum usb_role role; > > > > > + > > > > > + spin_lock_irqsave(&ci->lock, flags); > > > > > + role = ci->role; > > > > > + spin_unlock_irqrestore(&ci->lock, flags); > > > > > + > > > > > + return role; > > > > > +} > > > > > + > > > > > +static struct usb_role_switch_desc ci_role_switch = { > > > > > + .set = ci_usb_role_switch_set, > > > > > + .get = ci_usb_role_switch_get, }; > > > > > + > > > > > static int ci_cable_notifier(struct notifier_block *nb, unsigned long event, > > > > > void *ptr) { @@ -689,6 +730,9 @@ > > > > > static int ci_get_platdata(struct device *dev, > > > > > if (of_find_property(dev->of_node, "non-zero-ttctrl-ttha", NULL)) > > > > > platdata->flags |= CI_HDRC_SET_NON_ZERO_TTHA; > > > > > > > > > > + if (device_property_read_bool(dev, "usb-role-switch")) > > > > > + ci_role_switch.fwnode = dev->fwnode; > > > > > + > > > > > ext_id = ERR_PTR(-ENODEV); > > > > > ext_vbus = ERR_PTR(-ENODEV); > > > > > if (of_property_read_bool(dev->of_node, "extcon")) { @@ > > > > > -908,6 > > > > > +952,43 @@ static const struct attribute_group ci_attr_group = { > > > > > .attrs = ci_attrs, > > > > > }; > > > > > > > > > > +static int ci_start_initial_role(struct ci_hdrc *ci) { > > > > > + int ret = 0; > > > > > + > > > > > + if (ci->roles[USB_ROLE_HOST] && ci->roles[USB_ROLE_DEVICE]) { > > > > > + if (ci->is_otg) { > > > > > + ci->role = ci_otg_role(ci); > > > > > + /* Enable ID change irq */ > > > > > + hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); > > > > > + } else { > > > > > + /* > > > > > + * If the controller is not OTG capable, but support > > > > > + * role switch, the defalt role is gadget, and the > > > > > + * user can switch it through debugfs. > > > > > + */ > > > > > + ci->role = USB_ROLE_DEVICE; > > > > > + } > > > > > + } else { > > > > > + ci->role = ci->roles[USB_ROLE_HOST] > > > > > + ? USB_ROLE_HOST > > > > > + : USB_ROLE_DEVICE; > > > > > + } > > > > > + > > > > > + if (!ci_otg_is_fsm_mode(ci)) { > > > > > + /* only update vbus status for peripheral */ > > > > > + if (ci->role == USB_ROLE_DEVICE) > > > > > + ci_handle_vbus_change(ci); > > > > > + > > > > > + ret = ci_role_start(ci, ci->role); > > > > > + if (ret) > > > > > + dev_err(ci->dev, "can't start %s role\n", > > > > > + ci_role(ci)->name); > > > > > + } > > > > > + > > > > > + return ret; > > > > > +} > > > > > + > > > > > static int ci_hdrc_probe(struct platform_device *pdev) { > > > > > struct device *dev = &pdev->dev; > > > > > @@ -1051,36 +1132,10 @@ static int ci_hdrc_probe(struct > platform_device *pdev) > > > > > } > > > > > } > > > > > > > > > > - if (ci->roles[USB_ROLE_HOST] && ci->roles[USB_ROLE_DEVICE]) { > > > > > - if (ci->is_otg) { > > > > > - ci->role = ci_otg_role(ci); > > > > > - /* Enable ID change irq */ > > > > > - hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); > > > > > - } else { > > > > > - /* > > > > > - * If the controller is not OTG capable, but support > > > > > - * role switch, the defalt role is gadget, and the > > > > > - * user can switch it through debugfs. > > > > > - */ > > > > > - ci->role = USB_ROLE_DEVICE; > > > > > - } > > > > > - } else { > > > > > - ci->role = ci->roles[USB_ROLE_HOST] > > > > > - ? USB_ROLE_HOST > > > > > - : USB_ROLE_DEVICE; > > > > > - } > > > > > - > > > > > - if (!ci_otg_is_fsm_mode(ci)) { > > > > > - /* only update vbus status for peripheral */ > > > > > - if (ci->role == USB_ROLE_DEVICE) > > > > > - ci_handle_vbus_change(ci); > > > > > - > > > > > - ret = ci_role_start(ci, ci->role); > > > > > - if (ret) { > > > > > - dev_err(dev, "can't start %s role\n", > > > > > - ci_role(ci)->name); > > > > > + if (!ci_role_switch.fwnode) { > > > > > + ret = ci_start_initial_role(ci); > > > > > + if (ret) > > > > > goto stop; > > > > > - } > > > > > } > > > > > > > > > > ret = devm_request_irq(dev, ci->irq, ci_irq, > > > > > IRQF_SHARED, @@ > > > > > -1092,6 +1147,15 @@ static int ci_hdrc_probe(struct platform_device > *pdev) > > > > > if (ret) > > > > > goto stop; > > > > > > > > > > + if (ci_role_switch.fwnode) { > > > > > + ci->role_switch = usb_role_switch_register(dev, > > > > > + &ci_role_switch); > > > > > + if (IS_ERR(ci->role_switch)) { > > > > > + ret = PTR_ERR(ci->role_switch); > > > > > + goto stop; > > > > > + } > > > > > + } > > > > > + > > > > > if (ci->supports_runtime_pm) { > > > > > pm_runtime_set_active(&pdev->dev); > > > > > pm_runtime_enable(&pdev->dev); @@ -1133,6 > > > > > +1197,9 @@ static int ci_hdrc_remove(struct platform_device *pdev) { > > > > > struct ci_hdrc *ci = platform_get_drvdata(pdev); > > > > > > > > > > + if (ci->role_switch) > > > > > + usb_role_switch_unregister(ci->role_switch); > > > > > + > > > > > if (ci->supports_runtime_pm) { > > > > > pm_runtime_get_sync(&pdev->dev); > > > > > pm_runtime_disable(&pdev->dev); diff --git > > > > > a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index > > > > > 5bde0b5..0a22855 100644 > > > > > --- a/drivers/usb/chipidea/otg.c > > > > > +++ b/drivers/usb/chipidea/otg.c > > > > > @@ -214,6 +214,19 @@ static void ci_otg_work(struct work_struct *work) > > > > > ci_handle_vbus_change(ci); > > > > > } > > > > > > > > > > + if (ci->role_switch_event) { > > > > > + ci->role_switch_event = false; > > > > > + > > > > > + if (ci->role == USB_ROLE_DEVICE) { > > > > > + usb_gadget_vbus_disconnect(&ci->gadget); > > > > > + ci_role_start(ci, USB_ROLE_HOST); > > > > > + } else if (ci->role == USB_ROLE_HOST) { > > > > > + ci_role_stop(ci); > > > > > + usb_gadget_vbus_connect(&ci->gadget); > > > > > + ci->role = USB_ROLE_DEVICE; > > > > > + } > > > > > + } > > > > > + > > > > > pm_runtime_put_sync(ci->dev); > > > > > > > > > > enable_irq(ci->irq); > > > > > -- > > > > > 2.7.4 > > > > >