[PATCH v2 14/21] ARM: imx6: convert GPC to stacked domains

Stefan Agner stefan at agner.ch
Sat Jan 10 06:16:16 PST 2015


On 2015-01-10 14:34, Marc Zyngier wrote:
> On 2015-01-09 17:40, Stefan Agner wrote:
>> Hi Marc,
>>
>> On 2015-01-07 18:42, Marc Zyngier wrote:
>>> IMX6 has been (ab)using the gic_arch_extn to provide
>>> wakeup from suspend, and it makes a lot of sense to convert
>>> this code to use stacked domains instead.
>>>
>>> This patch does just this, updating the DT files to actually
>>> reflect what the HW provides.
>>>
>>> BIG FAT WARNING: because the DTs were so far lying by not
>>> exposing the fact that the GPC block is actually the first
>>> interrupt controller in the chain, kernels with this patch
>>> applied wont have any suspend-resume facility when booted
>>> with old DTs, and old kernels with updated DTs won't even boot.
>>>
>>> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
>>> ---
>>>  arch/arm/boot/dts/imx6qdl.dtsi  |   6 +-
>>>  arch/arm/boot/dts/imx6sl.dtsi   |   5 +-
>>>  arch/arm/boot/dts/imx6sx.dtsi   |   5 +-
>>>  arch/arm/mach-imx/common.h      |   1 -
>>>  arch/arm/mach-imx/gpc.c         | 127 ++++++++++++++++++++++++++++++++--------
>>>  arch/arm/mach-imx/mach-imx6q.c  |   1 -
>>>  arch/arm/mach-imx/mach-imx6sl.c |   1 -
>>>  arch/arm/mach-imx/mach-imx6sx.c |   1 -
>>>  8 files changed, 116 insertions(+), 31 deletions(-)
>>>
>>> diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
>>> index 4fc03b7..c16d428 100644
>>> --- a/arch/arm/boot/dts/imx6qdl.dtsi
>>> +++ b/arch/arm/boot/dts/imx6qdl.dtsi
>>> @@ -53,6 +53,7 @@
>>>  		interrupt-controller;
>>>  		reg = <0x00a01000 0x1000>,
>>>  		      <0x00a00100 0x100>;
>>> +		interrupt-parent = <&intc>;
>>>  	};
>>>
>>>  	clocks {
>>> @@ -82,7 +83,7 @@
>>>  		#address-cells = <1>;
>>>  		#size-cells = <1>;
>>>  		compatible = "simple-bus";
>>> -		interrupt-parent = <&intc>;
>>> +		interrupt-parent = <&gpc>;
>>>  		ranges;
>>>
>>>  		dma_apbh: dma-apbh at 00110000 {
>>> @@ -122,6 +123,7 @@
>>>  			compatible = "arm,cortex-a9-twd-timer";
>>>  			reg = <0x00a00600 0x20>;
>>>  			interrupts = <1 13 0xf01>;
>>> +			interrupt-parent = <&intc>;
>>>  			clocks = <&clks IMX6QDL_CLK_TWD>;
>>>  		};
>>>
>>> @@ -694,8 +696,10 @@
>>>  			gpc: gpc at 020dc000 {
>>>  				compatible = "fsl,imx6q-gpc";
>>>  				reg = <0x020dc000 0x4000>;
>>> +				interrupt-controller;
>>
>> #interrupt-cells = <3>; is missing here.
> 
> Ah, nice catch!
> 
>> I tested the patchset on a Colibri iMX6, but the module stopped booting
>> at some point. No error, no warn, but it looked like IRQ's are not
>> working:
>>
>> [    1.623939] platform sound: Driver imx-sgtl5000 requests probe
>> deferral
>> [    1.630677] backlight supply power not found, using dummy regulator
>> [    1.637067] pwm-backlight backlight: unable to request PWM, trying
>> legacy API
>> [    1.644271] pwm-backlight backlight: unable to request legacy PWM
>> [    1.650534] platform backlight: Driver pwm-backlight requests probe
>> deferral
>> [    1.658080] platform 2028000.ssi: Driver fsl-ssi-dai requests probe
>> deferral
>> [    1.665441] fec 2188000.ethernet eth0: Freescale FEC PHY driver
>> [Micrel KSZ8041] (mii_bus:phy_addr=2188000.ethernet:00, irq=-1)
>> [    1.677157] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
>> [freeze]
>>
>> I figured out that the GPC code did not get called. After digging
>> through the parsing code, I found the reason: irq_find_host always opted
>> to intc because this was missing... So, interrupt-cells mandatory for
>> all interrupt-controller? Maybe we could add a warn somewhere..?
> 
> interrupt-cells has a default of 1, I believe. I suppose I could add a
> WARN_ON in the xlate/alloc functions...
> 

I think the problem was in of_irq_find_parent (of drivers/of/irq.c). In
that while, interrupt-controller without interrupt-cells just get
ignored.

>> With that in place, it worked fine:
>>
>> Tested-by: Stefan Agner <stefan at agner.ch>
> 
> Thanks!
> 
>>
>>>  				interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>,
>>>  					     <0 90 IRQ_TYPE_LEVEL_HIGH>;
>>> +				interrupt-parent = <&intc>;
>>>  			};
>>>
>>>  			gpr: iomuxc-gpr at 020e0000 {
>>> diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi
>>> index 36ab8e0..35099b7 100644
>>> --- a/arch/arm/boot/dts/imx6sl.dtsi
>>> +++ b/arch/arm/boot/dts/imx6sl.dtsi
>>> @@ -72,6 +72,7 @@
>>>  		interrupt-controller;
>>>  		reg = <0x00a01000 0x1000>,
>>>  		      <0x00a00100 0x100>;
>>> +		interrupt-parent = <&intc>;
>>>  	};
>>>
>>>  	clocks {
>>> @@ -95,7 +96,7 @@
>>>  		#address-cells = <1>;
>>>  		#size-cells = <1>;
>>>  		compatible = "simple-bus";
>>> -		interrupt-parent = <&intc>;
>>> +		interrupt-parent = <&gpc>;
>>>  		ranges;
>>>
>>>  		ocram: sram at 00900000 {
>>> @@ -603,7 +604,9 @@
>>>  			gpc: gpc at 020dc000 {
>>>  				compatible = "fsl,imx6sl-gpc", "fsl,imx6q-gpc";
>>>  				reg = <0x020dc000 0x4000>;
>>> +				interrupt-controller;
>>>  				interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>;
>>> +				interrupt-parent = <&intc>;
>>>  			};
>>>
>>>  			gpr: iomuxc-gpr at 020e0000 {
>>> diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
>>> index 7a24fee..c476e67 100644
>>> --- a/arch/arm/boot/dts/imx6sx.dtsi
>>> +++ b/arch/arm/boot/dts/imx6sx.dtsi
>>> @@ -88,6 +88,7 @@
>>>  		interrupt-controller;
>>>  		reg = <0x00a01000 0x1000>,
>>>  		      <0x00a00100 0x100>;
>>> +		interrupt-parent = <&intc>;
>>>  	};
>>>
>>>  	clocks {
>>> @@ -131,7 +132,7 @@
>>>  		#address-cells = <1>;
>>>  		#size-cells = <1>;
>>>  		compatible = "simple-bus";
>>> -		interrupt-parent = <&intc>;
>>> +		interrupt-parent = <&gpc>;
>>>  		ranges;
>>>
>>>  		pmu {
>>> @@ -700,7 +701,9 @@
>>>  			gpc: gpc at 020dc000 {
>>>  				compatible = "fsl,imx6sx-gpc", "fsl,imx6q-gpc";
>>>  				reg = <0x020dc000 0x4000>;
>>> +				interrupt-controller;
>>>  				interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
>>> +				interrupt-parent = <&intc>;
>>>  			};
>>>
>>>  			iomuxc: iomuxc at 020e0000 {
>>> diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
>>> index cfcdb62..7052302 100644
>>> --- a/arch/arm/mach-imx/common.h
>>> +++ b/arch/arm/mach-imx/common.h
>>> @@ -102,7 +102,6 @@ static inline void imx_scu_map_io(void) {}
>>>  static inline void imx_smp_prepare(void) {}
>>>  #endif
>>>  void imx_src_init(void);
>>> -void imx_gpc_init(void);
>>>  void imx_gpc_pre_suspend(bool arm_power_off);
>>>  void imx_gpc_post_resume(void);
>>>  void imx_gpc_mask_all(void);
>>> diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
>>> index 5f3602e..240109b 100644
>>> --- a/arch/arm/mach-imx/gpc.c
>>> +++ b/arch/arm/mach-imx/gpc.c
>>> @@ -22,6 +22,7 @@
>>>  #define GPC_PGC_CPU_PDN		0x2a0
>>>
>>>  #define IMR_NUM			4
>>> +#define GPC_MAX_IRQS		(IMR_NUM * 32)
>>>
>>>  static void __iomem *gpc_base;
>>>  static u32 gpc_wake_irqs[IMR_NUM];
>>> @@ -56,17 +57,17 @@ void imx_gpc_post_resume(void)
>>>
>>>  static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on)
>>>  {
>>> -	unsigned int idx = d->hwirq / 32 - 1;
>>> +	unsigned int idx = d->hwirq / 32;
>>>  	u32 mask;
>>>
>>> -	/* Sanity check for SPI irq */
>>> -	if (d->hwirq < 32)
>>> -		return -EINVAL;
>>> -
>>>  	mask = 1 << d->hwirq % 32;
>>>  	gpc_wake_irqs[idx] = on ? gpc_wake_irqs[idx] | mask :
>>>  				  gpc_wake_irqs[idx] & ~mask;
>>>
>>> +	/*
>>> +	 * Do *not* call into the parent, as the GIC doesn't have any
>>> +	 * wake-up facility...
>>> +	 */
>>>  	return 0;
>>>  }
>>>
>>> @@ -96,7 +97,7 @@ void imx_gpc_hwirq_unmask(unsigned int hwirq)
>>>  	void __iomem *reg;
>>>  	u32 val;
>>>
>>> -	reg = gpc_base + GPC_IMR1 + (hwirq / 32 - 1) * 4;
>>> +	reg = gpc_base + GPC_IMR1 + hwirq / 32 * 4;
>>>  	val = readl_relaxed(reg);
>>>  	val &= ~(1 << hwirq % 32);
>>>  	writel_relaxed(val, reg);
>>> @@ -107,7 +108,7 @@ void imx_gpc_hwirq_mask(unsigned int hwirq)
>>>  	void __iomem *reg;
>>>  	u32 val;
>>>
>>> -	reg = gpc_base + GPC_IMR1 + (hwirq / 32 - 1) * 4;
>>> +	reg = gpc_base + GPC_IMR1 + hwirq / 32 * 4;
>>>  	val = readl_relaxed(reg);
>>>  	val |= 1 << (hwirq % 32);
>>>  	writel_relaxed(val, reg);
>>> @@ -115,37 +116,115 @@ void imx_gpc_hwirq_mask(unsigned int hwirq)
>>>
>>>  static void imx_gpc_irq_unmask(struct irq_data *d)
>>>  {
>>> -	/* Sanity check for SPI irq */
>>> -	if (d->hwirq < 32)
>>> -		return;
>>> -
>>>  	imx_gpc_hwirq_unmask(d->hwirq);
>>> +	irq_chip_unmask_parent(d);
>>>  }
>>>
>>>  static void imx_gpc_irq_mask(struct irq_data *d)
>>>  {
>>> -	/* Sanity check for SPI irq */
>>> -	if (d->hwirq < 32)
>>> -		return;
>>> -
>>>  	imx_gpc_hwirq_mask(d->hwirq);
>>> +	irq_chip_mask_parent(d);
>>> +}
>>
>> This two function end up very small, can't we just alter
>> imx_gpc_hwirq_unmask to use struct irq_data directly?
> 
> Unfortunately, pm-imx6.c directly uses the HW irq number.
> 

Now I see... 

--
Stefan

>> Code looks fine to me:
>>
>> Acked-by: Stefan Agner <stefan at agner.ch>
> 
> Thanks again,
> 
>         M.




More information about the linux-arm-kernel mailing list