aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2015-02-23 12:45:18 -0500
committerShawn Guo <shawn.guo@linaro.org>2015-03-30 04:42:15 -0400
commitb923ff6af0d5a806a3996dac6d4393cd9792d0f4 (patch)
tree718baca43f2ea435c221175ea6d098cf5a9b37f8 /arch/arm/mach-imx
parentfc26d5f29b0d056699e8921bcb1a0ec709122596 (diff)
ARM: imx6: convert GPC to stacked domains
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. Tested-by: Stefan Agner <stefan@agner.ch> Acked-by: Stefan Agner <stefan@agner.ch> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Diffstat (limited to 'arch/arm/mach-imx')
-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
6 files changed, 107 insertions, 30 deletions
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) {}
101static inline void imx_smp_prepare(void) {} 101static inline void imx_smp_prepare(void) {}
102#endif 102#endif
103void imx_src_init(void); 103void imx_src_init(void);
104void imx_gpc_init(void);
105void imx_gpc_pre_suspend(bool arm_power_off); 104void imx_gpc_pre_suspend(bool arm_power_off);
106void imx_gpc_post_resume(void); 105void imx_gpc_post_resume(void);
107void imx_gpc_mask_all(void); 106void 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 @@
36#define GPC_PGC_SW_SHIFT 0x0 36#define GPC_PGC_SW_SHIFT 0x0
37 37
38#define IMR_NUM 4 38#define IMR_NUM 4
39#define GPC_MAX_IRQS (IMR_NUM * 32)
39 40
40#define GPU_VPU_PUP_REQ BIT(1) 41#define GPU_VPU_PUP_REQ BIT(1)
41#define GPU_VPU_PDN_REQ BIT(0) 42#define GPU_VPU_PDN_REQ BIT(0)
@@ -99,17 +100,17 @@ void imx_gpc_post_resume(void)
99 100
100static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on) 101static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on)
101{ 102{
102 unsigned int idx = d->hwirq / 32 - 1; 103 unsigned int idx = d->hwirq / 32;
103 u32 mask; 104 u32 mask;
104 105
105 /* Sanity check for SPI irq */
106 if (d->hwirq < 32)
107 return -EINVAL;
108
109 mask = 1 << d->hwirq % 32; 106 mask = 1 << d->hwirq % 32;
110 gpc_wake_irqs[idx] = on ? gpc_wake_irqs[idx] | mask : 107 gpc_wake_irqs[idx] = on ? gpc_wake_irqs[idx] | mask :
111 gpc_wake_irqs[idx] & ~mask; 108 gpc_wake_irqs[idx] & ~mask;
112 109
110 /*
111 * Do *not* call into the parent, as the GIC doesn't have any
112 * wake-up facility...
113 */
113 return 0; 114 return 0;
114} 115}
115 116
@@ -139,7 +140,7 @@ void imx_gpc_hwirq_unmask(unsigned int hwirq)
139 void __iomem *reg; 140 void __iomem *reg;
140 u32 val; 141 u32 val;
141 142
142 reg = gpc_base + GPC_IMR1 + (hwirq / 32 - 1) * 4; 143 reg = gpc_base + GPC_IMR1 + hwirq / 32 * 4;
143 val = readl_relaxed(reg); 144 val = readl_relaxed(reg);
144 val &= ~(1 << hwirq % 32); 145 val &= ~(1 << hwirq % 32);
145 writel_relaxed(val, reg); 146 writel_relaxed(val, reg);
@@ -150,7 +151,7 @@ void imx_gpc_hwirq_mask(unsigned int hwirq)
150 void __iomem *reg; 151 void __iomem *reg;
151 u32 val; 152 u32 val;
152 153
153 reg = gpc_base + GPC_IMR1 + (hwirq / 32 - 1) * 4; 154 reg = gpc_base + GPC_IMR1 + hwirq / 32 * 4;
154 val = readl_relaxed(reg); 155 val = readl_relaxed(reg);
155 val |= 1 << (hwirq % 32); 156 val |= 1 << (hwirq % 32);
156 writel_relaxed(val, reg); 157 writel_relaxed(val, reg);
@@ -158,41 +159,119 @@ void imx_gpc_hwirq_mask(unsigned int hwirq)
158 159
159static void imx_gpc_irq_unmask(struct irq_data *d) 160static void imx_gpc_irq_unmask(struct irq_data *d)
160{ 161{
161 /* Sanity check for SPI irq */
162 if (d->hwirq < 32)
163 return;
164
165 imx_gpc_hwirq_unmask(d->hwirq); 162 imx_gpc_hwirq_unmask(d->hwirq);
163 irq_chip_unmask_parent(d);
166} 164}
167 165
168static void imx_gpc_irq_mask(struct irq_data *d) 166static void imx_gpc_irq_mask(struct irq_data *d)
169{ 167{
170 /* Sanity check for SPI irq */
171 if (d->hwirq < 32)
172 return;
173
174 imx_gpc_hwirq_mask(d->hwirq); 168 imx_gpc_hwirq_mask(d->hwirq);
169 irq_chip_mask_parent(d);
175} 170}
176 171
177void __init imx_gpc_init(void) 172static struct irq_chip imx_gpc_chip = {
173 .name = "GPC",
174 .irq_eoi = irq_chip_eoi_parent,
175 .irq_mask = imx_gpc_irq_mask,
176 .irq_unmask = imx_gpc_irq_unmask,
177 .irq_retrigger = irq_chip_retrigger_hierarchy,
178 .irq_set_wake = imx_gpc_irq_set_wake,
179};
180
181static int imx_gpc_domain_xlate(struct irq_domain *domain,
182 struct device_node *controller,
183 const u32 *intspec,
184 unsigned int intsize,
185 unsigned long *out_hwirq,
186 unsigned int *out_type)
178{ 187{
179 struct device_node *np; 188 if (domain->of_node != controller)
189 return -EINVAL; /* Shouldn't happen, really... */
190 if (intsize != 3)
191 return -EINVAL; /* Not GIC compliant */
192 if (intspec[0] != 0)
193 return -EINVAL; /* No PPI should point to this domain */
194
195 *out_hwirq = intspec[1];
196 *out_type = intspec[2];
197 return 0;
198}
199
200static int imx_gpc_domain_alloc(struct irq_domain *domain,
201 unsigned int irq,
202 unsigned int nr_irqs, void *data)
203{
204 struct of_phandle_args *args = data;
205 struct of_phandle_args parent_args;
206 irq_hw_number_t hwirq;
180 int i; 207 int i;
181 208
182 np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc"); 209 if (args->args_count != 3)
183 gpc_base = of_iomap(np, 0); 210 return -EINVAL; /* Not GIC compliant */
184 WARN_ON(!gpc_base); 211 if (args->args[0] != 0)
212 return -EINVAL; /* No PPI should point to this domain */
213
214 hwirq = args->args[1];
215 if (hwirq >= GPC_MAX_IRQS)
216 return -EINVAL; /* Can't deal with this */
217
218 for (i = 0; i < nr_irqs; i++)
219 irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i,
220 &imx_gpc_chip, NULL);
221
222 parent_args = *args;
223 parent_args.np = domain->parent->of_node;
224 return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs, &parent_args);
225}
226
227static struct irq_domain_ops imx_gpc_domain_ops = {
228 .xlate = imx_gpc_domain_xlate,
229 .alloc = imx_gpc_domain_alloc,
230 .free = irq_domain_free_irqs_common,
231};
232
233static int __init imx_gpc_init(struct device_node *node,
234 struct device_node *parent)
235{
236 struct irq_domain *parent_domain, *domain;
237 int i;
238
239 if (!parent) {
240 pr_err("%s: no parent, giving up\n", node->full_name);
241 return -ENODEV;
242 }
243
244 parent_domain = irq_find_host(parent);
245 if (!parent_domain) {
246 pr_err("%s: unable to obtain parent domain\n", node->full_name);
247 return -ENXIO;
248 }
249
250 gpc_base = of_iomap(node, 0);
251 if (WARN_ON(!gpc_base))
252 return -ENOMEM;
253
254 domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS,
255 node, &imx_gpc_domain_ops,
256 NULL);
257 if (!domain) {
258 iounmap(gpc_base);
259 return -ENOMEM;
260 }
185 261
186 /* Initially mask all interrupts */ 262 /* Initially mask all interrupts */
187 for (i = 0; i < IMR_NUM; i++) 263 for (i = 0; i < IMR_NUM; i++)
188 writel_relaxed(~0, gpc_base + GPC_IMR1 + i * 4); 264 writel_relaxed(~0, gpc_base + GPC_IMR1 + i * 4);
189 265
190 /* Register GPC as the secondary interrupt controller behind GIC */ 266 return 0;
191 gic_arch_extn.irq_mask = imx_gpc_irq_mask;
192 gic_arch_extn.irq_unmask = imx_gpc_irq_unmask;
193 gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake;
194} 267}
195 268
269/*
270 * We cannot use the IRQCHIP_DECLARE macro that lives in
271 * drivers/irqchip, so we're forced to roll our own. Not very nice.
272 */
273OF_DECLARE_2(irqchip, imx_gpc, "fsl,imx6q-gpc", imx_gpc_init);
274
196#ifdef CONFIG_PM_GENERIC_DOMAINS 275#ifdef CONFIG_PM_GENERIC_DOMAINS
197 276
198static void _imx6q_pm_pu_power_off(struct generic_pm_domain *genpd) 277static 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)
390 imx_init_revision_from_anatop(); 390 imx_init_revision_from_anatop();
391 imx_init_l2cache(); 391 imx_init_l2cache();
392 imx_src_init(); 392 imx_src_init();
393 imx_gpc_init();
394 irqchip_init(); 393 irqchip_init();
395} 394}
396 395
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)
64 imx_init_revision_from_anatop(); 64 imx_init_revision_from_anatop();
65 imx_init_l2cache(); 65 imx_init_l2cache();
66 imx_src_init(); 66 imx_src_init();
67 imx_gpc_init();
68 irqchip_init(); 67 irqchip_init();
69} 68}
70 69
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)
84 imx_init_revision_from_anatop(); 84 imx_init_revision_from_anatop();
85 imx_init_l2cache(); 85 imx_init_l2cache();
86 imx_src_init(); 86 imx_src_init();
87 imx_gpc_init();
88 irqchip_init(); 87 irqchip_init();
89} 88}
90 89
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)
310 * Low-Power mode. 310 * Low-Power mode.
311 * 3) Software should mask IRQ #32 right after CCM Low-Power mode 311 * 3) Software should mask IRQ #32 right after CCM Low-Power mode
312 * is set (set bits 0-1 of CCM_CLPCR). 312 * is set (set bits 0-1 of CCM_CLPCR).
313 *
314 * Note that IRQ #32 is GIC SPI #0.
313 */ 315 */
314 imx_gpc_hwirq_unmask(32); 316 imx_gpc_hwirq_unmask(0);
315 writel_relaxed(val, ccm_base + CLPCR); 317 writel_relaxed(val, ccm_base + CLPCR);
316 imx_gpc_hwirq_mask(32); 318 imx_gpc_hwirq_mask(0);
317 319
318 return 0; 320 return 0;
319} 321}