diff mbox

[V3,16/17] irqchip/gic: Prepare for adding platform driver

Message ID 572B54F4.2080103@arm.com
State Superseded, archived
Headers show

Commit Message

Marc Zyngier May 5, 2016, 2:13 p.m. UTC
On 04/05/16 17:25, Jon Hunter wrote:
> To support GICs that require runtime-pm, it is necessary to add a
> platform driver, so that the probing of the chip can be deferred if
> resources, such as a power-domain, is not yet available.
> 
> To prepare for adding a platform driver:
>  1. Drop the __init section from the gic_dist_config(), gic_dist_init()
>     and gic_pm_init() so these can be re-used by the platform driver.
>  2. Move the definitions for gic_base and gic_chip_data structures to a
>     local header files along with prototypes for functions required by
>     the platform driver.
> 
> Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
> ---
>  drivers/irqchip/irq-gic-common.c |  4 +--
>  drivers/irqchip/irq-gic.c        | 57 +++++++++++-------------------------
>  drivers/irqchip/irq-gic.h        | 63 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 82 insertions(+), 42 deletions(-)
>  create mode 100644 drivers/irqchip/irq-gic.h
> 
> diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c
> index 9fa92a17225c..083c30390aa3 100644
> --- a/drivers/irqchip/irq-gic-common.c
> +++ b/drivers/irqchip/irq-gic-common.c
> @@ -72,8 +72,8 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
>  	return ret;
>  }
>  
> -void __init gic_dist_config(void __iomem *base, int gic_irqs,
> -			    void (*sync_access)(void))
> +void gic_dist_config(void __iomem *base, int gic_irqs,
> +		     void (*sync_access)(void))
>  {
>  	unsigned int i;
>  
> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
> index 15e8a12813cc..bf9a256a1269 100644
> --- a/drivers/irqchip/irq-gic.c
> +++ b/drivers/irqchip/irq-gic.c
> @@ -48,6 +48,7 @@
>  #include <asm/smp_plat.h>
>  #include <asm/virt.h>
>  
> +#include "irq-gic.h"
>  #include "irq-gic-common.h"
>  
>  #ifdef CONFIG_ARM64
> @@ -63,31 +64,6 @@ static void gic_check_cpu_features(void)
>  #define gic_check_cpu_features()	do { } while(0)
>  #endif
>  
> -union gic_base {
> -	void __iomem *common_base;
> -	void __percpu * __iomem *percpu_base;
> -};
> -
> -struct gic_chip_data {
> -	struct irq_chip chip;
> -	union gic_base dist_base;
> -	union gic_base cpu_base;
> -#ifdef CONFIG_CPU_PM
> -	u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
> -	u32 saved_spi_active[DIV_ROUND_UP(1020, 32)];
> -	u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
> -	u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
> -	u32 __percpu *saved_ppi_enable;
> -	u32 __percpu *saved_ppi_active;
> -	u32 __percpu *saved_ppi_conf;
> -#endif
> -	struct irq_domain *domain;
> -	unsigned int gic_irqs;
> -#ifdef CONFIG_GIC_NON_BANKED
> -	void __iomem *(*get_base)(union gic_base *);
> -#endif
> -};
> -
>  static DEFINE_RAW_SPINLOCK(irq_controller_lock);
>  
>  /*
> @@ -352,7 +328,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
>  	} while (1);
>  }
>  
> -static void gic_handle_cascade_irq(struct irq_desc *desc)
> +void gic_handle_cascade_irq(struct irq_desc *desc)
>  {
>  	struct gic_chip_data *chip_data = irq_desc_get_handler_data(desc);
>  	struct irq_chip *chip = irq_desc_get_chip(desc);
> @@ -436,7 +412,7 @@ static void gic_cpu_if_up(struct gic_chip_data *gic)
>  }
>  
>  
> -static void __init gic_dist_init(struct gic_chip_data *gic)
> +void gic_dist_init(struct gic_chip_data *gic)
>  {
>  	unsigned int i;
>  	u32 cpumask;
> @@ -459,7 +435,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
>  	writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL);
>  }
>  
> -static void gic_cpu_init(struct gic_chip_data *gic)
> +void gic_cpu_init(struct gic_chip_data *gic)
>  {
>  	void __iomem *dist_base = gic_data_dist_base(gic);
>  	void __iomem *base = gic_data_cpu_base(gic);
> @@ -518,7 +494,7 @@ int gic_cpu_if_down(unsigned int gic_nr)
>   * this function, no interrupts will be delivered by the GIC, and another
>   * platform-specific wakeup source must be enabled.
>   */
> -static void gic_dist_save(struct gic_chip_data *gic)
> +void gic_dist_save(struct gic_chip_data *gic)
>  {
>  	unsigned int gic_irqs;
>  	void __iomem *dist_base;
> @@ -557,7 +533,7 @@ static void gic_dist_save(struct gic_chip_data *gic)
>   * handled normally, but any edge interrupts that occured will not be seen by
>   * the GIC and need to be handled by the platform-specific wakeup source.
>   */
> -static void gic_dist_restore(struct gic_chip_data *gic)
> +void gic_dist_restore(struct gic_chip_data *gic)
>  {
>  	unsigned int gic_irqs;
>  	unsigned int i;
> @@ -603,7 +579,7 @@ static void gic_dist_restore(struct gic_chip_data *gic)
>  	writel_relaxed(GICD_ENABLE, dist_base + GIC_DIST_CTRL);
>  }
>  
> -static void gic_cpu_save(struct gic_chip_data *gic)
> +void gic_cpu_save(struct gic_chip_data *gic)
>  {
>  	int i;
>  	u32 *ptr;
> @@ -633,7 +609,7 @@ static void gic_cpu_save(struct gic_chip_data *gic)
>  
>  }
>  
> -static void gic_cpu_restore(struct gic_chip_data *gic)
> +void gic_cpu_restore(struct gic_chip_data *gic)
>  {
>  	int i;
>  	u32 *ptr;
> @@ -710,7 +686,7 @@ static struct notifier_block gic_notifier_block = {
>  	.notifier_call = gic_notifier,
>  };
>  
> -static void __init gic_pm_init(struct gic_chip_data *gic)
> +void gic_pm_init(struct gic_chip_data *gic)
>  {
>  	gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
>  		sizeof(u32));
> @@ -728,7 +704,7 @@ static void __init gic_pm_init(struct gic_chip_data *gic)
>  		cpu_pm_register_notifier(&gic_notifier_block);
>  }
>  #else
> -static void __init gic_pm_init(struct gic_chip_data *gic)
> +void gic_pm_init(struct gic_chip_data *gic)
>  {
>  }
>  #endif
> @@ -1002,10 +978,10 @@ static const struct irq_domain_ops gic_irq_domain_ops = {
>  	.unmap = gic_irq_domain_unmap,
>  };
>  
> -static int gic_init_bases(struct gic_chip_data *gic, int irq_start,
> -			  void __iomem *dist_base, void __iomem *cpu_base,
> -			  u32 percpu_offset, struct fwnode_handle *handle,
> -			  const char *name)
> +int gic_init_bases(struct gic_chip_data *gic, int irq_start,
> +		   void __iomem *dist_base, void __iomem *cpu_base,
> +		   u32 percpu_offset, struct fwnode_handle *handle,
> +		   const char *name)
>  {
>  	irq_hw_number_t hwirq_base;
>  	int gic_irqs, irq_base, ret;
> @@ -1153,6 +1129,7 @@ static int __init __gic_init_bases(unsigned int gic_nr, int irq_start,
>  		set_smp_cross_call(gic_raise_softirq);
>  		register_cpu_notifier(&gic_cpu_notifier);
>  #endif
> +
>  		set_handle_irq(gic_handle_irq);
>  		if (static_key_true(&supports_deactivate))
>  			pr_info("GIC: Using split EOI/Deactivate mode\n");
> @@ -1217,8 +1194,8 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
>  	return true;
>  }
>  
> -static int gic_of_setup(struct device_node *node, void __iomem **dist_base,
> -			void __iomem **cpu_base, u32 *percpu_offset)
> +int gic_of_setup(struct device_node *node, void __iomem **dist_base,
> +		 void __iomem **cpu_base, u32 *percpu_offset)
>  {
>  	if (!node || !dist_base || !cpu_base || !percpu_offset)
>  		return -EINVAL;
> diff --git a/drivers/irqchip/irq-gic.h b/drivers/irqchip/irq-gic.h
> new file mode 100644
> index 000000000000..59198d5e7175
> --- /dev/null
> +++ b/drivers/irqchip/irq-gic.h
> @@ -0,0 +1,63 @@
> +/*
> + * Copyright (C) 2016 NVIDIA CORPORATION, All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef _IRQ_GIC_H
> +#define _IRQ_GIC_H
> +
> +union gic_base {
> +	void __iomem *common_base;
> +	void __percpu * __iomem *percpu_base;
> +};
> +
> +struct gic_chip_data {
> +	struct irq_chip chip;
> +	union gic_base dist_base;
> +	union gic_base cpu_base;
> +#ifdef CONFIG_CPU_PM
> +	u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
> +	u32 saved_spi_active[DIV_ROUND_UP(1020, 32)];
> +	u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
> +	u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
> +	u32 __percpu *saved_ppi_enable;
> +	u32 __percpu *saved_ppi_active;
> +	u32 __percpu *saved_ppi_conf;
> +#endif
> +	struct irq_domain *domain;
> +	unsigned int gic_irqs;
> +#ifdef CONFIG_GIC_NON_BANKED
> +	void __iomem *(*get_base)(union gic_base *);
> +#endif
> +};

Gahhh. No. Please. Last time we did that, it took 6 months to untangle
the mess people made by adding their own hacks in this structure, 
so I definitely want to keep it completely private, forever. Same goes
for the gic_{dist,cpu.pm}_init() functions.

I've had a go at this, and came up with the following patch. I've only
briefly tested it on a host and a VM, so it is likely to break some stuff
somewhere, but you'll get the idea: The gic_chip_data struct is entirely
opaque, allocated by the GIC driver itself, with a few new fields in
it so that it becomes self-contained. This applies on top of your series.

It should also make it easy to switch to a model where we allocate
the structure dynamically instead of the old static crap.

Thoughts?

	M.

Comments

Jon Hunter May 6, 2016, 2:09 p.m. UTC | #1
Hi Marc,

On 05/05/16 15:13, Marc Zyngier wrote:

[...]

> Gahhh. No. Please. Last time we did that, it took 6 months to untangle
> the mess people made by adding their own hacks in this structure, 
> so I definitely want to keep it completely private, forever. Same goes
> for the gic_{dist,cpu.pm}_init() functions.

OK.

> I've had a go at this, and came up with the following patch. I've only
> briefly tested it on a host and a VM, so it is likely to break some stuff
> somewhere, but you'll get the idea: The gic_chip_data struct is entirely
> opaque, allocated by the GIC driver itself, with a few new fields in
> it so that it becomes self-contained. This applies on top of your series.
> 
> It should also make it easy to switch to a model where we allocate
> the structure dynamically instead of the old static crap.
> 
> Thoughts?

Yes I have been doing some testing and with a couple tweaks we can make
something like this work. One thing that caught me out was ...

> +int gic_of_setup(struct device_node *node, struct device *dev,
> +		 struct gic_chip_data **gicp)
> +{
> +	struct gic_chip_data *gic;
>  
> -	*cpu_base = of_iomap(node, 1);
> -	if (WARN(!*cpu_base, "unable to map gic cpu registers\n")) {
> -		iounmap(*dist_base);
> -		return -ENOMEM;
> +	if (!node || !gicp)
> +		return -EINVAL;
> +
> +	if (dev) {
> +		*gicp = devm_kzalloc(dev, sizeof(*gic), GFP_KERNEL);
> +		if (!*gicp)
> +			return -ENOMEM;
>  	}
>  
> -	if (of_property_read_u32(node, "cpu-offset", percpu_offset))
> -		*percpu_offset = 0;
> +	gic = *gicp;
> +
> +	gic->raw_dist_base = of_iomap(node, 0);
> +	if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n"))
> +		goto err;
> +
> +	gic->raw_cpu_base = of_iomap(node, 1);
> +	if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n"))
> +		goto err;
> +
> +	if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset))
> +		gic->percpu_offset = 0;
>  
> +	gic->chip.parent_device = dev;

We can't initialise the device here as it gets overwritten in the
gic_init_bases. So I have had to re-organise things a bit. Good news is
that I have eliminated the call from the platform driver to
gic_init_bases so we only have a single call to initialise the GIC.

Cheers
Jon
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Marc Zyngier May 6, 2016, 2:27 p.m. UTC | #2
On Fri, 6 May 2016 15:09:51 +0100
Jon Hunter <jonathanh@nvidia.com> wrote:

Hi Jon,

[...]

> > +int gic_of_setup(struct device_node *node, struct device *dev,
> > +		 struct gic_chip_data **gicp)
> > +{
> > +	struct gic_chip_data *gic;
> >  
> > -	*cpu_base = of_iomap(node, 1);
> > -	if (WARN(!*cpu_base, "unable to map gic cpu registers\n")) {
> > -		iounmap(*dist_base);
> > -		return -ENOMEM;
> > +	if (!node || !gicp)
> > +		return -EINVAL;
> > +
> > +	if (dev) {
> > +		*gicp = devm_kzalloc(dev, sizeof(*gic), GFP_KERNEL);
> > +		if (!*gicp)
> > +			return -ENOMEM;
> >  	}
> >  
> > -	if (of_property_read_u32(node, "cpu-offset", percpu_offset))
> > -		*percpu_offset = 0;
> > +	gic = *gicp;
> > +
> > +	gic->raw_dist_base = of_iomap(node, 0);
> > +	if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n"))
> > +		goto err;
> > +
> > +	gic->raw_cpu_base = of_iomap(node, 1);
> > +	if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n"))
> > +		goto err;
> > +
> > +	if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset))
> > +		gic->percpu_offset = 0;
> >  
> > +	gic->chip.parent_device = dev;
> 
> We can't initialise the device here as it gets overwritten in the
> gic_init_bases. So I have had to re-organise things a bit. Good news is
> that I have eliminated the call from the platform driver to
> gic_init_bases so we only have a single call to initialise the GIC.

Ah, good. That sounds a lot better. Looking forward to v4.

Thanks,

	M.
diff mbox

Patch

diff --git a/drivers/irqchip/irq-gic-pm.c b/drivers/irqchip/irq-gic-pm.c
index 0a86da6..c4d0621 100644
--- a/drivers/irqchip/irq-gic-pm.c
+++ b/drivers/irqchip/irq-gic-pm.c
@@ -97,9 +97,6 @@  static int gic_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	const struct gic_clk_data *data;
 	struct gic_chip_data *gic;
-	void __iomem *dist_base;
-	void __iomem *cpu_base;
-	u32 percpu_offset;
 	int ret, irq;
 
 	data = of_device_get_match_data(&pdev->dev);
@@ -108,16 +105,10 @@  static int gic_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	gic = devm_kzalloc(dev, sizeof(*gic), GFP_KERNEL);
-	if (!gic)
-		return -ENOMEM;
-
 	ret = gic_get_clocks(dev, data);
 	if (ret)
 		return ret;
 
-	platform_set_drvdata(pdev, gic);
-
 	pm_runtime_enable(dev);
 
 	ret = pm_runtime_get_sync(dev);
@@ -131,21 +122,16 @@  static int gic_probe(struct platform_device *pdev)
 		goto rpm_put;
 	}
 
-	ret = gic_of_setup(dev->of_node, &dist_base, &cpu_base, &percpu_offset);
+	ret = gic_of_setup(dev->of_node, dev, &gic);
 	if (ret)
 		goto irq_dispose;
 
-	ret = gic_init_bases(gic, -1, dist_base, cpu_base,
-			     percpu_offset, &dev->of_node->fwnode,
+	ret = gic_init_bases(gic, -1, gic, &dev->of_node->fwnode,
 			     dev->of_node->name);
 	if (ret)
 		goto gic_unmap;
 
-	gic_dist_init(gic);
-	gic_cpu_init(gic);
-	gic_pm_init(gic);
-
-	gic->chip.parent_device = dev;
+	platform_set_drvdata(pdev, gic);
 
 	irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq, gic);
 
@@ -156,8 +142,7 @@  static int gic_probe(struct platform_device *pdev)
 	return 0;
 
 gic_unmap:
-	iounmap(dist_base);
-	iounmap(cpu_base);
+	gic_of_teardown(gic);
 irq_dispose:
 	irq_dispose_mapping(irq);
 rpm_put:
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 5108a85..e779c5d 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -51,6 +51,34 @@ 
 #include "irq-gic.h"
 #include "irq-gic-common.h"
 
+union gic_base {
+	void __iomem *common_base;
+	void __percpu * __iomem *percpu_base;
+};
+
+struct gic_chip_data {
+	struct irq_chip chip;
+	union gic_base dist_base;
+	union gic_base cpu_base;
+	void __iomem *raw_dist_base;
+	void __iomem *raw_cpu_base;
+	u32 percpu_offset;
+#if defined(CONFIG_CPU_PM) || defined(ARM_GIC_PM)
+	u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
+	u32 saved_spi_active[DIV_ROUND_UP(1020, 32)];
+	u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
+	u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
+	u32 __percpu *saved_ppi_enable;
+	u32 __percpu *saved_ppi_active;
+	u32 __percpu *saved_ppi_conf;
+#endif
+	struct irq_domain *domain;
+	unsigned int gic_irqs;
+#ifdef CONFIG_GIC_NON_BANKED
+	void __iomem *(*get_base)(union gic_base *);
+#endif
+};
+
 #ifdef CONFIG_ARM64
 #include <asm/cpufeature.h>
 
@@ -420,7 +448,7 @@  static void gic_cpu_if_up(struct gic_chip_data *gic)
 }
 
 
-void gic_dist_init(struct gic_chip_data *gic)
+static void gic_dist_init(struct gic_chip_data *gic)
 {
 	unsigned int i;
 	u32 cpumask;
@@ -443,7 +471,7 @@  void gic_dist_init(struct gic_chip_data *gic)
 	writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL);
 }
 
-void gic_cpu_init(struct gic_chip_data *gic)
+static void gic_cpu_init(struct gic_chip_data *gic)
 {
 	void __iomem *dist_base = gic_data_dist_base(gic);
 	void __iomem *base = gic_data_cpu_base(gic);
@@ -693,7 +721,7 @@  static struct notifier_block gic_notifier_block = {
 	.notifier_call = gic_notifier,
 };
 
-void gic_pm_init(struct gic_chip_data *gic)
+static void gic_pm_init(struct gic_chip_data *gic)
 {
 	gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
 		sizeof(u32));
@@ -711,7 +739,7 @@  void gic_pm_init(struct gic_chip_data *gic)
 		cpu_pm_register_notifier(&gic_notifier_block);
 }
 #else
-void gic_pm_init(struct gic_chip_data *gic)
+static void gic_pm_init(struct gic_chip_data *gic)
 {
 }
 #endif
@@ -986,9 +1014,7 @@  static const struct irq_domain_ops gic_irq_domain_ops = {
 };
 
 int gic_init_bases(struct gic_chip_data *gic, int irq_start,
-		   void __iomem *dist_base, void __iomem *cpu_base,
-		   u32 percpu_offset, struct fwnode_handle *handle,
-		   const char *name)
+		   struct fwnode_handle *handle, const char *name)
 {
 	irq_hw_number_t hwirq_base;
 	int gic_irqs, irq_base, ret;
@@ -1013,7 +1039,7 @@  int gic_init_bases(struct gic_chip_data *gic, int irq_start,
 		gic->chip.irq_set_affinity = gic_set_affinity;
 #endif
 
-	if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && percpu_offset) {
+	if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) {
 		/* Frankein-GIC without banked registers... */
 		unsigned int cpu;
 
@@ -1028,19 +1054,19 @@  int gic_init_bases(struct gic_chip_data *gic, int irq_start,
 		for_each_possible_cpu(cpu) {
 			u32 mpidr = cpu_logical_map(cpu);
 			u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
-			unsigned long offset = percpu_offset * core_id;
-			*per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset;
-			*per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset;
+			unsigned long offset = gic->percpu_offset * core_id;
+			*per_cpu_ptr(gic->dist_base.percpu_base, cpu) = gic->raw_dist_base + offset;
+			*per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = gic->raw_cpu_base + offset;
 		}
 
 		gic_set_base_accessor(gic, gic_get_percpu_base);
 	} else {
 		/* Normal, sane GIC... */
-		WARN(percpu_offset,
+		WARN(gic->percpu_offset,
 		     "GIC_NON_BANKED not enabled, ignoring %08x offset!",
-		     percpu_offset);
-		gic->dist_base.common_base = dist_base;
-		gic->cpu_base.common_base = cpu_base;
+		     gic->percpu_offset);
+		gic->dist_base.common_base = gic->raw_dist_base;
+		gic->cpu_base.common_base = gic->raw_cpu_base;
 		gic_set_base_accessor(gic, gic_get_common_base);
 	}
 
@@ -1090,10 +1116,14 @@  int gic_init_bases(struct gic_chip_data *gic, int irq_start,
 		goto error;
 	}
 
+	gic_dist_init(gic);
+	gic_cpu_init(gic);
+	gic_pm_init(gic);
+
 	return 0;
 
 error:
-	if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && percpu_offset) {
+	if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) {
 		free_percpu(gic->dist_base.percpu_base);
 		free_percpu(gic->cpu_base.percpu_base);
 	}
@@ -1101,37 +1131,24 @@  error:
 	return ret;
 }
 
-static int __init __gic_init_bases(unsigned int gic_nr, int irq_start,
-			   void __iomem *dist_base, void __iomem *cpu_base,
-			   u32 percpu_offset, struct fwnode_handle *handle)
+static int __init __gic_init_bases(struct gic_chip_data *gic, int irq_start,
+				   struct fwnode_handle *handle)
 {
-	struct gic_chip_data *gic;
 	char *name;
-	int i, ret;
-
-	if (WARN_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR))
-		return -EINVAL;
-
-	gic = &gic_data[gic_nr];
+	int ret;
 
-	if (static_key_true(&supports_deactivate) && gic_nr == 0)
+	if (static_key_true(&supports_deactivate) && gic == &gic_data[0])
 		name = kasprintf(GFP_KERNEL, "GICv2");
 	else
-		name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr);
+		name = kasprintf(GFP_KERNEL, "GIC-%d", (int)(gic - &gic_data[0]));
 
-	ret = gic_init_bases(gic, irq_start, dist_base, cpu_base, percpu_offset,
-			     handle, name);
-	if (ret) {
-		kfree(name);
-		return ret;
-	}
-
-	if (gic_nr == 0) {
+	if (gic == &gic_data[0]) {
 		/*
 		 * Initialize the CPU interface map to all CPUs.
 		 * It will be refined as each CPU probes its ID.
 		 * This is only necessary for the primary GIC.
 		 */
+		int i;
 		for (i = 0; i < NR_GIC_CPU_IF; i++)
 			gic_cpu_map[i] = 0xff;
 #ifdef CONFIG_SMP
@@ -1144,22 +1161,26 @@  static int __init __gic_init_bases(unsigned int gic_nr, int irq_start,
 			pr_info("GIC: Using split EOI/Deactivate mode\n");
 	}
 
-	gic_dist_init(gic);
-	gic_cpu_init(gic);
-	gic_pm_init(gic);
+	ret = gic_init_bases(gic, irq_start, handle, name);
+	if (ret)
+		kfree(name);
 
-	return 0;
+	return ret;
 }
 
 void __init gic_init(unsigned int gic_nr, int irq_start,
 		     void __iomem *dist_base, void __iomem *cpu_base)
 {
+	struct gic_chip_data *gic = &gic_data[gic_nr];
+
 	/*
 	 * Non-DT/ACPI systems won't run a hypervisor, so let's not
 	 * bother with these...
 	 */
 	static_key_slow_dec(&supports_deactivate);
-	__gic_init_bases(gic_nr, irq_start, dist_base, cpu_base, 0, NULL);
+	gic->raw_dist_base = dist_base;
+	gic->raw_cpu_base = cpu_base;
+	__gic_init_bases(gic, irq_start, NULL);
 }
 
 #ifdef CONFIG_OF
@@ -1203,34 +1224,52 @@  static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
 	return true;
 }
 
-int gic_of_setup(struct device_node *node, void __iomem **dist_base,
-		 void __iomem **cpu_base, u32 *percpu_offset)
+void gic_of_teardown(struct gic_chip_data *gic)
 {
-	if (!node || !dist_base || !cpu_base || !percpu_offset)
-		return -EINVAL;
+	if (gic->raw_dist_base)
+		iounmap(gic->raw_dist_base);
+	if (gic->raw_cpu_base)
+		iounmap(gic->raw_cpu_base);
+}
 
-	*dist_base = of_iomap(node, 0);
-	if (WARN(!*dist_base, "unable to map gic dist registers\n"))
-		return -ENOMEM;
+int gic_of_setup(struct device_node *node, struct device *dev,
+		 struct gic_chip_data **gicp)
+{
+	struct gic_chip_data *gic;
 
-	*cpu_base = of_iomap(node, 1);
-	if (WARN(!*cpu_base, "unable to map gic cpu registers\n")) {
-		iounmap(*dist_base);
-		return -ENOMEM;
+	if (!node || !gicp)
+		return -EINVAL;
+
+	if (dev) {
+		*gicp = devm_kzalloc(dev, sizeof(*gic), GFP_KERNEL);
+		if (!*gicp)
+			return -ENOMEM;
 	}
 
-	if (of_property_read_u32(node, "cpu-offset", percpu_offset))
-		*percpu_offset = 0;
+	gic = *gicp;
+
+	gic->raw_dist_base = of_iomap(node, 0);
+	if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n"))
+		goto err;
+
+	gic->raw_cpu_base = of_iomap(node, 1);
+	if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n"))
+		goto err;
+
+	if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset))
+		gic->percpu_offset = 0;
 
+	gic->chip.parent_device = dev;
 	return 0;
+err:
+	gic_of_teardown(gic);
+	return -ENOMEM;
 }
 
 int __init
 gic_of_init(struct device_node *node, struct device_node *parent)
 {
-	void __iomem *cpu_base;
-	void __iomem *dist_base;
-	u32 percpu_offset;
+	struct gic_chip_data *gic;
 	int irq, ret;
 
 	if (WARN_ON(!node))
@@ -1245,7 +1284,8 @@  gic_of_init(struct device_node *node, struct device_node *parent)
 	    of_property_read_bool(node, "power-domains"))
 		return 0;
 
-	ret = gic_of_setup(node, &dist_base, &cpu_base, &percpu_offset);
+	gic = &gic_data[gic_cnt];
+	ret = gic_of_setup(node, NULL, &gic);
 	if (ret)
 		return ret;
 
@@ -1253,14 +1293,13 @@  gic_of_init(struct device_node *node, struct device_node *parent)
 	 * Disable split EOI/Deactivate if either HYP is not available
 	 * or the CPU interface is too small.
 	 */
-	if (gic_cnt == 0 && !gic_check_eoimode(node, &cpu_base))
+	if (gic_cnt == 0 && !gic_check_eoimode(node, &gic->raw_cpu_base))
 		static_key_slow_dec(&supports_deactivate);
 
-	ret = __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset,
-			 &node->fwnode);
+	ret = __gic_init_bases(gic, -1, &node->fwnode);
 	if (ret) {
-		iounmap(dist_base);
-		iounmap(cpu_base);
+		iounmap(gic->raw_dist_base);
+		iounmap(gic->raw_cpu_base);
 		return ret;
 	}
 
@@ -1395,7 +1434,9 @@  static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
 		return -ENOMEM;
 	}
 
-	ret = __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle);
+	gic_data[0].raw_dist_base = dist_base;
+	gic_data[0].raw_cpu_base = cpu_base;
+	ret = __gic_init_bases(&gic_data[0], -1, domain_handle);
 	if (ret) {
 		pr_err("Failed to initialise GIC\n");
 		irq_domain_free_fwnode(domain_handle);
diff --git a/drivers/irqchip/irq-gic.h b/drivers/irqchip/irq-gic.h
index 31e7733..77d4001 100644
--- a/drivers/irqchip/irq-gic.h
+++ b/drivers/irqchip/irq-gic.h
@@ -17,46 +17,18 @@ 
 #ifndef _IRQ_GIC_H
 #define _IRQ_GIC_H
 
-union gic_base {
-	void __iomem *common_base;
-	void __percpu * __iomem *percpu_base;
-};
+struct gic_chip_data;
 
-struct gic_chip_data {
-	struct irq_chip chip;
-	union gic_base dist_base;
-	union gic_base cpu_base;
-#if defined(CONFIG_CPU_PM) || defined(ARM_GIC_PM)
-	u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
-	u32 saved_spi_active[DIV_ROUND_UP(1020, 32)];
-	u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
-	u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
-	u32 __percpu *saved_ppi_enable;
-	u32 __percpu *saved_ppi_active;
-	u32 __percpu *saved_ppi_conf;
-#endif
-	struct irq_domain *domain;
-	unsigned int gic_irqs;
-#ifdef CONFIG_GIC_NON_BANKED
-	void __iomem *(*get_base)(union gic_base *);
-#endif
-};
-
-void gic_cpu_init(struct gic_chip_data *gic);
 void gic_cpu_save(struct gic_chip_data *gic);
 void gic_cpu_restore(struct gic_chip_data *gic);
-void gic_dist_init(struct gic_chip_data *gic);
 void gic_dist_save(struct gic_chip_data *gic);
 void gic_dist_restore(struct gic_chip_data *gic);
-void gic_pm_init(struct gic_chip_data *gic);
-
-int gic_of_setup(struct device_node *node, void __iomem **dist_base,
-		 void __iomem **cpu_base, u32 *percpu_offset);
 
+int gic_of_setup(struct device_node *node, struct device *dev,
+		 struct gic_chip_data **gic);
+void gic_of_teardown(struct gic_chip_data *gic);
 int gic_init_bases(struct gic_chip_data *gic, int irq_start,
-		   void __iomem *dist_base, void __iomem *cpu_base,
-		   u32 percpu_offset, struct fwnode_handle *handle,
-		   const char *name);
+		   struct fwnode_handle *handle, const char *name);
 
 void gic_handle_cascade_irq(struct irq_desc *desc);