aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/gpc.c
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/gpc.c
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/gpc.c')
-rw-r--r--arch/arm/mach-imx/gpc.c127
1 files changed, 103 insertions, 24 deletions
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)