summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/boot/dts/imx6qdl.dtsi7
-rw-r--r--arch/arm/boot/dts/imx6sl.dtsi6
-rw-r--r--arch/arm/boot/dts/imx6sx.dtsi6
-rw-r--r--arch/arm/mach-imx/common.h1
-rw-r--r--arch/arm/mach-imx/gpc.c127
-rw-r--r--arch/arm/mach-imx/mach-imx6q.c1
-rw-r--r--arch/arm/mach-imx/mach-imx6sl.c1
-rw-r--r--arch/arm/mach-imx/mach-imx6sx.c1
-rw-r--r--arch/arm/mach-imx/pm-imx6.c6
9 files changed, 123 insertions, 33 deletions
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 1b6f380e7eaa..da09dc456814 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@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>;
};
@@ -693,8 +695,11 @@
gpc: gpc@020dc000 {
compatible = "fsl,imx6q-gpc";
reg = <0x020dc000 0x4000>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>,
<0 90 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&intc>;
};
gpr: iomuxc-gpr@020e0000 {
diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi
index 36ab8e054cee..0d0962bf37c4 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@00900000 {
@@ -603,7 +604,10 @@
gpc: gpc@020dc000 {
compatible = "fsl,imx6sl-gpc", "fsl,imx6q-gpc";
reg = <0x020dc000 0x4000>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&intc>;
};
gpr: iomuxc-gpr@020e0000 {
diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
index 7a24fee1e7ae..dabaf89a5dd9 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,10 @@
gpc: gpc@020dc000 {
compatible = "fsl,imx6sx-gpc", "fsl,imx6q-gpc";
reg = <0x020dc000 0x4000>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&intc>;
};
iomuxc: iomuxc@020e0000 {
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index 771ecfe96c14..2fbdc283bc99 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -101,7 +101,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 029f59ce2712..6f1f77ed0c71 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -36,6 +36,7 @@
#define GPC_PGC_SW_SHIFT 0x0
#define IMR_NUM 4
+#define GPC_MAX_IRQS (IMR_NUM * 32)
#define GPU_VPU_PUP_REQ BIT(1)
#define GPU_VPU_PDN_REQ BIT(0)
@@ -99,17 +100,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;
}
@@ -139,7 +140,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);
@@ -150,7 +151,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);
@@ -158,41 +159,119 @@ 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);
}
-void __init imx_gpc_init(void)
+static struct irq_chip imx_gpc_chip = {
+ .name = "GPC",
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_mask = imx_gpc_irq_mask,
+ .irq_unmask = imx_gpc_irq_unmask,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_wake = imx_gpc_irq_set_wake,
+};
+
+static int imx_gpc_domain_xlate(struct irq_domain *domain,
+ struct device_node *controller,
+ const u32 *intspec,
+ unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
{
- struct device_node *np;
+ if (domain->of_node != controller)
+ return -EINVAL; /* Shouldn't happen, really... */
+ if (intsize != 3)
+ return -EINVAL; /* Not GIC compliant */
+ if (intspec[0] != 0)
+ return -EINVAL; /* No PPI should point to this domain */
+
+ *out_hwirq = intspec[1];
+ *out_type = intspec[2];
+ return 0;
+}
+
+static int imx_gpc_domain_alloc(struct irq_domain *domain,
+ unsigned int irq,
+ unsigned int nr_irqs, void *data)
+{
+ struct of_phandle_args *args = data;
+ struct of_phandle_args parent_args;
+ irq_hw_number_t hwirq;
int i;
- np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc");
- gpc_base = of_iomap(np, 0);
- WARN_ON(!gpc_base);
+ if (args->args_count != 3)
+ return -EINVAL; /* Not GIC compliant */
+ if (args->args[0] != 0)
+ return -EINVAL; /* No PPI should point to this domain */
+
+ hwirq = args->args[1];
+ if (hwirq >= GPC_MAX_IRQS)
+ return -EINVAL; /* Can't deal with this */
+
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i,
+ &imx_gpc_chip, NULL);
+
+ parent_args = *args;
+ parent_args.np = domain->parent->of_node;
+ return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs, &parent_args);
+}
+
+static struct irq_domain_ops imx_gpc_domain_ops = {
+ .xlate = imx_gpc_domain_xlate,
+ .alloc = imx_gpc_domain_alloc,
+ .free = irq_domain_free_irqs_common,
+};
+
+static int __init imx_gpc_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct irq_domain *parent_domain, *domain;
+ int i;
+
+ if (!parent) {
+ pr_err("%s: no parent, giving up\n", node->full_name);
+ return -ENODEV;
+ }
+
+ parent_domain = irq_find_host(parent);
+ if (!parent_domain) {
+ pr_err("%s: unable to obtain parent domain\n", node->full_name);
+ return -ENXIO;
+ }
+
+ gpc_base = of_iomap(node, 0);
+ if (WARN_ON(!gpc_base))
+ return -ENOMEM;
+
+ domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS,
+ node, &imx_gpc_domain_ops,
+ NULL);
+ if (!domain) {
+ iounmap(gpc_base);
+ return -ENOMEM;
+ }
/* Initially mask all interrupts */
for (i = 0; i < IMR_NUM; i++)
writel_relaxed(~0, gpc_base + GPC_IMR1 + i * 4);
- /* Register GPC as the secondary interrupt controller behind GIC */
- gic_arch_extn.irq_mask = imx_gpc_irq_mask;
- gic_arch_extn.irq_unmask = imx_gpc_irq_unmask;
- gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake;
+ return 0;
}
+/*
+ * We cannot use the IRQCHIP_DECLARE macro that lives in
+ * drivers/irqchip, so we're forced to roll our own. Not very nice.
+ */
+OF_DECLARE_2(irqchip, imx_gpc, "fsl,imx6q-gpc", imx_gpc_init);
+
#ifdef CONFIG_PM_GENERIC_DOMAINS
static void _imx6q_pm_pu_power_off(struct generic_pm_domain *genpd)
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index 4ad6e473cf83..6fc2b7e89c6b 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -390,7 +390,6 @@ static void __init imx6q_init_irq(void)
imx_init_revision_from_anatop();
imx_init_l2cache();
imx_src_init();
- imx_gpc_init();
irqchip_init();
}
diff --git a/arch/arm/mach-imx/mach-imx6sl.c b/arch/arm/mach-imx/mach-imx6sl.c
index 24bfaaf944c8..d39c274910c5 100644
--- a/arch/arm/mach-imx/mach-imx6sl.c
+++ b/arch/arm/mach-imx/mach-imx6sl.c
@@ -64,7 +64,6 @@ static void __init imx6sl_init_irq(void)
imx_init_revision_from_anatop();
imx_init_l2cache();
imx_src_init();
- imx_gpc_init();
irqchip_init();
}
diff --git a/arch/arm/mach-imx/mach-imx6sx.c b/arch/arm/mach-imx/mach-imx6sx.c
index 66988eb6a3a4..8595f9ea30a0 100644
--- a/arch/arm/mach-imx/mach-imx6sx.c
+++ b/arch/arm/mach-imx/mach-imx6sx.c
@@ -84,7 +84,6 @@ static void __init imx6sx_init_irq(void)
imx_init_revision_from_anatop();
imx_init_l2cache();
imx_src_init();
- imx_gpc_init();
irqchip_init();
}
diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c
index 46fd695203c7..6a7c6fc780cc 100644
--- a/arch/arm/mach-imx/pm-imx6.c
+++ b/arch/arm/mach-imx/pm-imx6.c
@@ -310,10 +310,12 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
* Low-Power mode.
* 3) Software should mask IRQ #32 right after CCM Low-Power mode
* is set (set bits 0-1 of CCM_CLPCR).
+ *
+ * Note that IRQ #32 is GIC SPI #0.
*/
- imx_gpc_hwirq_unmask(32);
+ imx_gpc_hwirq_unmask(0);
writel_relaxed(val, ccm_base + CLPCR);
- imx_gpc_hwirq_mask(32);
+ imx_gpc_hwirq_mask(0);
return 0;
}
OpenPOWER on IntegriCloud