Re: [PATCH 3/6] usb: chipidea: imx: add wakeup interrupt handling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Feb 21, 2025 at 10:23:26AM -0500, Frank Li wrote:
> On Fri, Feb 21, 2025 at 11:23:48AM +0800, Xu Yang wrote:
> > On Wed, Feb 19, 2025 at 03:26:42PM -0500, Frank Li wrote:
> > > On Wed, Feb 19, 2025 at 05:31:01PM +0800, Xu Yang wrote:
> > > > In previous imx platform, normal USB controller interrupt and wakeup
> > > > interrupt are bound to one irq line. However, it changes on latest
> > > > i.MX95 platform since it has a dedicated irq line for wakeup interrupt.
> > > > This will add wakup interrupt handling for i.MX95 to support various
> > > > wakeup events.
> > > >
> > > > Reviewed-by: Jun Li <jun.li@xxxxxxx>
> > > > Signed-off-by: Xu Yang <xu.yang_2@xxxxxxx>
> > > > ---
> > > >  drivers/usb/chipidea/ci_hdrc_imx.c | 42 ++++++++++++++++++++++++++++++
> > > >  1 file changed, 42 insertions(+)
> > > >
> > > > diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
> > > > index 1a7fc638213e..5779568ebcf6 100644
> > > > --- a/drivers/usb/chipidea/ci_hdrc_imx.c
> > > > +++ b/drivers/usb/chipidea/ci_hdrc_imx.c
> > > > @@ -98,9 +98,12 @@ struct ci_hdrc_imx_data {
> > > >  	struct clk *clk;
> > > >  	struct clk *clk_wakeup;
> > > >  	struct imx_usbmisc_data *usbmisc_data;
> > > > +	/* wakeup interrupt*/
> > > > +	int irq;
> > >
> > > use "wakeup_irq" to avoid confuse with normal controller irq?
> >
> > It doesn't matter. It can't be confused since the driver is different. The
> > controller driver is core.c. This glue driver is ci_hdrc_imx.c.
> >
> > >
> > > >  	bool supports_runtime_pm;
> > > >  	bool override_phy_control;
> > > >  	bool in_lpm;
> > > > +	bool wakeup_pending;
> > > >  	struct pinctrl *pinctrl;
> > > >  	struct pinctrl_state *pinctrl_hsic_active;
> > > >  	struct regulator *hsic_pad_regulator;
> > > > @@ -336,6 +339,24 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
> > > >  	return ret;
> > > >  }
> > > >
> > > > +static irqreturn_t ci_wakeup_irq_handler(int irq, void *data)
> > > > +{
> > > > +	struct ci_hdrc_imx_data *imx_data = data;
> > > > +
> > > > +	if (imx_data->in_lpm) {
> > > > +		if (imx_data->wakeup_pending)
> > > > +			return IRQ_HANDLED;
> > > > +
> > > > +		disable_irq_nosync(irq);
> > > > +		imx_data->wakeup_pending = true;
> > > > +		pm_runtime_resume(&imx_data->ci_pdev->dev);
> > >
> > > Not sure why need pm_runtime_resume here? There are not access register
> > > here.
> >
> > It's needed for runtime resume case.
> > When wakeup event happens, wakeup irq will be triggered. To let controller
> > resume back, we need enable USB controller clock to trigger controller
> > irq. So we call pm_runtime_resume() to resume controller's parent back
> > and the parent device will enable clocks.
> 
> Please add comment here. why need in_lpm if we can make sure irq only
> enable during suspend/resume pharse?

I have checked again, in_lpm checking is not needed. I will remove the
if condition later.

> 
> >
> > >
> > > > +
> > > > +		return IRQ_HANDLED;
> > > > +	}
> > > > +
> > > > +	return IRQ_NONE;
> > > > +}
> > > > +
> > > >  static int ci_hdrc_imx_probe(struct platform_device *pdev)
> > > >  {
> > > >  	struct ci_hdrc_imx_data *data;
> > > > @@ -476,6 +497,15 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
> > > >  	if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
> > > >  		data->supports_runtime_pm = true;
> > > >
> > > > +	data->irq = platform_get_irq_optional(pdev, 1);
> > > > +	if (data->irq > 0) {
> > > > +		ret = devm_request_threaded_irq(dev, data->irq,
> > > > +				NULL, ci_wakeup_irq_handler,
> > > > +				IRQF_ONESHOT, pdata.name, data);
> > > > +		if (ret)
> > > > +			goto err_clk;
> > > > +	}
> > > > +
> > > >  	ret = imx_usbmisc_init(data->usbmisc_data);
> > > >  	if (ret) {
> > > >  		dev_err(dev, "usbmisc init failed, ret=%d\n", ret);
> > > > @@ -621,6 +651,11 @@ static int imx_controller_resume(struct device *dev,
> > > >  		goto clk_disable;
> > > >  	}
> > > >
> > > > +	if (data->wakeup_pending) {
> > > > +		data->wakeup_pending = false;
> > > > +		enable_irq(data->irq);
> > > > +	}
> > > > +
> > > >  	return 0;
> > > >
> > > >  clk_disable:
> > > > @@ -643,6 +678,10 @@ static int ci_hdrc_imx_suspend(struct device *dev)
> > > >  		return ret;
> > > >
> > > >  	pinctrl_pm_select_sleep_state(dev);
> > > > +
> > > > +	if (device_may_wakeup(dev))
> > > > +		enable_irq_wake(data->irq);
> > > > +
> > > >  	return ret;
> > > >  }
> > > >
> > > > @@ -651,6 +690,9 @@ static int ci_hdrc_imx_resume(struct device *dev)
> > > >  	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
> > > >  	int ret;
> > > >
> > > > +	if (device_may_wakeup(dev))
> > > > +		disable_irq_wake(data->irq);
> > > > +
> > >
> > > Look like only want enable wakeup irq between suspend and resume. There are
> > > some questions to understand hehavor.
> > >
> > > 1 driver probe --> 2. hdrc suspend -->3 hdrc resume --> 4 controller resume
> > >
> > > 1--2,  look like wakeup irq is enabled. maybe controller have some bit to
> > > disable issue wakeup irq, otherwise flood irq will happen because you check
> > > lpm in irq handler.
> >
> > It's not true.
> >
> > We has a bit WAKE_ENABLE to enable/disable wakeup irq. This bit will only be
> > enabled when do suspend() and disabled when do resume(). So before suspend()
> > called, the wakeup irq can't be triggered. No flood irq too.
> >
> > >
> > > after 2. wakeup irq enable,  if wakeup irq happen, system resume.
> > > ci_hdrc_imx_resume() only clear a flags, not any sync problem.
> > >
> > > But sequence imx_controller_resume() and ci_wakeup_irq_handler() may not
> > > guaranteed.
> > >
> > > If ci_wakeup_irq_handler() call first, ci_wakeup_irq_handler() disable
> > > itself. imx_controller_resume() will enable wakeup irq, which will be same
> > > state 1--2. but if imx_controller_resume() run firstly,
> >
> > It's not true too. Because WAKE_ENABLE is disabled after resume().
> >
> > > ci_wakeup_irq_handler() will disable wakeup irq, which difference state
> > > with 1--2.
> > >
> > > If there are a bit(WAKEUP_EN) in controller can control wakeup irq
> > > enable/disable,
> >
> > Yes, WAKE_ENABLE bit. It's already used:
> >
> > ci_hdrc_imx_suspend()
> >   imx_controller_suspend() -> enable WAKE_ENABLE
> >
> > ci_hdrc_imx_resume()
> >   imx_controller_resume() -> disable WAKE_ENABLE
> 
> Okay,
> 
> I think wakeup_pending is not neccesary. ci_wakeup_irq_handler() can
> simple disable WAKE_ENABLE.

Right, wakeup_pending can be removed. However, it's not that simple to
control WAKE_ENABLE in ci_hdrc_imx.c due to imx_controller_susepnd/resume()
do more things expect enable/disable WAKE_ENABLE bit. And usbmisc.c doesn't
export a function to directly control WAKE_ENABLE. Therefore, I still need
to use enable/disable_irq() for simplicity.

Due to this wakeup irq is only used in suspend case, I would like to add
IRQF_NO_AUTOEN flag to request_threaded_irq(), then enable irq in
ci_hdrc_imx_suspend() and disable irq in ci_hdrc_imx_resume(). 

For example:

ci_wakeup_irq_handler()
{
	disable_irq_nosync(data->irq);
	...
}

ci_hdrc_imx_suspend()
{
	...
	enable_irq(data->irq);	
}

ci_hdrc_imx_resume()
{
	if (irq disabled)
		disable_irq_nosync(data->irq);
	...
}

Do you think if it's feasible and better than current patch?

Thanks,
Xu Yang




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux