diff mbox

[v8,1/4] irqchip: gic: Support hierarchy irq domain.

Message ID 1416902662-19281-2-git-send-email-yingjoe.chen@mediatek.com (mailing list archive)
State New, archived
Headers show

Commit Message

Yingjoe Chen Nov. 25, 2014, 8:04 a.m. UTC
Add support to use gic as a parent for stacked irq domain.

Signed-off-by: Yingjoe Chen <yingjoe.chen@mediatek.com>
---
 drivers/irqchip/Kconfig   |  1 +
 drivers/irqchip/irq-gic.c | 77 ++++++++++++++++++++++++++++++++---------------
 2 files changed, 54 insertions(+), 24 deletions(-)

Comments

Marc Zyngier Nov. 25, 2014, 11:27 a.m. UTC | #1
On 25/11/14 08:04, Yingjoe Chen wrote:
> Add support to use gic as a parent for stacked irq domain.
> 
> Signed-off-by: Yingjoe Chen <yingjoe.chen@mediatek.com>

Looks good to me.

Acked-by: Marc Zyngier <marc.zyngier@arm.com>

	M.
Geert Uytterhoeven Dec. 2, 2014, 1:27 p.m. UTC | #2
On Tue, Nov 25, 2014 at 9:04 AM, Yingjoe Chen <yingjoe.chen@mediatek.com> wrote:
> Add support to use gic as a parent for stacked irq domain.
>
> Signed-off-by: Yingjoe Chen <yingjoe.chen@mediatek.com>

This change (which is now in -next as commit 9a1091ef0017c40a) breaks
booting on r8a7740/armadillo-legacy.

It hangs because the timers cannot get their interrupts:

console [tty0] enabled
 sh-tmu.0: ch0: used for clock events
 sh-tmu.0: ch0: used for periodic clock events
 sh-tmu.0: ch0: failed to request irq 230
 sh-tmu.0: ch1: used as clock source
 sh-cmt-48.1: ch0: failed to request irq 90
 sh-cmt-48.1: ch0: registration failed
earlytimer: unable to probe sh-cmt-48 early.
Calibrating delay loop...

> ---
>  drivers/irqchip/Kconfig   |  1 +
>  drivers/irqchip/irq-gic.c | 77 ++++++++++++++++++++++++++++++++---------------
>  2 files changed, 54 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index b21f12f..7f34138 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -5,6 +5,7 @@ config IRQCHIP
>  config ARM_GIC
>         bool
>         select IRQ_DOMAIN
> +       select IRQ_DOMAIN_HIERARCHY
>         select MULTI_IRQ_HANDLER
>
>  config GIC_NON_BANKED
> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
> index 38493ff..ab6069b 100644
> --- a/drivers/irqchip/irq-gic.c
> +++ b/drivers/irqchip/irq-gic.c
> @@ -788,17 +788,16 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
>  {
>         if (hw < 32) {
>                 irq_set_percpu_devid(irq);
> -               irq_set_chip_and_handler(irq, &gic_chip,
> -                                        handle_percpu_devid_irq);
> +               irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
> +                                   handle_percpu_devid_irq, NULL, NULL);
>                 set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
>         } else {
> -               irq_set_chip_and_handler(irq, &gic_chip,
> -                                        handle_fasteoi_irq);
> +               irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
> +                                   handle_fasteoi_irq, NULL, NULL);
>                 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
>
>                 gic_routable_irq_domain_ops->map(d, irq, hw);
>         }
> -       irq_set_chip_data(irq, d->host_data);
>         return 0;
>  }
>
> @@ -858,6 +857,31 @@ static struct notifier_block gic_cpu_notifier = {
>  };
>  #endif
>
> +static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +                               unsigned int nr_irqs, void *arg)
> +{
> +       int i, ret;
> +       irq_hw_number_t hwirq;
> +       unsigned int type = IRQ_TYPE_NONE;
> +       struct of_phandle_args *irq_data = arg;
> +
> +       ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args,
> +                                  irq_data->args_count, &hwirq, &type);
> +       if (ret)
> +               return ret;
> +
> +       for (i = 0; i < nr_irqs; i++)
> +               gic_irq_domain_map(domain, virq + i, hwirq + i);
> +
> +       return 0;
> +}
> +
> +static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
> +       .xlate = gic_irq_domain_xlate,
> +       .alloc = gic_irq_domain_alloc,
> +       .free = irq_domain_free_irqs_top,
> +};
> +
>  static const struct irq_domain_ops gic_irq_domain_ops = {
>         .map = gic_irq_domain_map,
>         .unmap = gic_irq_domain_unmap,
> @@ -948,18 +972,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
>                 gic_cpu_map[i] = 0xff;
>
>         /*
> -        * For primary GICs, skip over SGIs.
> -        * For secondary GICs, skip over PPIs, too.
> -        */
> -       if (gic_nr == 0 && (irq_start & 31) > 0) {
> -               hwirq_base = 16;
> -               if (irq_start != -1)
> -                       irq_start = (irq_start & ~31) + 16;
> -       } else {
> -               hwirq_base = 32;
> -       }
> -
> -       /*
>          * Find out how many interrupts are supported.
>          * The GIC only supports up to 1020 interrupt sources.
>          */
> @@ -969,10 +981,31 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
>                 gic_irqs = 1020;
>         gic->gic_irqs = gic_irqs;
>
> -       gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
> +       if (node) {             /* DT case */
> +               const struct irq_domain_ops *ops = &gic_irq_domain_hierarchy_ops;
> +
> +               if (!of_property_read_u32(node, "arm,routable-irqs",
> +                                         &nr_routable_irqs)) {
> +                       ops = &gic_irq_domain_ops;
> +                       gic_irqs = nr_routable_irqs;
> +               }
> +
> +               gic->domain = irq_domain_add_linear(node, gic_irqs, ops, gic);
> +       } else {                /* Non-DT case */
> +               /*
> +                * For primary GICs, skip over SGIs.
> +                * For secondary GICs, skip over PPIs, too.
> +                */
> +               if (gic_nr == 0 && (irq_start & 31) > 0) {
> +                       hwirq_base = 16;
> +                       if (irq_start != -1)
> +                               irq_start = (irq_start & ~31) + 16;
> +               } else {
> +                       hwirq_base = 32;
> +               }
> +
> +               gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
>
> -       if (of_property_read_u32(node, "arm,routable-irqs",
> -                                &nr_routable_irqs)) {
>                 irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
>                                            numa_node_id());
>                 if (IS_ERR_VALUE(irq_base)) {
> @@ -983,10 +1016,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
>
>                 gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
>                                         hwirq_base, &gic_irq_domain_ops, gic);
> -       } else {
> -               gic->domain = irq_domain_add_linear(node, nr_routable_irqs,
> -                                                   &gic_irq_domain_ops,
> -                                                   gic);
>         }
>
>         if (WARN_ON(!gic->domain))
> --
> 1.8.1.1.dirty

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Marc Zyngier Dec. 2, 2014, 1:37 p.m. UTC | #3
On 02/12/14 13:27, Geert Uytterhoeven wrote:
> On Tue, Nov 25, 2014 at 9:04 AM, Yingjoe Chen <yingjoe.chen@mediatek.com> wrote:
>> Add support to use gic as a parent for stacked irq domain.
>>
>> Signed-off-by: Yingjoe Chen <yingjoe.chen@mediatek.com>
> 
> This change (which is now in -next as commit 9a1091ef0017c40a) breaks
> booting on r8a7740/armadillo-legacy.
> 
> It hangs because the timers cannot get their interrupts:
> 
> console [tty0] enabled
>  sh-tmu.0: ch0: used for clock events
>  sh-tmu.0: ch0: used for periodic clock events
>  sh-tmu.0: ch0: failed to request irq 230
>  sh-tmu.0: ch1: used as clock source
>  sh-cmt-48.1: ch0: failed to request irq 90
>  sh-cmt-48.1: ch0: registration failed
> earlytimer: unable to probe sh-cmt-48 early.
> Calibrating delay loop...

Grmbl... Are you, by any (lack of) chance, using a setup where the GIC
is probed via DT, but the timers have their IRQs hardcoded?

	M.
Geert Uytterhoeven Dec. 2, 2014, 1:55 p.m. UTC | #4
Hi Marc,

On Tue, Dec 2, 2014 at 2:37 PM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 02/12/14 13:27, Geert Uytterhoeven wrote:
>> On Tue, Nov 25, 2014 at 9:04 AM, Yingjoe Chen <yingjoe.chen@mediatek.com> wrote:
>>> Add support to use gic as a parent for stacked irq domain.
>>>
>>> Signed-off-by: Yingjoe Chen <yingjoe.chen@mediatek.com>
>>
>> This change (which is now in -next as commit 9a1091ef0017c40a) breaks
>> booting on r8a7740/armadillo-legacy.
>>
>> It hangs because the timers cannot get their interrupts:
>>
>> console [tty0] enabled
>>  sh-tmu.0: ch0: used for clock events
>>  sh-tmu.0: ch0: used for periodic clock events
>>  sh-tmu.0: ch0: failed to request irq 230
>>  sh-tmu.0: ch1: used as clock source
>>  sh-cmt-48.1: ch0: failed to request irq 90
>>  sh-cmt-48.1: ch0: registration failed
>> earlytimer: unable to probe sh-cmt-48 early.
>> Calibrating delay loop...
>
> Grmbl... Are you, by any (lack of) chance, using a setup where the GIC
> is probed via DT, but the timers have their IRQs hardcoded?

Yes, the GIC is declared in arch/arm/boot/dts/r8a7740.dtsi:

        gic: interrupt-controller@c2800000 {
                compatible = "arm,cortex-a9-gic";
                #interrupt-cells = <3>;
                interrupt-controller;
                reg = <0xc2800000 0x1000>,
                      <0xc2000000 0x1000>;
        };

The timers come from legacy C board code in
arch/arm/mach-shmobile/setup-r8a7740.c:

/* CMT */
static struct sh_timer_config cmt1_platform_data = {
        .channels_mask = 0x3f,
};

static struct resource cmt1_resources[] = {
        DEFINE_RES_MEM(0xe6138000, 0x170),
        DEFINE_RES_IRQ(gic_spi(58)),
};

static struct platform_device cmt1_device = {
        .name           = "sh-cmt-48",
        .id             = 1,
        .dev = {
                .platform_data  = &cmt1_platform_data,
        },
        .resource       = cmt1_resources,
        .num_resources  = ARRAY_SIZE(cmt1_resources),
};

/* TMU */
static struct sh_timer_config tmu0_platform_data = {
        .channels_mask = 7,
};

static struct resource tmu0_resources[] = {
        DEFINE_RES_MEM(0xfff80000, 0x2c),
        DEFINE_RES_IRQ(gic_spi(198)),
        DEFINE_RES_IRQ(gic_spi(199)),
        DEFINE_RES_IRQ(gic_spi(200)),
};

static struct platform_device tmu0_device = {
        .name           = "sh-tmu",
        .id             = 0,
        .dev = {
                .platform_data  = &tmu0_platform_data,
        },
        .resource       = tmu0_resources,
        .num_resources  = ARRAY_SIZE(tmu0_resources),
};

The corresponding r8a7740/armadillo-multiplatform works fine.

Still, I'm wondering why sh73a0/kzm9g-legacy is not impacted by this
change...

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Marc Zyngier Dec. 2, 2014, 2:12 p.m. UTC | #5
On 02/12/14 13:55, Geert Uytterhoeven wrote:
> Hi Marc,
> 
> On Tue, Dec 2, 2014 at 2:37 PM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> On 02/12/14 13:27, Geert Uytterhoeven wrote:
>>> On Tue, Nov 25, 2014 at 9:04 AM, Yingjoe Chen <yingjoe.chen@mediatek.com> wrote:
>>>> Add support to use gic as a parent for stacked irq domain.
>>>>
>>>> Signed-off-by: Yingjoe Chen <yingjoe.chen@mediatek.com>
>>>
>>> This change (which is now in -next as commit 9a1091ef0017c40a) breaks
>>> booting on r8a7740/armadillo-legacy.
>>>
>>> It hangs because the timers cannot get their interrupts:
>>>
>>> console [tty0] enabled
>>>  sh-tmu.0: ch0: used for clock events
>>>  sh-tmu.0: ch0: used for periodic clock events
>>>  sh-tmu.0: ch0: failed to request irq 230
>>>  sh-tmu.0: ch1: used as clock source
>>>  sh-cmt-48.1: ch0: failed to request irq 90
>>>  sh-cmt-48.1: ch0: registration failed
>>> earlytimer: unable to probe sh-cmt-48 early.
>>> Calibrating delay loop...
>>
>> Grmbl... Are you, by any (lack of) chance, using a setup where the GIC
>> is probed via DT, but the timers have their IRQs hardcoded?
> 
> Yes, the GIC is declared in arch/arm/boot/dts/r8a7740.dtsi:
> 
>         gic: interrupt-controller@c2800000 {
>                 compatible = "arm,cortex-a9-gic";
>                 #interrupt-cells = <3>;
>                 interrupt-controller;
>                 reg = <0xc2800000 0x1000>,
>                       <0xc2000000 0x1000>;
>         };
> 
> The timers come from legacy C board code in
> arch/arm/mach-shmobile/setup-r8a7740.c:
> 
> /* CMT */
> static struct sh_timer_config cmt1_platform_data = {
>         .channels_mask = 0x3f,
> };
> 
> static struct resource cmt1_resources[] = {
>         DEFINE_RES_MEM(0xe6138000, 0x170),
>         DEFINE_RES_IRQ(gic_spi(58)),
> };

Yup. This gic_spi() macro is completely doomed, as it computes a
hardware interrupt where the kernel expects a virtual one. The only way
to fix this is to init the GIC outside of DT, so that we stick to a
legacy irqdomain. You can still use information from DT, but you can't
rely on irqchip_init() to produce something that a legacy platform can use.

> The corresponding r8a7740/armadillo-multiplatform works fine.

OK, that's indeed the expected result.

> Still, I'm wondering why sh73a0/kzm9g-legacy is not impacted by this
> change...

I'm a bit lost looking at the shmobile code. But I agree, kzm9g should
die a painful death, as it has the same level of breakage as armadillo.

	M.
diff mbox

Patch

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index b21f12f..7f34138 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -5,6 +5,7 @@  config IRQCHIP
 config ARM_GIC
 	bool
 	select IRQ_DOMAIN
+	select IRQ_DOMAIN_HIERARCHY
 	select MULTI_IRQ_HANDLER
 
 config GIC_NON_BANKED
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 38493ff..ab6069b 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -788,17 +788,16 @@  static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
 {
 	if (hw < 32) {
 		irq_set_percpu_devid(irq);
-		irq_set_chip_and_handler(irq, &gic_chip,
-					 handle_percpu_devid_irq);
+		irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
+				    handle_percpu_devid_irq, NULL, NULL);
 		set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
 	} else {
-		irq_set_chip_and_handler(irq, &gic_chip,
-					 handle_fasteoi_irq);
+		irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
+				    handle_fasteoi_irq, NULL, NULL);
 		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
 
 		gic_routable_irq_domain_ops->map(d, irq, hw);
 	}
-	irq_set_chip_data(irq, d->host_data);
 	return 0;
 }
 
@@ -858,6 +857,31 @@  static struct notifier_block gic_cpu_notifier = {
 };
 #endif
 
+static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				unsigned int nr_irqs, void *arg)
+{
+	int i, ret;
+	irq_hw_number_t hwirq;
+	unsigned int type = IRQ_TYPE_NONE;
+	struct of_phandle_args *irq_data = arg;
+
+	ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args,
+				   irq_data->args_count, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < nr_irqs; i++)
+		gic_irq_domain_map(domain, virq + i, hwirq + i);
+
+	return 0;
+}
+
+static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
+	.xlate = gic_irq_domain_xlate,
+	.alloc = gic_irq_domain_alloc,
+	.free = irq_domain_free_irqs_top,
+};
+
 static const struct irq_domain_ops gic_irq_domain_ops = {
 	.map = gic_irq_domain_map,
 	.unmap = gic_irq_domain_unmap,
@@ -948,18 +972,6 @@  void __init gic_init_bases(unsigned int gic_nr, int irq_start,
 		gic_cpu_map[i] = 0xff;
 
 	/*
-	 * For primary GICs, skip over SGIs.
-	 * For secondary GICs, skip over PPIs, too.
-	 */
-	if (gic_nr == 0 && (irq_start & 31) > 0) {
-		hwirq_base = 16;
-		if (irq_start != -1)
-			irq_start = (irq_start & ~31) + 16;
-	} else {
-		hwirq_base = 32;
-	}
-
-	/*
 	 * Find out how many interrupts are supported.
 	 * The GIC only supports up to 1020 interrupt sources.
 	 */
@@ -969,10 +981,31 @@  void __init gic_init_bases(unsigned int gic_nr, int irq_start,
 		gic_irqs = 1020;
 	gic->gic_irqs = gic_irqs;
 
-	gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
+	if (node) {		/* DT case */
+		const struct irq_domain_ops *ops = &gic_irq_domain_hierarchy_ops;
+
+		if (!of_property_read_u32(node, "arm,routable-irqs",
+					  &nr_routable_irqs)) {
+			ops = &gic_irq_domain_ops;
+			gic_irqs = nr_routable_irqs;
+		}
+
+		gic->domain = irq_domain_add_linear(node, gic_irqs, ops, gic);
+	} else {		/* Non-DT case */
+		/*
+		 * For primary GICs, skip over SGIs.
+		 * For secondary GICs, skip over PPIs, too.
+		 */
+		if (gic_nr == 0 && (irq_start & 31) > 0) {
+			hwirq_base = 16;
+			if (irq_start != -1)
+				irq_start = (irq_start & ~31) + 16;
+		} else {
+			hwirq_base = 32;
+		}
+
+		gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
 
-	if (of_property_read_u32(node, "arm,routable-irqs",
-				 &nr_routable_irqs)) {
 		irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
 					   numa_node_id());
 		if (IS_ERR_VALUE(irq_base)) {
@@ -983,10 +1016,6 @@  void __init gic_init_bases(unsigned int gic_nr, int irq_start,
 
 		gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
 					hwirq_base, &gic_irq_domain_ops, gic);
-	} else {
-		gic->domain = irq_domain_add_linear(node, nr_routable_irqs,
-						    &gic_irq_domain_ops,
-						    gic);
 	}
 
 	if (WARN_ON(!gic->domain))