Subject: [PATCH v3] irqchip: gic: Allow interrupt level to be set for PPIs.

From: [email-masked] (Marc Zyngier)
Date: Mon, 12 Jan 2015 11:19:25 +0000

Hi Liviu,

Welcome back! ;-)

On 12/01/15 11:15, Liviu Dudau wrote:
> On Mon, Jan 05, 2015 at 09:05:06AM +0000, Marc Zyngier wrote:
>> Hi Liviu,
> 
> Hi Marc,
> 
>>
>> On 01/12/14 12:45, Liviu Dudau wrote:
>>> During a recent cleanup of the arm64 DTs it has become clear that
>>> the handling of PPIs in xxxx_set_type() is incorrect. The ARM TRMs
>>> for GICv2 and later allow for "implementation defined" support for
>>> setting the edge or level type of the PPI interrupts and don't restrict
>>> the activation level of the signal. Current ARM implementations
>>> do restrict the PPI level type to IRQ_TYPE_LEVEL_LOW, but licensees
>>> of the IP can decide to shoot themselves in the foot at any time.
>>>
>>> Signed-off-by: Liviu Dudau <[email-masked]>
>>> ---
>>>
>>> Made a stupid mistake in v2 and got my bit test confused with logical test.
>>>
>>>  Documentation/devicetree/bindings/arm/gic.txt |  8 ++++++--
>>>  drivers/irqchip/irq-gic-common.c              | 21 +++++++++++++--------
>>>  drivers/irqchip/irq-gic-common.h              |  2 +-
>>>  drivers/irqchip/irq-gic-v3.c                  |  8 ++++----
>>>  drivers/irqchip/irq-gic.c                     |  9 ++++++---
>>>  drivers/irqchip/irq-hip04.c                   |  9 ++++++---
>>>  6 files changed, 36 insertions(+), 21 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/arm/gic.txt b/Documentation/devicetree/bindings/arm/gic.txt
>>> index 8112d0c..c97484b 100644
>>> --- a/Documentation/devicetree/bindings/arm/gic.txt
>>> +++ b/Documentation/devicetree/bindings/arm/gic.txt
>>> @@ -32,12 +32,16 @@ Main node required properties:
>>>    The 3rd cell is the flags, encoded as follows:
>>>  	bits[3:0] trigger type and level flags.
>>>  		1 = low-to-high edge triggered
>>> -		2 = high-to-low edge triggered
>>> +		2 = high-to-low edge triggered (invalid for SPIs)
>>>  		4 = active high level-sensitive
>>> -		8 = active low level-sensitive
>>> +		8 = active low level-sensitive (invalid for SPIs).
>>>  	bits[15:8] PPI interrupt cpu mask.  Each bit corresponds to each of
>>>  	the 8 possible cpus attached to the GIC.  A bit set to '1' indicated
>>>  	the interrupt is wired to that CPU.  Only valid for PPI interrupts.
>>> +	Also note that the configurability of PPI interrupts is IMPLEMENTATION
>>> +	DEFINED and as such not guaranteed to be present (most SoC available
>>> +	in 2014 seem to ignore the setting of this flag and use the hardware
>>> +	default value).
>>>  
>>>  - reg : Specifies base physical address(s) and size of the GIC registers. The
>>>    first region is the GIC distributor register base and size. The 2nd region is
>>> diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c
>>> index 61541ff..ffee521 100644
>>> --- a/drivers/irqchip/irq-gic-common.c
>>> +++ b/drivers/irqchip/irq-gic-common.c
>>> @@ -21,7 +21,7 @@
>>>  
>>>  #include "irq-gic-common.h"
>>>  
>>> -void gic_configure_irq(unsigned int irq, unsigned int type,
>>> +int gic_configure_irq(unsigned int irq, unsigned int type,
>>>  		       void __iomem *base, void (*sync_access)(void))
>>>  {
>>>  	u32 enablemask = 1 << (irq % 32);
>>> @@ -29,16 +29,17 @@ void gic_configure_irq(unsigned int irq, unsigned int type,
>>>  	u32 confmask = 0x2 << ((irq % 16) * 2);
>>>  	u32 confoff = (irq / 16) * 4;
>>>  	bool enabled = false;
>>> -	u32 val;
>>> +	u32 val, oldval;
>>> +	int ret = 0;
>>>  
>>>  	/*
>>>  	 * Read current configuration register, and insert the config
>>>  	 * for "irq", depending on "type".
>>>  	 */
>>> -	val = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
>>> -	if (type == IRQ_TYPE_LEVEL_HIGH)
>>> +	val = oldval = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
>>> +	if (type & IRQ_TYPE_LEVEL_MASK)
>>>  		val &= ~confmask;
>>> -	else if (type == IRQ_TYPE_EDGE_RISING)
>>> +	else if (type & IRQ_TYPE_EDGE_BOTH)
>>>  		val |= confmask;
>>>  
>>>  	/*
>>> @@ -54,15 +55,19 @@ void gic_configure_irq(unsigned int irq, unsigned int type,
>>>  
>>>  	/*
>>>  	 * Write back the new configuration, and possibly re-enable
>>> -	 * the interrupt.
>>> +	 * the interrupt. If we tried to write a new configuration and failed,
>>> +	 * return an error.
>>>  	 */
>>>  	writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
>>> -
>>> -	if (enabled)
>>> +	if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval)
>>> +		ret = -EINVAL;
>>> +	else if (enabled)
>>>  		writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
>>
>> So if you change the configuration of an enabled interrupt and fail to
>> do so, you end-up with the interrupt disabled. This is a change in
>> behaviour, and is likely to introduce regressions.
> 
> Is it? Beforehand we were silently ignoring the fact that the new values haven't been
> set, now we return an error. Should we continue to ignore the fact that there is now
> a difference between what the kernel believes the setup is and what the hardware is
> configured to do?

Returning the error is fine. It is the fact that you've now disabled a
line that was previously enabled.I don't think that sort of side effect
is acceptable.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...