diff mbox

[v7,1/5] usb: dwc3: omap: use request_threaded_irq()

Message ID 1462873919-20532-2-git-send-email-rogerq@ti.com
State New
Headers show

Commit Message

Roger Quadros May 10, 2016, 9:51 a.m. UTC
We intend to share this interrupt with the OTG driver an to ensure
that irqflags match for the shared interrupt handlers we use
request_threaded_irq()

If we don't use request_treaded_irq() then forced threaded irq will
set IRQF_ONESHOT and this won't match with the OTG IRQ handler's
IRQ flags.

NOTE: OTG IRQ handler is yet to be added. This is a preparatory step.

Signed-off-by: Roger Quadros <rogerq@ti.com>

---
 drivers/usb/dwc3/dwc3-omap.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

-- 
2.7.4

Comments

Roger Quadros May 10, 2016, 10:04 a.m. UTC | #1
On 10/05/16 12:58, Felipe Balbi wrote:
> 

> Hi,

> 

> Roger Quadros <rogerq@ti.com> writes:

>> We intend to share this interrupt with the OTG driver an to ensure

>> that irqflags match for the shared interrupt handlers we use

>> request_threaded_irq()

>>

>> If we don't use request_treaded_irq() then forced threaded irq will

>> set IRQF_ONESHOT and this won't match with the OTG IRQ handler's

>> IRQ flags.

>>

>> NOTE: OTG IRQ handler is yet to be added. This is a preparatory step.

>>

>> Signed-off-by: Roger Quadros <rogerq@ti.com>

>> ---

>>  drivers/usb/dwc3/dwc3-omap.c | 12 +++++++++---

>>  1 file changed, 9 insertions(+), 3 deletions(-)

>>

>> diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c

>> index 59ea35f..6504e94 100644

>> --- a/drivers/usb/dwc3/dwc3-omap.c

>> +++ b/drivers/usb/dwc3/dwc3-omap.c

>> @@ -272,16 +272,22 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)

>>  {

>>  	struct dwc3_omap	*omap = _omap;

>>  	u32			reg;

>> +	int			ret = IRQ_NONE;

>>  

>>  	reg = dwc3_omap_read_irqmisc_status(omap);

>>  

>> +	if (reg)

>> +		ret = IRQ_HANDLED;

>> +

>>  	dwc3_omap_write_irqmisc_status(omap, reg);

>>  

>>  	reg = dwc3_omap_read_irq0_status(omap);

>> +	if (reg)

>> +		ret = IRQ_HANDLED;

>>  

>>  	dwc3_omap_write_irq0_status(omap, reg);

>>  

>> -	return IRQ_HANDLED;

>> +	return ret;

>>  }

>>  

>>  static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)

>> @@ -497,8 +503,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)

>>  	/* check the DMA Status */

>>  	reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);

>>  

>> -	ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,

>> -			"dwc3-omap", omap);

>> +	ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt,

>> +					NULL, 0, "dwc3-omap", omap);

> 

> if you're using threaded_irq, it's better to have a NULL top half and

> valid bottom half.


But in this case we don't need a bottom half as there is nothing to do :).

> 

> In fact, since this will be shared, you could do a proper preparation

> and on top half check if $this device generated the IRQ and

> conditionally schedule the bottom half. Don't forget to mask device's

> interrupts from top half so you can run without IRQF_ONESHOT.

> 


Why do this at all if there is nothing to do in the bottom half?

cheers,
-roger
Roger Quadros May 10, 2016, 10:21 a.m. UTC | #2
On 10/05/16 13:12, Felipe Balbi wrote:
> 

> Hi,

> 

> Roger Quadros <rogerq@ti.com> writes:

>>>> @@ -497,8 +503,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)

>>>>  	/* check the DMA Status */

>>>>  	reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);

>>>>  

>>>> -	ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,

>>>> -			"dwc3-omap", omap);

>>>> +	ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt,

>>>> +					NULL, 0, "dwc3-omap", omap);

>>>

>>> if you're using threaded_irq, it's better to have a NULL top half and

>>> valid bottom half.

>>

>> But in this case we don't need a bottom half as there is nothing to do :).

>>

>>>

>>> In fact, since this will be shared, you could do a proper preparation

>>> and on top half check if $this device generated the IRQ and

>>> conditionally schedule the bottom half. Don't forget to mask device's

>>> interrupts from top half so you can run without IRQF_ONESHOT.

>>>

>>

>> Why do this at all if there is nothing to do in the bottom half?

> 

> oh, but there is :-)

> 

> The whole idea of threaded IRQs is that you spend as little time as

> possible on top half and the (strong) recommendation is that you *only*

> check if $this device generated the interrupt. Note that "checking if

> $this device generated the interrupt" will be mandatory as soon as you

> mark the IRQ line as shared ;-)

> 

> So here's how this should look like:

> 

> static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)

> {

>         struct dwc3_omap *omap = _omap;

>         u32 reg;

> 

>         reg = readl(IRQSTATUS)

>         if (reg) {

>                 mask_interrupts(omap);

>         	return IRQ_WAKE_THREAD;

>         }

> 

> 	return IRQ_HANDLED;

> }

> 

> static irqreturn_t dwc3_omap_threaded_interrupt(int irq, void *_omap)

> {

>         struct dwc3_omap *omap = _omap;

>         u32 reg;

> 

>         spin_lock(&omap->lock);

>         reg = readl(IRQSTATUS);

> 

>         if (reg & BIT0)

>         	handle_bit_0(omap);

> 

> 	if (reg & BIT1)

>         	handle_bit_1(omap);

> 

> 	unmask_interrupts(omap);

> 	spin_unlock(&omap->lock);

> 

> 	return IRQ_HANDLED;

> }

> 

> this will *always* behave well with RT and non-RT kernels. It also

> allows for the user to change priorities on these interrupt handlers if

> necessary.

> 


No problem, I can implement a bottom half. We are not handling anything
there at the moment so it is a bit of an overkill :)
It might help in the future if someone wants to handle something.

cheers,
-roge
Roger Quadros May 11, 2016, 11:46 a.m. UTC | #3
On 11/05/16 12:47, Felipe Balbi wrote:
> 

> Hi,

> 

> Roger Quadros <rogerq@ti.com> writes:

>>> Roger Quadros <rogerq@ti.com> writes:

>>>>>> @@ -497,8 +503,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)

>>>>>>  	/* check the DMA Status */

>>>>>>  	reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);

>>>>>>  

>>>>>> -	ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,

>>>>>> -			"dwc3-omap", omap);

>>>>>> +	ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt,

>>>>>> +					NULL, 0, "dwc3-omap", omap);

>>>>>

>>>>> if you're using threaded_irq, it's better to have a NULL top half and

>>>>> valid bottom half.

>>>>

>>>> But in this case we don't need a bottom half as there is nothing to do :).

>>>>

>>>>>

>>>>> In fact, since this will be shared, you could do a proper preparation

>>>>> and on top half check if $this device generated the IRQ and

>>>>> conditionally schedule the bottom half. Don't forget to mask device's

>>>>> interrupts from top half so you can run without IRQF_ONESHOT.

>>>>>

>>>>

>>>> Why do this at all if there is nothing to do in the bottom half?

>>>

>>> oh, but there is :-)

>>>

>>> The whole idea of threaded IRQs is that you spend as little time as

>>> possible on top half and the (strong) recommendation is that you *only*

>>> check if $this device generated the interrupt. Note that "checking if

>>> $this device generated the interrupt" will be mandatory as soon as you

>>> mark the IRQ line as shared ;-)

>>>

>>> So here's how this should look like:

>>>

>>> static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)

>>> {

>>>         struct dwc3_omap *omap = _omap;

>>>         u32 reg;

>>>

>>>         reg = readl(IRQSTATUS)

>>>         if (reg) {

>>>                 mask_interrupts(omap);

>>>         	return IRQ_WAKE_THREAD;

>>>         }

>>>

>>> 	return IRQ_HANDLED;

>>

>> This should be IRQ_NONE right?

> 

> possibly, testing will say ;-)

> 

>>> static irqreturn_t dwc3_omap_threaded_interrupt(int irq, void *_omap)

>>> {

>>>         struct dwc3_omap *omap = _omap;

>>>         u32 reg;

>>>

>>>         spin_lock(&omap->lock);

>>

>> Do we really need a spin_lock for the dwc3-omap driver?

>> Currently we won't be doing anything other than just

>> clearing the irqstatus and re-enabling the interrupts.

> 

> well, if there's no possibility of races, then no. But only testing will

> say for sure, I guess. I didn't really go through the entire thing just

> to a write a quick little template :-p

> 

OK. Another hurdle I have is that how do I mask/unmask the interrupts?
I do not see any masking bits, only enable/disable bits.

I don't think we can use the enable/disable bits to mask as we'd loose
events while the event is disabled.

cheers,
-roger
Roger Quadros May 11, 2016, 1:52 p.m. UTC | #4
On 11/05/16 15:39, Felipe Balbi wrote:
> 

> Hi,

> 

> Roger Quadros <rogerq@ti.com> writes:

>>>>> static irqreturn_t dwc3_omap_threaded_interrupt(int irq, void *_omap)

>>>>> {

>>>>>         struct dwc3_omap *omap = _omap;

>>>>>         u32 reg;

>>>>>

>>>>>         spin_lock(&omap->lock);

>>>>

>>>> Do we really need a spin_lock for the dwc3-omap driver?

>>>> Currently we won't be doing anything other than just

>>>> clearing the irqstatus and re-enabling the interrupts.

>>>

>>> well, if there's no possibility of races, then no. But only testing will

>>> say for sure, I guess. I didn't really go through the entire thing just

>>> to a write a quick little template :-p

>>>

>> OK. Another hurdle I have is that how do I mask/unmask the interrupts?

>> I do not see any masking bits, only enable/disable bits.

>>

>> I don't think we can use the enable/disable bits to mask as we'd loose

>> events while the event is disabled.

> 

> I'm pretty sure your TRM discusses the usage of IRQSTATUS_RAW*

> registers, doesn't it ? :-)


It does and also says that it is mostly for debug. But I don't mind using it
if it solves our problem :).

> 

> Those registers should be modified by HW even when interrupts are

> disabled/masked. Note that, the IRQSTATUS_SET* and IRQSTATUS_CLR*

> registers act more like mask/unmask than enable/disable considering we

> can still read IRQ status using *RAW* registers.

> 

> See if below works fine for OMAP5, AM4 and AM5 SoCs:

> 

> diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c

> index af264493bbae..ece2f25ad2c3 100644

> --- a/drivers/usb/dwc3/dwc3-omap.c

> +++ b/drivers/usb/dwc3/dwc3-omap.c

> @@ -165,20 +165,20 @@ static void dwc3_omap_write_utmi_ctrl(struct dwc3_omap *omap, u32 value)

>  

>  static u32 dwc3_omap_read_irq0_status(struct dwc3_omap *omap)

>  {

> -	return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0 -

> +	return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_RAW_0 -

>  						omap->irq0_offset);

>  }

>  

>  static void dwc3_omap_write_irq0_status(struct dwc3_omap *omap, u32 value)

>  {

> -	dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0 -

> +	dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_RAW_0 -

>  						omap->irq0_offset, value);


We shouldn't write to RAW here as it will trigger a software IRQ instead of
clearing the event.
>  

>  }

>  

>  static u32 dwc3_omap_read_irqmisc_status(struct dwc3_omap *omap)

>  {

> -	return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_MISC +

> +	return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_RAW_MISC +

>  						omap->irqmisc_offset);

>  }

>  

> 


The rest is fine. It seemed to work on omap5. I'll do more tests though.
Thanks for the hint :)

cheers,
-roger
diff mbox

Patch

diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 59ea35f..6504e94 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -272,16 +272,22 @@  static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
 {
 	struct dwc3_omap	*omap = _omap;
 	u32			reg;
+	int			ret = IRQ_NONE;
 
 	reg = dwc3_omap_read_irqmisc_status(omap);
 
+	if (reg)
+		ret = IRQ_HANDLED;
+
 	dwc3_omap_write_irqmisc_status(omap, reg);
 
 	reg = dwc3_omap_read_irq0_status(omap);
+	if (reg)
+		ret = IRQ_HANDLED;
 
 	dwc3_omap_write_irq0_status(omap, reg);
 
-	return IRQ_HANDLED;
+	return ret;
 }
 
 static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
@@ -497,8 +503,8 @@  static int dwc3_omap_probe(struct platform_device *pdev)
 	/* check the DMA Status */
 	reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
 
-	ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
-			"dwc3-omap", omap);
+	ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt,
+					NULL, 0, "dwc3-omap", omap);
 	if (ret) {
 		dev_err(dev, "failed to request IRQ #%d --> %d\n",
 				omap->irq, ret);