Message ID | 1409239879-12376-3-git-send-email-thierry.reding@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thursday 28 August 2014 17:31:17 Thierry Reding wrote: > void __init tegra_init_irq(void) > { > - int i; > - void __iomem *distbase; > + unsigned int max_ictlrs = ARRAY_SIZE(ictlr_regs), i; > + const struct of_device_id *match; > + struct device_node *np; > + struct resource res; > + > + np = of_find_matching_node_and_match(NULL, ictlr_matches, &match); > + if (np) { > + const struct tegra_ictlr_soc *soc = match->data; > + > + for (i = 0; i < soc->num_ictlrs; i++) { > + if (of_address_to_resource(np, i, &res) < 0) > + break; > + > + ictlr_regs[i] = res; > + } > + > + WARN(i != soc->num_ictlrs, > + "Found %u interrupt controllers in DT; expected %u.\n", > + i, soc->num_ictlrs); > + > + max_ictlrs = soc->num_ictlrs; > + of_node_put(np); > + } else { > + /* > + * If no matching device node was found, fall back to using > + * the chip ID. > + */ > + > + /* Tegra30 and later have five interrupt controllers, ... */ > + max_ictlrs = ARRAY_SIZE(ictlr_regs); > + > + /* ..., but Tegra20 only has four. */ > + if (of_machine_is_compatible("nvidia,tegra20")) > + max_ictlrs--; > + } How about moving the entire file to drivers/irqchip and using the IRQCHIP_DECLARE() helper for the DT case? For the fallback, you can have an entry into that file that just takes the address and number, which you can call from platform code here. Arnd
On 08/28/2014 09:01 PM, Thierry Reding wrote: > From: Thierry Reding <treding@nvidia.com> > > Obtains the register ranges for the legacy interrupt controller from DT > and provide hard-coded values as fallback. > (...) > +static const struct tegra_ictlr_soc tegra30_ictlr_soc = { > + .num_ictlrs = 5, > +}; > + > +static const struct of_device_id ictlr_matches[] = { > + { .compatible = "nvidia,tegra30-ictlr", .data = &tegra30_ictlr_soc }, > + { .compatible = "nvidia,tegra20-ictlr", .data = &tegra20_ictlr_soc }, > + { } > +}; > + > +static const struct of_device_id gic_matches[] = { > + { .compatible = "arm,cortex-a15-gic", }, > + { .compatible = "arm,cortex-a9-gic", }, > + { } > +}; > + > Missed MODULE_DEVICE_TABLE(of, ...)....?
On Fri, Aug 29, 2014 at 08:57:49AM +0530, Varka Bhadram wrote: > On 08/28/2014 09:01 PM, Thierry Reding wrote: > >From: Thierry Reding <treding@nvidia.com> > > > >Obtains the register ranges for the legacy interrupt controller from DT > >and provide hard-coded values as fallback. > > > (...) > > >+static const struct tegra_ictlr_soc tegra30_ictlr_soc = { > >+ .num_ictlrs = 5, > >+}; > >+ > >+static const struct of_device_id ictlr_matches[] = { > >+ { .compatible = "nvidia,tegra30-ictlr", .data = &tegra30_ictlr_soc }, > >+ { .compatible = "nvidia,tegra20-ictlr", .data = &tegra20_ictlr_soc }, > >+ { } > >+}; > >+ > >+static const struct of_device_id gic_matches[] = { > >+ { .compatible = "arm,cortex-a15-gic", }, > >+ { .compatible = "arm,cortex-a9-gic", }, > >+ { } > >+}; > >+ > > > Missed MODULE_DEVICE_TABLE(of, ...)....? The driver isn't useful as a module, so I don't think that's necessary. I'm not even sure it could be loaded that late in the process. Without it peripheral interrupts don't work properly, so loading this from the filesystem doesn't work (unless it's in an initrd), but perhaps even that won't work. I'm not aware of anyone actually ever trying to build it as a module. Thierry
On Thu, Aug 28, 2014 at 06:10:55PM +0200, Arnd Bergmann wrote: > On Thursday 28 August 2014 17:31:17 Thierry Reding wrote: > > > void __init tegra_init_irq(void) > > { > > - int i; > > - void __iomem *distbase; > > + unsigned int max_ictlrs = ARRAY_SIZE(ictlr_regs), i; > > + const struct of_device_id *match; > > + struct device_node *np; > > + struct resource res; > > + > > + np = of_find_matching_node_and_match(NULL, ictlr_matches, &match); > > + if (np) { > > + const struct tegra_ictlr_soc *soc = match->data; > > + > > + for (i = 0; i < soc->num_ictlrs; i++) { > > + if (of_address_to_resource(np, i, &res) < 0) > > + break; > > + > > + ictlr_regs[i] = res; > > + } > > + > > + WARN(i != soc->num_ictlrs, > > + "Found %u interrupt controllers in DT; expected %u.\n", > > + i, soc->num_ictlrs); > > + > > + max_ictlrs = soc->num_ictlrs; > > + of_node_put(np); > > + } else { > > + /* > > + * If no matching device node was found, fall back to using > > + * the chip ID. > > + */ > > + > > + /* Tegra30 and later have five interrupt controllers, ... */ > > + max_ictlrs = ARRAY_SIZE(ictlr_regs); > > + > > + /* ..., but Tegra20 only has four. */ > > + if (of_machine_is_compatible("nvidia,tegra20")) > > + max_ictlrs--; > > + } > > How about moving the entire file to drivers/irqchip and using the > IRQCHIP_DECLARE() helper for the DT case? > > For the fallback, you can have an entry into that file that just takes > the address and number, which you can call from platform code here. I think I did try that at some point, but there were issues that I don't remember. I'll give it another shot. Thierry
On Fri, Aug 29, 2014 at 09:31:40AM +0200, Thierry Reding wrote: > On Thu, Aug 28, 2014 at 06:10:55PM +0200, Arnd Bergmann wrote: > > On Thursday 28 August 2014 17:31:17 Thierry Reding wrote: > > > > > void __init tegra_init_irq(void) > > > { > > > - int i; > > > - void __iomem *distbase; > > > + unsigned int max_ictlrs = ARRAY_SIZE(ictlr_regs), i; > > > + const struct of_device_id *match; > > > + struct device_node *np; > > > + struct resource res; > > > + > > > + np = of_find_matching_node_and_match(NULL, ictlr_matches, &match); > > > + if (np) { > > > + const struct tegra_ictlr_soc *soc = match->data; > > > + > > > + for (i = 0; i < soc->num_ictlrs; i++) { > > > + if (of_address_to_resource(np, i, &res) < 0) > > > + break; > > > + > > > + ictlr_regs[i] = res; > > > + } > > > + > > > + WARN(i != soc->num_ictlrs, > > > + "Found %u interrupt controllers in DT; expected %u.\n", > > > + i, soc->num_ictlrs); > > > + > > > + max_ictlrs = soc->num_ictlrs; > > > + of_node_put(np); > > > + } else { > > > + /* > > > + * If no matching device node was found, fall back to using > > > + * the chip ID. > > > + */ > > > + > > > + /* Tegra30 and later have five interrupt controllers, ... */ > > > + max_ictlrs = ARRAY_SIZE(ictlr_regs); > > > + > > > + /* ..., but Tegra20 only has four. */ > > > + if (of_machine_is_compatible("nvidia,tegra20")) > > > + max_ictlrs--; > > > + } > > > > How about moving the entire file to drivers/irqchip and using the > > IRQCHIP_DECLARE() helper for the DT case? > > > > For the fallback, you can have an entry into that file that just takes > > the address and number, which you can call from platform code here. > > I think I did try that at some point, but there were issues that I don't > remember. I'll give it another shot. So I got pretty far with this and the system still boots. But for some reason suspend/resume is now broken. The difference seems to be that earlier the legacy interrupt controller would be registered first, and the GIC second. When the legacy interrupt controller is initialized after the GIC (which happens when I use IRQCHIP_DECLARE), then suspend and resume won't work (for some yet unknown reason). Unfortunately the of_irq_init() code is too clever, so I can't even work around it by changing link order or device tree order. I'll see if I can find out what causes this combination to malfunction when initialized in the opposite order. Thierry
On Fri, Aug 29, 2014 at 04:24:10PM +0200, Thierry Reding wrote: > On Fri, Aug 29, 2014 at 09:31:40AM +0200, Thierry Reding wrote: > > On Thu, Aug 28, 2014 at 06:10:55PM +0200, Arnd Bergmann wrote: > > > On Thursday 28 August 2014 17:31:17 Thierry Reding wrote: > > > > > > > void __init tegra_init_irq(void) > > > > { > > > > - int i; > > > > - void __iomem *distbase; > > > > + unsigned int max_ictlrs = ARRAY_SIZE(ictlr_regs), i; > > > > + const struct of_device_id *match; > > > > + struct device_node *np; > > > > + struct resource res; > > > > + > > > > + np = of_find_matching_node_and_match(NULL, ictlr_matches, &match); > > > > + if (np) { > > > > + const struct tegra_ictlr_soc *soc = match->data; > > > > + > > > > + for (i = 0; i < soc->num_ictlrs; i++) { > > > > + if (of_address_to_resource(np, i, &res) < 0) > > > > + break; > > > > + > > > > + ictlr_regs[i] = res; > > > > + } > > > > + > > > > + WARN(i != soc->num_ictlrs, > > > > + "Found %u interrupt controllers in DT; expected %u.\n", > > > > + i, soc->num_ictlrs); > > > > + > > > > + max_ictlrs = soc->num_ictlrs; > > > > + of_node_put(np); > > > > + } else { > > > > + /* > > > > + * If no matching device node was found, fall back to using > > > > + * the chip ID. > > > > + */ > > > > + > > > > + /* Tegra30 and later have five interrupt controllers, ... */ > > > > + max_ictlrs = ARRAY_SIZE(ictlr_regs); > > > > + > > > > + /* ..., but Tegra20 only has four. */ > > > > + if (of_machine_is_compatible("nvidia,tegra20")) > > > > + max_ictlrs--; > > > > + } > > > > > > How about moving the entire file to drivers/irqchip and using the > > > IRQCHIP_DECLARE() helper for the DT case? > > > > > > For the fallback, you can have an entry into that file that just takes > > > the address and number, which you can call from platform code here. > > > > I think I did try that at some point, but there were issues that I don't > > remember. I'll give it another shot. > > So I got pretty far with this and the system still boots. But for some > reason suspend/resume is now broken. The difference seems to be that > earlier the legacy interrupt controller would be registered first, and > the GIC second. When the legacy interrupt controller is initialized > after the GIC (which happens when I use IRQCHIP_DECLARE), then suspend > and resume won't work (for some yet unknown reason). Unfortunately the > of_irq_init() code is too clever, so I can't even work around it by > changing link order or device tree order. > > I'll see if I can find out what causes this combination to malfunction > when initialized in the opposite order. The reason for why ordering matters is this line from the GIC driver (see drivers/irqchip/irq-gic.c): static int gic_init_bases(...) { ... gic_chip.flags |= gic_arch_extn.flags; ... } So there's an implicit dependency on having the architecture-specific extension initialized before the GIC. If I manually add in the flags set by the LIC (IRQCHIP_MASK_ON_SUSPEND) the suspend/resume issue that I observed goes away. Perhaps a better way to handle this would be to add an API that GIC IRQ extensions can use to register with the GIC independent of the initialization order? In this particular case something like: void gic_arch_register(unsigned long flags) { gic_chip.flags |= flags; } would work irrespective of the ordering. But perhaps something more elaborate would be more futureproof: void gic_arch_register(const struct irqchip *extn) { gic_chip.flags |= extn->flags; gic_arch_extn = *extn; } Or perhaps gic_arch_extn could be turned into a pointer so that checks for already registered extension drivers can be more easily done. That is: static struct irq_chip *extn; void gic_arch_register(const struct irqchip *chip) { if (WARN(extn != NULL)) return; gic_chip.flags |= chip->flags; extn = chip; } Any preferences, or other ideas? Adding Thomas and Jason, perhaps they can provide more input on how to solve this. Thierry
On Friday 29 August 2014 17:04:28 Thierry Reding wrote: > static struct irq_chip *extn; > > void gic_arch_register(const struct irqchip *chip) > { > if (WARN(extn != NULL)) > return; > > gic_chip.flags |= chip->flags; > extn = chip; > } > > Any preferences, or other ideas? Adding Thomas and Jason, perhaps they > can provide more input on how to solve this. I think the entire gic_arch_extn method is done in a rather odd way and we should try to come up with a replacement. These are the users at the moment: arch/arm/mach-exynos/pm.c: gic_arch_extn.irq_set_wake = exynos_irq_set_wake; arch/arm/mach-imx/gpc.c: gic_arch_extn.irq_mask = imx_gpc_irq_mask; arch/arm/mach-imx/gpc.c: gic_arch_extn.irq_unmask = imx_gpc_irq_unmask; arch/arm/mach-imx/gpc.c: gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake; arch/arm/mach-omap2/omap-wakeupgen.c: gic_arch_extn.irq_mask = wakeupgen_mask; arch/arm/mach-omap2/omap-wakeupgen.c: gic_arch_extn.irq_unmask = wakeupgen_unmask; arch/arm/mach-omap2/omap-wakeupgen.c: gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_W arch/arm/mach-shmobile/intc-sh73a0.c: gic_arch_extn.irq_set_wake = sh73a0_set_wake; arch/arm/mach-shmobile/setup-r8a7779.c: gic_arch_extn.irq_set_wake = r8a7779_set_wake; arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_ack = tegra_ack; arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_eoi = tegra_eoi; arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_mask = tegra_mask; arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_unmask = tegra_unmask; arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_retrigger = tegra_retrigger; arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_set_wake = tegra_set_wake; arch/arm/mach-tegra/irq.c: gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND; arch/arm/mach-ux500/cpu.c: gic_arch_extn.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; arch/arm/mach-zynq/common.c: gic_arch_extn.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; I have to admit I don't really understand how these work, but what I'd expect to work better is a way to turn the gic code into more of a library that can be used by specialized drivers. In that case you would register a driver for the tegra gic using IRQCHIP_DECLARE and that driver would call a variation of gic_of_init() or gic_init_bases() with the extra stuff as arguments. We'd have to hack around the fact that all these platforms currently don't list a specialized compatible string, but at least for the future we should be able to do this without special hacks. Arnd
Arnd, On Fri, Aug 29, 2014 at 09:53:42PM +0200, Arnd Bergmann wrote: > On Friday 29 August 2014 17:04:28 Thierry Reding wrote: > > static struct irq_chip *extn; > > > > void gic_arch_register(const struct irqchip *chip) > > { > > if (WARN(extn != NULL)) > > return; > > > > gic_chip.flags |= chip->flags; > > extn = chip; > > } > > > > Any preferences, or other ideas? Adding Thomas and Jason, perhaps they > > can provide more input on how to solve this. > > I think the entire gic_arch_extn method is done in a rather odd way > and we should try to come up with a replacement. > > These are the users at the moment: > > arch/arm/mach-exynos/pm.c: gic_arch_extn.irq_set_wake = exynos_irq_set_wake; > arch/arm/mach-imx/gpc.c: gic_arch_extn.irq_mask = imx_gpc_irq_mask; > arch/arm/mach-imx/gpc.c: gic_arch_extn.irq_unmask = imx_gpc_irq_unmask; > arch/arm/mach-imx/gpc.c: gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake; > arch/arm/mach-omap2/omap-wakeupgen.c: gic_arch_extn.irq_mask = wakeupgen_mask; > arch/arm/mach-omap2/omap-wakeupgen.c: gic_arch_extn.irq_unmask = wakeupgen_unmask; > arch/arm/mach-omap2/omap-wakeupgen.c: gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_W > arch/arm/mach-shmobile/intc-sh73a0.c: gic_arch_extn.irq_set_wake = sh73a0_set_wake; > arch/arm/mach-shmobile/setup-r8a7779.c: gic_arch_extn.irq_set_wake = r8a7779_set_wake; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_ack = tegra_ack; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_eoi = tegra_eoi; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_mask = tegra_mask; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_unmask = tegra_unmask; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_retrigger = tegra_retrigger; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_set_wake = tegra_set_wake; > arch/arm/mach-tegra/irq.c: gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND; > arch/arm/mach-ux500/cpu.c: gic_arch_extn.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; > arch/arm/mach-zynq/common.c: gic_arch_extn.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; > > I have to admit I don't really understand how these work, but what > I'd expect to work better is a way to turn the gic code into more > of a library that can be used by specialized drivers. In that > case you would register a driver for the tegra gic using IRQCHIP_DECLARE > and that driver would call a variation of gic_of_init() or gic_init_bases() > with the extra stuff as arguments. > > We'd have to hack around the fact that all these platforms currently > don't list a specialized compatible string, but at least for the future > we should be able to do this without special hacks. Thomas was talking about this just the other day: https://lkml.kernel.org/r/alpine.DEB.2.10.1408271347210.3323@nanos thx, Jason.
On Saturday 30 August 2014 11:54:59 Jason Cooper wrote: > > I have to admit I don't really understand how these work, but what > > I'd expect to work better is a way to turn the gic code into more > > of a library that can be used by specialized drivers. In that > > case you would register a driver for the tegra gic using IRQCHIP_DECLARE > > and that driver would call a variation of gic_of_init() or gic_init_bases() > > with the extra stuff as arguments. > > > > We'd have to hack around the fact that all these platforms currently > > don't list a specialized compatible string, but at least for the future > > we should be able to do this without special hacks. > > Thomas was talking about this just the other day: > > https://lkml.kernel.org/r/alpine.DEB.2.10.1408271347210.3323@nanos Ah, cool. That seems better than what I had in mind. Yes, let's do that. Arnd
On Fri, Aug 29, 2014 at 09:53:42PM +0200, Arnd Bergmann wrote: > On Friday 29 August 2014 17:04:28 Thierry Reding wrote: > > static struct irq_chip *extn; > > > > void gic_arch_register(const struct irqchip *chip) > > { > > if (WARN(extn != NULL)) > > return; > > > > gic_chip.flags |= chip->flags; > > extn = chip; > > } > > > > Any preferences, or other ideas? Adding Thomas and Jason, perhaps they > > can provide more input on how to solve this. > > I think the entire gic_arch_extn method is done in a rather odd way > and we should try to come up with a replacement. > > These are the users at the moment: > > arch/arm/mach-exynos/pm.c: gic_arch_extn.irq_set_wake = exynos_irq_set_wake; > arch/arm/mach-imx/gpc.c: gic_arch_extn.irq_mask = imx_gpc_irq_mask; > arch/arm/mach-imx/gpc.c: gic_arch_extn.irq_unmask = imx_gpc_irq_unmask; > arch/arm/mach-imx/gpc.c: gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake; > arch/arm/mach-omap2/omap-wakeupgen.c: gic_arch_extn.irq_mask = wakeupgen_mask; > arch/arm/mach-omap2/omap-wakeupgen.c: gic_arch_extn.irq_unmask = wakeupgen_unmask; > arch/arm/mach-omap2/omap-wakeupgen.c: gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_W > arch/arm/mach-shmobile/intc-sh73a0.c: gic_arch_extn.irq_set_wake = sh73a0_set_wake; > arch/arm/mach-shmobile/setup-r8a7779.c: gic_arch_extn.irq_set_wake = r8a7779_set_wake; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_ack = tegra_ack; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_eoi = tegra_eoi; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_mask = tegra_mask; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_unmask = tegra_unmask; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_retrigger = tegra_retrigger; > arch/arm/mach-tegra/irq.c: gic_arch_extn.irq_set_wake = tegra_set_wake; > arch/arm/mach-tegra/irq.c: gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND; > arch/arm/mach-ux500/cpu.c: gic_arch_extn.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; > arch/arm/mach-zynq/common.c: gic_arch_extn.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; > > I have to admit I don't really understand how these work, but what > I'd expect to work better is a way to turn the gic code into more > of a library that can be used by specialized drivers. In that > case you would register a driver for the tegra gic using IRQCHIP_DECLARE > and that driver would call a variation of gic_of_init() or gic_init_bases() > with the extra stuff as arguments. > > We'd have to hack around the fact that all these platforms currently > don't list a specialized compatible string, but at least for the future > we should be able to do this without special hacks. Besides the issue with the missing compatible values, the hardware blocks that provide the additional functionality are separate from the GIC. So they would have to look up the GIC node explicitly. I suppose that might actually be to our advantage, since these blocks will presumably have a specialized compatible string. So perhaps one solution would be to make the drivers for these separate IP blocks look up the GIC device tree node and call gic_of_init_ext() with a pointer to the IRQ chip implementation of this platform-specific glue to replace gic_arch_extn. Thierry
diff --git a/arch/arm/mach-tegra/iomap.h b/arch/arm/mach-tegra/iomap.h index ee79808e93a3..52bbb5c8fe84 100644 --- a/arch/arm/mach-tegra/iomap.h +++ b/arch/arm/mach-tegra/iomap.h @@ -28,24 +28,6 @@ #define TEGRA_ARM_PERIF_BASE 0x50040000 #define TEGRA_ARM_PERIF_SIZE SZ_8K -#define TEGRA_ARM_INT_DIST_BASE 0x50041000 -#define TEGRA_ARM_INT_DIST_SIZE SZ_4K - -#define TEGRA_PRIMARY_ICTLR_BASE 0x60004000 -#define TEGRA_PRIMARY_ICTLR_SIZE SZ_64 - -#define TEGRA_SECONDARY_ICTLR_BASE 0x60004100 -#define TEGRA_SECONDARY_ICTLR_SIZE SZ_64 - -#define TEGRA_TERTIARY_ICTLR_BASE 0x60004200 -#define TEGRA_TERTIARY_ICTLR_SIZE SZ_64 - -#define TEGRA_QUATERNARY_ICTLR_BASE 0x60004300 -#define TEGRA_QUATERNARY_ICTLR_SIZE SZ_64 - -#define TEGRA_QUINARY_ICTLR_BASE 0x60004400 -#define TEGRA_QUINARY_ICTLR_SIZE SZ_64 - #define TEGRA_TMR1_BASE 0x60005000 #define TEGRA_TMR1_SIZE SZ_8 diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c index da7be13aecce..636ebfd466cb 100644 --- a/arch/arm/mach-tegra/irq.c +++ b/arch/arm/mach-tegra/irq.c @@ -27,8 +27,7 @@ #include <linux/of.h> #include <linux/syscore_ops.h> -#include "board.h" -#include "iomap.h" +#include <soc/tegra/fuse.h> #define ICTLR_CPU_IEP_VFIQ 0x08 #define ICTLR_CPU_IEP_FIR 0x14 @@ -52,13 +51,7 @@ static int num_ictlrs; -static void __iomem *ictlr_reg_base[] = { - IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE), - IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE), - IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE), - IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE), - IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE), -}; +static void __iomem *ictlr_reg_base[] = { NULL, NULL, NULL, NULL, NULL }; #ifdef CONFIG_PM_SLEEP static u32 cop_ier[TEGRA_MAX_NUM_ICTLRS]; @@ -70,10 +63,11 @@ static u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS]; static void __iomem *tegra_gic_cpu_base; #endif +static void __iomem *distbase; + bool tegra_pending_sgi(void) { u32 pending_set; - void __iomem *distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE); pending_set = readl_relaxed(distbase + GIC_DIST_PENDING_SET); @@ -255,24 +249,109 @@ static void tegra114_gic_cpu_pm_registration(void) static void tegra114_gic_cpu_pm_registration(void) { } #endif +static struct resource ictlr_regs[] = { + { .start = 0x60004000, .end = 0x6000403f, .flags = IORESOURCE_MEM }, + { .start = 0x60004100, .end = 0x6000413f, .flags = IORESOURCE_MEM }, + { .start = 0x60004200, .end = 0x6000423f, .flags = IORESOURCE_MEM }, + { .start = 0x60004300, .end = 0x6000433f, .flags = IORESOURCE_MEM }, + { .start = 0x60004400, .end = 0x6000443f, .flags = IORESOURCE_MEM }, +}; + +struct tegra_ictlr_soc { + unsigned int num_ictlrs; +}; + +static const struct tegra_ictlr_soc tegra20_ictlr_soc = { + .num_ictlrs = 4, +}; + +static const struct tegra_ictlr_soc tegra30_ictlr_soc = { + .num_ictlrs = 5, +}; + +static const struct of_device_id ictlr_matches[] = { + { .compatible = "nvidia,tegra30-ictlr", .data = &tegra30_ictlr_soc }, + { .compatible = "nvidia,tegra20-ictlr", .data = &tegra20_ictlr_soc }, + { } +}; + +static const struct of_device_id gic_matches[] = { + { .compatible = "arm,cortex-a15-gic", }, + { .compatible = "arm,cortex-a9-gic", }, + { } +}; + void __init tegra_init_irq(void) { - int i; - void __iomem *distbase; + unsigned int max_ictlrs = ARRAY_SIZE(ictlr_regs), i; + const struct of_device_id *match; + struct device_node *np; + struct resource res; + + np = of_find_matching_node_and_match(NULL, ictlr_matches, &match); + if (np) { + const struct tegra_ictlr_soc *soc = match->data; + + for (i = 0; i < soc->num_ictlrs; i++) { + if (of_address_to_resource(np, i, &res) < 0) + break; + + ictlr_regs[i] = res; + } + + WARN(i != soc->num_ictlrs, + "Found %u interrupt controllers in DT; expected %u.\n", + i, soc->num_ictlrs); + + max_ictlrs = soc->num_ictlrs; + of_node_put(np); + } else { + /* + * If no matching device node was found, fall back to using + * the chip ID. + */ + + /* Tegra30 and later have five interrupt controllers, ... */ + max_ictlrs = ARRAY_SIZE(ictlr_regs); + + /* ..., but Tegra20 only has four. */ + if (of_machine_is_compatible("nvidia,tegra20")) + max_ictlrs--; + } - distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE); + memset(&res, 0, sizeof(res)); + + np = of_find_matching_node(NULL, gic_matches); + if (np) { + if (of_address_to_resource(np, 0, &res) < 0) + WARN(1, "GIC registers are missing from DT\n"); + + of_node_put(np); + } + + if (res.start == 0 || res.end == 0) { + res.start = 0x50041000; + res.end = 0x50041fff; + res.flags = IORESOURCE_MEM; + } + + distbase = ioremap_nocache(res.start, resource_size(&res)); num_ictlrs = readl_relaxed(distbase + GIC_DIST_CTR) & 0x1f; - if (num_ictlrs > ARRAY_SIZE(ictlr_reg_base)) { - WARN(1, "Too many (%d) interrupt controllers found. Maximum is %d.", - num_ictlrs, ARRAY_SIZE(ictlr_reg_base)); - num_ictlrs = ARRAY_SIZE(ictlr_reg_base); + if (num_ictlrs != max_ictlrs) { + WARN(1, "Found %u interrupt controllers; expected %u.\n", + num_ictlrs, max_ictlrs); + num_ictlrs = max_ictlrs; } for (i = 0; i < num_ictlrs; i++) { - void __iomem *ictlr = ictlr_reg_base[i]; + struct resource *regs = &ictlr_regs[i]; + void __iomem *ictlr; + + ictlr = ioremap_nocache(regs->start, resource_size(regs)); writel(~0, ictlr + ICTLR_CPU_IER_CLR); writel(0, ictlr + ICTLR_CPU_IEP_CLASS); + ictlr_reg_base[i] = ictlr; } gic_arch_extn.irq_ack = tegra_ack; @@ -287,9 +366,10 @@ void __init tegra_init_irq(void) * Check if there is a devicetree present, since the GIC will be * initialized elsewhere under DT. */ - if (!of_have_populated_dt()) - gic_init(0, 29, distbase, - IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100)); + if (!of_have_populated_dt()) { + void __iomem *cpubase = ioremap_nocache(0x50040000, 0x2000); + gic_init(0, 29, distbase, cpubase); + } tegra114_gic_cpu_pm_registration(); }